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

Commit 150dfc6

Browse files
committed
[core] Line-break ideographic text by character
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.
1 parent a4d259c commit 150dfc6

File tree

8 files changed

+105
-9
lines changed

8 files changed

+105
-9
lines changed

cmake/core-files.cmake

+2
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,8 @@ set(MBGL_CORE_FILES
451451
src/mbgl/util/http_header.hpp
452452
src/mbgl/util/http_timeout.cpp
453453
src/mbgl/util/http_timeout.hpp
454+
src/mbgl/util/i18n.cpp
455+
src/mbgl/util/i18n.hpp
454456
src/mbgl/util/interpolate.hpp
455457
src/mbgl/util/intersection_tests.cpp
456458
src/mbgl/util/intersection_tests.hpp

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"lodash": "^4.16.4",
2424
"mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#98a56d538b11fb331aa67a6d632d6ecd6821b007",
2525
"mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#7f62a4fc9f21e619824d68abbc4b03cbc1685572",
26-
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#844ecd23c3314eec509dbcdaf0f956d5504e6fef",
26+
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#4a508b6da9bad0fe267b0ccd4440fa061da3b4f8",
2727
"mkdirp": "^0.5.1",
2828
"node-cmake": "^1.2.1",
2929
"request": "^2.72.0",

platform/ios/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
1919
* 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))
2020
* 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))
2121
* 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))
22+
* 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))
2223
* `-[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))
2324
* 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))
2425
* 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))

platform/macos/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* 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))
1616
* 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))
1717
* 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))
18+
* 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))
1819
* 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))
1920
* 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))
2021
* 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))

src/mbgl/text/glyph_set.cpp

+18-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <mbgl/text/glyph_set.hpp>
22
#include <mbgl/platform/log.hpp>
33
#include <mbgl/math/minmax.hpp>
4+
#include <mbgl/util/i18n.hpp>
45

56
#include <cassert>
67

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

57-
lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify, translate);
58+
lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify, translate,
59+
util::i18n::allowsIdeographicBreaking(string));
5860

5961
return shaping;
6062
}
@@ -85,9 +87,10 @@ void justifyLine(std::vector<PositionedGlyph> &positionedGlyphs, const std::map<
8587
}
8688
}
8789

88-
void GlyphSet::lineWrap(Shaping &shaping, const float lineHeight, const float maxWidth,
89-
const float horizontalAlign, const float verticalAlign,
90-
const float justify, const Point<float> &translate) const {
90+
void GlyphSet::lineWrap(Shaping &shaping, const float lineHeight, float maxWidth,
91+
const float horizontalAlign, const float verticalAlign,
92+
const float justify, const Point<float> &translate,
93+
bool useBalancedIdeographicBreaking) const {
9194
uint32_t lastSafeBreak = 0;
9295

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

101104
if (maxWidth) {
105+
if (useBalancedIdeographicBreaking) {
106+
auto lastPositionedGlyph = positionedGlyphs[positionedGlyphs.size() - 1];
107+
uint32_t estimatedLineCount = std::fmax(1, std::ceil(lastPositionedGlyph.x / maxWidth));
108+
maxWidth = lastPositionedGlyph.x / estimatedLineCount;
109+
}
110+
102111
for (uint32_t i = 0; i < positionedGlyphs.size(); i++) {
103112
PositionedGlyph &shape = positionedGlyphs[i];
104113

@@ -133,8 +142,9 @@ void GlyphSet::lineWrap(Shaping &shaping, const float lineHeight, const float ma
133142
line++;
134143
}
135144

136-
// Spaces, plus word-breaking punctuation that often appears without surrounding spaces.
137-
if (shape.glyph == 0x20 /* space */
145+
// Ideographic characters, spaces, and word-breaking punctuation that often appear without surrounding spaces.
146+
if (useBalancedIdeographicBreaking
147+
|| shape.glyph == 0x20 /* space */
138148
|| shape.glyph == 0x26 /* ampersand */
139149
|| shape.glyph == 0x2b /* plus sign */
140150
|| shape.glyph == 0x2d /* hyphen-minus */
@@ -143,7 +153,8 @@ void GlyphSet::lineWrap(Shaping &shaping, const float lineHeight, const float ma
143153
|| shape.glyph == 0xb7 /* middle dot */
144154
|| shape.glyph == 0x200b /* zero-width space */
145155
|| shape.glyph == 0x2010 /* hyphen */
146-
|| shape.glyph == 0x2013 /* en dash */) {
156+
|| shape.glyph == 0x2013 /* en dash */
157+
|| util::i18n::allowsIdeographicBreaking(shape.glyph)) {
147158
lastSafeBreak = i;
148159
}
149160
}

src/mbgl/text/glyph_set.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ class GlyphSet {
1313
float horizontalAlign, float verticalAlign, float justify,
1414
float spacing, const Point<float> &translate) const;
1515
void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign,
16-
float verticalAlign, float justify, const Point<float> &translate) const;
16+
float verticalAlign, float justify, const Point<float> &translate,
17+
bool useBalancedIdeographicBreaking) const;
1718

1819
private:
1920
std::map<uint32_t, SDFGlyph> sdfs;

src/mbgl/util/i18n.cpp

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include "i18n.hpp"
2+
3+
namespace mbgl {
4+
namespace util {
5+
namespace i18n {
6+
7+
bool allowsIdeographicBreaking(const std::u32string& string) {
8+
for (uint32_t chr : string) {
9+
if (!allowsIdeographicBreaking(chr)) {
10+
return false;
11+
}
12+
}
13+
return true;
14+
}
15+
16+
bool allowsIdeographicBreaking(uint32_t chr) {
17+
// Return early for characters outside all ideographic ranges.
18+
if (chr < 0x2E80) return false;
19+
20+
// CJK Radicals Supplement, Kangxi Radicals, Ideographic Description Characters, CJK Symbols and Punctuation: “⺀” to “〿”
21+
if (chr >= 0x2E80 && chr <= 0x303F) return true;
22+
23+
// Hiragana: before “ぁ” to “ゟ”
24+
if (chr >= 0x3040 && chr <= 0x309F) return true;
25+
26+
// Katakana: “゠” to “ヿ”
27+
if (chr >= 0x30A0 && chr <= 0x30FF) return true;
28+
29+
// CJK Strokes: “㇀” to past “㇣”
30+
if (chr >= 0x31C0 && chr <= 0x31EF) return true;
31+
32+
// Katakana Phonetic Extensions: “ㇰ” to “ㇿ”
33+
if (chr >= 0x31F0 && chr <= 0x31FF) return true;
34+
35+
// Enclosed CJK Letters and Months, CJK Compatibility: “㈀” to “㏿”
36+
if (chr >= 0x3200 && chr <= 0x33FF) return true;
37+
38+
// CJK Unified Ideographs Extension A: “㐀” to past “䶵”
39+
if (chr >= 0x3400 && chr <= 0x4DBF) return true;
40+
41+
// CJK Unified Ideographs: “一” to past “鿕”
42+
if (chr >= 0x4E00 && chr <= 0x9FFF) return true;
43+
44+
// Yi Syllables, Yi Radicals: “ꀀ” to past “꓆”
45+
if (chr >= 0xA000 && chr <= 0xA4CF) return true;
46+
47+
// CJK Compatibility Forms: “︰” to “﹏”
48+
if (chr >= 0xFE30 && chr <= 0xFE4F) return true;
49+
50+
// CJK Compatibility Ideographs: “豈” to past “龎”
51+
if (chr >= 0xF900 && chr <= 0xFAFF) return true;
52+
53+
// Halfwidth and Fullwidth Forms: before “!” to past “○”
54+
if (chr >= 0xFF00 && chr <= 0xFFEF) return true;
55+
56+
return false;
57+
}
58+
59+
} // namespace i18n
60+
} // namespace util
61+
} // namespace mbgl
62+

src/mbgl/util/i18n.hpp

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#pragma once
2+
3+
#include <string>
4+
5+
namespace mbgl {
6+
namespace util {
7+
namespace i18n {
8+
9+
/// Returns whether a line break can be inserted after any character in the given string.
10+
/// If false, line breaking should occur on word boundaries instead.
11+
bool allowsIdeographicBreaking(const std::u32string& string);
12+
13+
/// Returns whether a line break can be inserted after the character indicated by the given Unicode codepoint.
14+
bool allowsIdeographicBreaking(uint32_t chr);
15+
16+
} // namespace i18n
17+
} // namespace util
18+
} // namespace mbgl

0 commit comments

Comments
 (0)