Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[core] Line-break ideographic text by character
Browse files Browse the repository at this point in the history
Allow a line break to be inserted after any supported Chinese, Japanese, or Yi character in a point-placed label. Balance the lines unless non-ideographic text such as Latin letters are present.

Fixes #1223.
  • Loading branch information
1ec5 committed Oct 26, 2016
1 parent a4d259c commit 150dfc6
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 9 deletions.
2 changes: 2 additions & 0 deletions cmake/core-files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,8 @@ set(MBGL_CORE_FILES
src/mbgl/util/http_header.hpp
src/mbgl/util/http_timeout.cpp
src/mbgl/util/http_timeout.hpp
src/mbgl/util/i18n.cpp
src/mbgl/util/i18n.hpp
src/mbgl/util/interpolate.hpp
src/mbgl/util/intersection_tests.cpp
src/mbgl/util/intersection_tests.hpp
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"lodash": "^4.16.4",
"mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#98a56d538b11fb331aa67a6d632d6ecd6821b007",
"mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#7f62a4fc9f21e619824d68abbc4b03cbc1685572",
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#844ecd23c3314eec509dbcdaf0f956d5504e6fef",
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#4a508b6da9bad0fe267b0ccd4440fa061da3b4f8",
"mkdirp": "^0.5.1",
"node-cmake": "^1.2.1",
"request": "^2.72.0",
Expand Down
1 change: 1 addition & 0 deletions platform/ios/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* Added [quadkey](https://msdn.microsoft.com/en-us/library/bb259689.aspx) support and limited WMS support in raster tile URL templates. ([#5628](https://github.com/mapbox/mapbox-gl-native/pull/5628))
* TileJSON manifests can now specify `"scheme": "tms"` to indicate the use of [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) coordinates. ([#2270](https://github.com/mapbox/mapbox-gl-native/pull/2270))
* Fixed rendering artifacts and missing glyphs that occurred after viewing a large number of CJK characters on the map. ([#5908](https://github.com/mapbox/mapbox-gl-native/pull/5908))
* Improved the line wrapping behavior of point-placed labels written in Chinese, Japanese, and Yi. ([#6828](https://github.com/mapbox/mapbox-gl-native/pull/6828))
* `-[MGLMapView resetPosition]` now resets to the current style’s default center coordinates, zoom level, direction, and pitch, if specified. ([#6127](https://github.com/mapbox/mapbox-gl-native/pull/6127))
* The `text-pitch-alignment` property is now supported in stylesheets for improved street label legibility on a tilted map. ([#5288](https://github.com/mapbox/mapbox-gl-native/pull/5288))
* The `icon-text-fit` and `icon-text-fit-padding` properties are now supported in stylesheets, allowing the background of a shield to automatically resize to fit the shield’s text. ([#5334](https://github.com/mapbox/mapbox-gl-native/pull/5334))
Expand Down
1 change: 1 addition & 0 deletions platform/macos/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* GeoJSON sources specified by the stylesheet at design time now support `cluster`, `clusterMaxZoom`, and `clusterRadius` attributes for clustering point features on the base map. ([#5724](https://github.com/mapbox/mapbox-gl-native/pull/5724))
* TileJSON manifests can now specify `"scheme": "tms"` to indicate the use of [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) coordinates. ([#2270](https://github.com/mapbox/mapbox-gl-native/pull/2270))
* Fixed rendering artifacts and missing glyphs that occurred after viewing a large number of CJK characters on the map. ([#5908](https://github.com/mapbox/mapbox-gl-native/pull/5908))
* Improved the line wrapping behavior of point-placed labels written in Chinese, Japanese, and Yi. ([#6828](https://github.com/mapbox/mapbox-gl-native/pull/6828))
* Fixed an issue where the style zoom levels were not respected when deciding when to render a layer. ([#5811](https://github.com/mapbox/mapbox-gl-native/issues/5811))
* If MGLMapView is unable to obtain or parse a style, it now calls its delegate’s `-mapViewDidFailLoadingMap:withError:` method. ([#6145](https://github.com/mapbox/mapbox-gl-native/pull/6145))
* Added the `-[MGLMapViewDelegate mapView:didFinishLoadingStyle:]` delegate method, which offers the earliest opportunity to modify the layout or appearance of the current style before the map view is displayed to the user. ([#6636](https://github.com/mapbox/mapbox-gl-native/pull/6636))
Expand Down
25 changes: 18 additions & 7 deletions src/mbgl/text/glyph_set.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <mbgl/text/glyph_set.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/math/minmax.hpp>
#include <mbgl/util/i18n.hpp>

#include <cassert>

Expand Down Expand Up @@ -54,7 +55,8 @@ const Shaping GlyphSet::getShaping(const std::u32string &string, const float max
if (shaping.positionedGlyphs.empty())
return shaping;

lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify, translate);
lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify, translate,
util::i18n::allowsIdeographicBreaking(string));

return shaping;
}
Expand Down Expand Up @@ -85,9 +87,10 @@ void justifyLine(std::vector<PositionedGlyph> &positionedGlyphs, const std::map<
}
}

void GlyphSet::lineWrap(Shaping &shaping, const float lineHeight, const float maxWidth,
const float horizontalAlign, const float verticalAlign,
const float justify, const Point<float> &translate) const {
void GlyphSet::lineWrap(Shaping &shaping, const float lineHeight, float maxWidth,
const float horizontalAlign, const float verticalAlign,
const float justify, const Point<float> &translate,
bool useBalancedIdeographicBreaking) const {
uint32_t lastSafeBreak = 0;

uint32_t lengthBeforeCurrentLine = 0;
Expand All @@ -99,6 +102,12 @@ void GlyphSet::lineWrap(Shaping &shaping, const float lineHeight, const float ma
std::vector<PositionedGlyph> &positionedGlyphs = shaping.positionedGlyphs;

if (maxWidth) {
if (useBalancedIdeographicBreaking) {
auto lastPositionedGlyph = positionedGlyphs[positionedGlyphs.size() - 1];
uint32_t estimatedLineCount = std::fmax(1, std::ceil(lastPositionedGlyph.x / maxWidth));
maxWidth = lastPositionedGlyph.x / estimatedLineCount;
}

for (uint32_t i = 0; i < positionedGlyphs.size(); i++) {
PositionedGlyph &shape = positionedGlyphs[i];

Expand Down Expand Up @@ -133,8 +142,9 @@ void GlyphSet::lineWrap(Shaping &shaping, const float lineHeight, const float ma
line++;
}

// Spaces, plus word-breaking punctuation that often appears without surrounding spaces.
if (shape.glyph == 0x20 /* space */
// Ideographic characters, spaces, and word-breaking punctuation that often appear without surrounding spaces.
if (useBalancedIdeographicBreaking
|| shape.glyph == 0x20 /* space */
|| shape.glyph == 0x26 /* ampersand */
|| shape.glyph == 0x2b /* plus sign */
|| shape.glyph == 0x2d /* hyphen-minus */
Expand All @@ -143,7 +153,8 @@ void GlyphSet::lineWrap(Shaping &shaping, const float lineHeight, const float ma
|| shape.glyph == 0xb7 /* middle dot */
|| shape.glyph == 0x200b /* zero-width space */
|| shape.glyph == 0x2010 /* hyphen */
|| shape.glyph == 0x2013 /* en dash */) {
|| shape.glyph == 0x2013 /* en dash */
|| util::i18n::allowsIdeographicBreaking(shape.glyph)) {
lastSafeBreak = i;
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/mbgl/text/glyph_set.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ class GlyphSet {
float horizontalAlign, float verticalAlign, float justify,
float spacing, const Point<float> &translate) const;
void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign,
float verticalAlign, float justify, const Point<float> &translate) const;
float verticalAlign, float justify, const Point<float> &translate,
bool useBalancedIdeographicBreaking) const;

private:
std::map<uint32_t, SDFGlyph> sdfs;
Expand Down
62 changes: 62 additions & 0 deletions src/mbgl/util/i18n.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include "i18n.hpp"

namespace mbgl {
namespace util {
namespace i18n {

bool allowsIdeographicBreaking(const std::u32string& string) {
for (uint32_t chr : string) {
if (!allowsIdeographicBreaking(chr)) {
return false;
}
}
return true;
}

bool allowsIdeographicBreaking(uint32_t chr) {
// Return early for characters outside all ideographic ranges.
if (chr < 0x2E80) return false;

// CJK Radicals Supplement, Kangxi Radicals, Ideographic Description Characters, CJK Symbols and Punctuation: “⺀” to “〿”
if (chr >= 0x2E80 && chr <= 0x303F) return true;

// Hiragana: before “ぁ” to “ゟ”
if (chr >= 0x3040 && chr <= 0x309F) return true;

// Katakana: “゠” to “ヿ”
if (chr >= 0x30A0 && chr <= 0x30FF) return true;

// CJK Strokes: “㇀” to past “㇣”
if (chr >= 0x31C0 && chr <= 0x31EF) return true;

// Katakana Phonetic Extensions: “ㇰ” to “ㇿ”
if (chr >= 0x31F0 && chr <= 0x31FF) return true;

// Enclosed CJK Letters and Months, CJK Compatibility: “㈀” to “㏿”
if (chr >= 0x3200 && chr <= 0x33FF) return true;

// CJK Unified Ideographs Extension A: “㐀” to past “䶵”
if (chr >= 0x3400 && chr <= 0x4DBF) return true;

// CJK Unified Ideographs: “一” to past “鿕”
if (chr >= 0x4E00 && chr <= 0x9FFF) return true;

// Yi Syllables, Yi Radicals: “ꀀ” to past “꓆”
if (chr >= 0xA000 && chr <= 0xA4CF) return true;

// CJK Compatibility Forms: “︰” to “﹏”
if (chr >= 0xFE30 && chr <= 0xFE4F) return true;

// CJK Compatibility Ideographs: “豈” to past “龎”
if (chr >= 0xF900 && chr <= 0xFAFF) return true;

// Halfwidth and Fullwidth Forms: before “!” to past “○”
if (chr >= 0xFF00 && chr <= 0xFFEF) return true;

return false;
}

} // namespace i18n
} // namespace util
} // namespace mbgl

18 changes: 18 additions & 0 deletions src/mbgl/util/i18n.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <string>

namespace mbgl {
namespace util {
namespace i18n {

/// Returns whether a line break can be inserted after any character in the given string.
/// If false, line breaking should occur on word boundaries instead.
bool allowsIdeographicBreaking(const std::u32string& string);

/// Returns whether a line break can be inserted after the character indicated by the given Unicode codepoint.
bool allowsIdeographicBreaking(uint32_t chr);

} // namespace i18n
} // namespace util
} // namespace mbgl

0 comments on commit 150dfc6

Please sign in to comment.