From 4298980bbc713ba8f13c47f12e186dfd4fa04ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Wed, 17 Aug 2016 00:44:08 -0700 Subject: [PATCH 1/3] [core] Rudimentary right-to-left text support Reverse text that begins with a character in the Arabic, Hebrew, Syriac, or Thaana Unicode blocks. This change does not include complex text layout (bidirectional text, context-sensitive shaping, ligatures, or ordering). --- src/mbgl/text/glyph_set.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/mbgl/text/glyph_set.cpp b/src/mbgl/text/glyph_set.cpp index 0875a83850d..2a7b1ec15d3 100644 --- a/src/mbgl/text/glyph_set.cpp +++ b/src/mbgl/text/glyph_set.cpp @@ -70,6 +70,25 @@ void align(Shaping &shaping, const float justify, const float horizontalAlign, glyph.y += shiftY; } } + +void reverse(std::vector &positionedGlyphs, const std::map &sdfs, const float horizontalAlign, const uint32_t maxLineLength) { + auto first = positionedGlyphs.begin()->glyph; + auto last = positionedGlyphs.end()->glyph; + if ((first >= 0x600 && first <= 0x6ff) /* Arabic */ + || (first >= 0x750 && first <= 0x77f) /* Arabic Supplement */ + || (first >= 0x8a0 && first <= 0x8ff) /* Arabic Extended-A */ + || (first >= 0x590 && first <= 0x5ff) /* Hebrew */ + || (first >= 0x700 && first <= 0x74f) /* Syriac */ + || (first >= 0x780 && first <= 0x7bf) /* Thaana */) { + for (auto& glyph : positionedGlyphs) { + glyph.x *= -1; + auto it = sdfs.find(glyph.glyph); + if (it != sdfs.end()) { + glyph.x -= it->second.metrics.advance; + } + } + } +} void justifyLine(std::vector &positionedGlyphs, const std::map &sdfs, uint32_t start, uint32_t end, float justify) { @@ -159,6 +178,7 @@ void GlyphSet::lineWrap(Shaping &shaping, const float lineHeight, const float ma justifyLine(positionedGlyphs, sdfs, lineStartIndex, uint32_t(positionedGlyphs.size()) - 1, justify); align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line, translate); + reverse(positionedGlyphs, sdfs, horizontalAlign, maxLineLength); // Calculate the bounding box shaping.top += -verticalAlign * height; From ce8fccddb118ab0c86000354378a8952ec5785a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Wed, 17 Aug 2016 10:26:10 -0700 Subject: [PATCH 2/3] =?UTF-8?q?[core]=20Nai=CC=88ve=20bidirectional=20text?= =?UTF-8?q?=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mbgl/text/glyph_set.cpp | 82 ++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 11 deletions(-) diff --git a/src/mbgl/text/glyph_set.cpp b/src/mbgl/text/glyph_set.cpp index 2a7b1ec15d3..bfcd8e44330 100644 --- a/src/mbgl/text/glyph_set.cpp +++ b/src/mbgl/text/glyph_set.cpp @@ -70,16 +70,31 @@ void align(Shaping &shaping, const float justify, const float horizontalAlign, glyph.y += shiftY; } } - -void reverse(std::vector &positionedGlyphs, const std::map &sdfs, const float horizontalAlign, const uint32_t maxLineLength) { - auto first = positionedGlyphs.begin()->glyph; - auto last = positionedGlyphs.end()->glyph; - if ((first >= 0x600 && first <= 0x6ff) /* Arabic */ - || (first >= 0x750 && first <= 0x77f) /* Arabic Supplement */ - || (first >= 0x8a0 && first <= 0x8ff) /* Arabic Extended-A */ - || (first >= 0x590 && first <= 0x5ff) /* Hebrew */ - || (first >= 0x700 && first <= 0x74f) /* Syriac */ - || (first >= 0x780 && first <= 0x7bf) /* Thaana */) { + +// Returns true if the glyph is a strong right-to-left glyph. +bool isRTL(uint32_t glyph) { + // Use the major RTL Unicode code blocks as a rough approximation. + return ((glyph >= 0x600 && glyph <= 0x6ff) /* Arabic */ + || (glyph >= 0x750 && glyph <= 0x77f) /* Arabic Supplement */ + || (glyph >= 0x8a0 && glyph <= 0x8ff) /* Arabic Extended-A */ + || (glyph >= 0x590 && glyph <= 0x5ff) /* Hebrew */ + || (glyph >= 0x700 && glyph <= 0x74f) /* Syriac */ + || (glyph >= 0x780 && glyph <= 0x7bf) /* Thaana */); +} + +// Reverse the string to right-to-left orientation if necessary. +void reverse(std::vector &positionedGlyphs, const std::map &sdfs) { + size_t numRTLChars = 0; + for (auto& positionedGlyph : positionedGlyphs) { + auto glyph = positionedGlyph.glyph; + if (isRTL(glyph)) { + numRTLChars++; + } + } + // Treat the entire string as right-to-left if most characters are strong right-to-left characters. + bool isMostlyRTL = numRTLChars >= static_cast(positionedGlyphs.size()) / 2.0f; + if (isMostlyRTL) { + // Reverse the entire string. for (auto& glyph : positionedGlyphs) { glyph.x *= -1; auto it = sdfs.find(glyph.glyph); @@ -87,6 +102,51 @@ void reverse(std::vector &positionedGlyphs, const std::mapsecond.metrics.advance; } } + // For bilingual labels, unreverse short spans of LTR text in predominantly RTL strings. + for (auto start = positionedGlyphs.begin(), end = start; end != positionedGlyphs.end(); ++end) { + if (start != end && (isRTL(end->glyph) || end->y != (end - 1)->y)) { + // End of LTR run or line wrap. + auto left = (end - 1)->x; + auto right = start->x; + auto leftSDF = sdfs.find((end - 1)->glyph); + if (leftSDF != sdfs.end()) { + right -= leftSDF->second.metrics.advance; + } + for (; start != end; ++start) { + start->x = left + right - start->x; + auto sdf = sdfs.find(start->glyph); + if (sdf != sdfs.end()) { + start->x += sdf->second.metrics.advance; + } + } + ++start; + } else if (isRTL(end->glyph)) { + ++start; + } + } + } else { + // For bilingual labels, reverse short spans of RTL text in predominantly LTR strings. + for (auto start = positionedGlyphs.begin(), end = start; end != positionedGlyphs.end(); ++end) { + if (start != end && (!isRTL(end->glyph) || end->y != (end - 1)->y)) { + // End of RTL run or line wrap. + auto left = start->x; + auto right = (end - 1)->x; + auto rightSDF = sdfs.find((end - 1)->glyph); + if (rightSDF != sdfs.end()) { + right += rightSDF->second.metrics.advance; + } + for (; start != end; ++start) { + start->x = left + right - start->x; + auto sdf = sdfs.find(start->glyph); + if (sdf != sdfs.end()) { + start->x -= sdf->second.metrics.advance; + } + } + ++start; + } else if (!isRTL(end->glyph)) { + ++start; + } + } } } @@ -178,7 +238,7 @@ void GlyphSet::lineWrap(Shaping &shaping, const float lineHeight, const float ma justifyLine(positionedGlyphs, sdfs, lineStartIndex, uint32_t(positionedGlyphs.size()) - 1, justify); align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line, translate); - reverse(positionedGlyphs, sdfs, horizontalAlign, maxLineLength); + reverse(positionedGlyphs, sdfs); // Calculate the bounding box shaping.top += -verticalAlign * height; From d823b3ee3c27b2ce0b2a3e64d4cb67d616705e87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Sun, 21 Aug 2016 23:02:20 -0700 Subject: [PATCH 3/3] [core] Eastern Arabic numerals are strong LTR --- src/mbgl/text/glyph_set.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/text/glyph_set.cpp b/src/mbgl/text/glyph_set.cpp index bfcd8e44330..d2b7f65e978 100644 --- a/src/mbgl/text/glyph_set.cpp +++ b/src/mbgl/text/glyph_set.cpp @@ -74,7 +74,7 @@ void align(Shaping &shaping, const float justify, const float horizontalAlign, // Returns true if the glyph is a strong right-to-left glyph. bool isRTL(uint32_t glyph) { // Use the major RTL Unicode code blocks as a rough approximation. - return ((glyph >= 0x600 && glyph <= 0x6ff) /* Arabic */ + return ((glyph >= 0x600 && glyph <= 0x6ff && (glyph < 0x660 || glyph > 0x669) && glyph != 0x66b && glyph != 0x66c && (glyph < 0x6f0 || glyph > 0x6f9)) /* Arabic */ || (glyph >= 0x750 && glyph <= 0x77f) /* Arabic Supplement */ || (glyph >= 0x8a0 && glyph <= 0x8ff) /* Arabic Extended-A */ || (glyph >= 0x590 && glyph <= 0x5ff) /* Hebrew */