diff options
author | Chris Loer <chris.loer@gmail.com> | 2016-11-16 12:50:57 -0800 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2016-11-17 14:33:03 -0800 |
commit | d4fc66af3924805d40576989c1e139ddafcc4670 (patch) | |
tree | 87e8dac7bc21051086f2765b4d04e884aad4cec7 | |
parent | bceeba29a3fa85105c21718ed0be8704508ab585 (diff) | |
download | qtlocation-mapboxgl-d4fc66af3924805d40576989c1e139ddafcc4670.tar.gz |
[core] Add minimal line breaking support for RTL text.
-rw-r--r-- | src/mbgl/layout/symbol_feature.hpp | 2 | ||||
-rw-r--r-- | src/mbgl/layout/symbol_layout.cpp | 5 | ||||
-rw-r--r-- | src/mbgl/text/bidi.cpp | 7 | ||||
-rw-r--r-- | src/mbgl/text/bidi.hpp | 9 | ||||
-rw-r--r-- | src/mbgl/text/glyph_set.cpp | 13 | ||||
-rw-r--r-- | src/mbgl/text/glyph_set.hpp | 5 | ||||
-rw-r--r-- | test/util/merge_lines.test.cpp | 48 |
7 files changed, 52 insertions, 37 deletions
diff --git a/src/mbgl/layout/symbol_feature.hpp b/src/mbgl/layout/symbol_feature.hpp index 9e0eacaac5..b1ac3ffe78 100644 --- a/src/mbgl/layout/symbol_feature.hpp +++ b/src/mbgl/layout/symbol_feature.hpp @@ -1,5 +1,6 @@ #pragma once +#include <mbgl/text/bidi.hpp> #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/util/optional.hpp> @@ -11,6 +12,7 @@ class SymbolFeature { public: GeometryCollection geometry; optional<std::u16string> text; + optional<WritingDirection> writingDirection; optional<std::string> icon; std::size_t index; }; diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 00395fafc2..6556d65d3d 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -92,7 +92,9 @@ SymbolLayout::SymbolLayout(std::string bucketName_, u8string = platform::lowercase(u8string); } - ft.text = bidi.bidiTransform(util::utf8_to_utf16::convert(u8string)); + std::u16string u16string = util::utf8_to_utf16::convert(u8string); + ft.text = bidi.bidiTransform(u16string); + ft.writingDirection = bidi.baseWritingDirection(u16string); // Loop through all characters of this text and collect unique codepoints. for (char16_t chr : *ft.text) { @@ -196,6 +198,7 @@ void SymbolLayout::prepare(uintptr_t tileUID, if (feature.text) { shapedText = glyphSet->getShaping( /* string */ *feature.text, + /* base direction of text */ *feature.writingDirection, /* maxWidth: ems */ layout.symbolPlacement != SymbolPlacementType::Line ? layout.textMaxWidth * 24 : 0, /* lineHeight: ems */ layout.textLineHeight * 24, diff --git a/src/mbgl/text/bidi.cpp b/src/mbgl/text/bidi.cpp index 2b6967110c..4c127e9cab 100644 --- a/src/mbgl/text/bidi.cpp +++ b/src/mbgl/text/bidi.cpp @@ -2,6 +2,7 @@ #include <mbgl/text/bidi.hpp> #include <unicode/ubidi.h> +#include <unicode/ubiditransform.h> #include <unicode/ushape.h> namespace mbgl { @@ -44,10 +45,12 @@ std::u16string BiDi::bidiTransform(const std::u16string& input) { return std::u16string(outputText.get(), outputLength); } -bool BiDi::baseDirectionRightToLeft(const std::u16string& input) { +WritingDirection BiDi::baseWritingDirection(const std::u16string& input) { // This just looks for the first character with a strong direction property, it does not perform // the BiDi algorithm - return ubidi_getBaseDirection(input.c_str(), static_cast<int32_t>(input.size())) == UBIDI_RTL; + return ubidi_getBaseDirection(input.c_str(), static_cast<int32_t>(input.size())) == UBIDI_RTL + ? WritingDirection::RightToLeft + : WritingDirection::LeftToRight; } } // end namespace mbgl diff --git a/src/mbgl/text/bidi.hpp b/src/mbgl/text/bidi.hpp index 030ac88ce2..e29bf041e2 100644 --- a/src/mbgl/text/bidi.hpp +++ b/src/mbgl/text/bidi.hpp @@ -3,17 +3,20 @@ #include <string> #include <mbgl/util/noncopyable.hpp> -#include <unicode/ubiditransform.h> -namespace mbgl { +struct UBiDiTransform; +namespace mbgl { + +enum class WritingDirection : bool { LeftToRight, RightToLeft }; + class BiDi : private util::noncopyable { public: BiDi(); ~BiDi(); std::u16string bidiTransform(const std::u16string&); - bool baseDirectionRightToLeft(const std::u16string&); + WritingDirection baseWritingDirection(const std::u16string&); private: UBiDiTransform* transform; diff --git a/src/mbgl/text/glyph_set.cpp b/src/mbgl/text/glyph_set.cpp index f0e3991540..67c2ab93e4 100644 --- a/src/mbgl/text/glyph_set.cpp +++ b/src/mbgl/text/glyph_set.cpp @@ -2,6 +2,7 @@ #include <mbgl/platform/log.hpp> #include <mbgl/math/minmax.hpp> #include <mbgl/util/i18n.hpp> +#include <mbgl/text/bidi.hpp> #include <cassert> @@ -31,7 +32,7 @@ const std::map<uint32_t, SDFGlyph> &GlyphSet::getSDFs() const { return sdfs; } -const Shaping GlyphSet::getShaping(const std::u16string &string, const float maxWidth, +const Shaping GlyphSet::getShaping(const std::u16string &string, const WritingDirection writingDirection, const float maxWidth, const float lineHeight, const float horizontalAlign, const float verticalAlign, const float justify, const float spacing, const Point<float> &translate) const { @@ -56,7 +57,7 @@ const Shaping GlyphSet::getShaping(const std::u16string &string, const float max return shaping; lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify, translate, - util::i18n::allowsIdeographicBreaking(string)); + util::i18n::allowsIdeographicBreaking(string), writingDirection); return shaping; } @@ -90,7 +91,9 @@ void justifyLine(std::vector<PositionedGlyph> &positionedGlyphs, const std::map< 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 { + bool useBalancedIdeographicBreaking, const WritingDirection writingDirection) const { + float lineFeedOffset = writingDirection == WritingDirection::RightToLeft ? -lineHeight : lineHeight; + uint32_t lastSafeBreak = 0; uint32_t lengthBeforeCurrentLine = 0; @@ -112,7 +115,7 @@ void GlyphSet::lineWrap(Shaping &shaping, const float lineHeight, float maxWidth PositionedGlyph &shape = positionedGlyphs[i]; shape.x -= lengthBeforeCurrentLine; - shape.y += lineHeight * line; + shape.y += lineFeedOffset * line; if (shape.x > maxWidth && lastSafeBreak > 0) { @@ -120,7 +123,7 @@ void GlyphSet::lineWrap(Shaping &shaping, const float lineHeight, float maxWidth maxLineLength = util::max(lineLength, maxLineLength); for (uint32_t k = lastSafeBreak + 1; k <= i; k++) { - positionedGlyphs[k].y += lineHeight; + positionedGlyphs[k].y += lineFeedOffset; positionedGlyphs[k].x -= lineLength; } diff --git a/src/mbgl/text/glyph_set.hpp b/src/mbgl/text/glyph_set.hpp index 004cae343d..b4fcf4c3a4 100644 --- a/src/mbgl/text/glyph_set.hpp +++ b/src/mbgl/text/glyph_set.hpp @@ -1,5 +1,6 @@ #pragma once +#include <mbgl/text/bidi.hpp> #include <mbgl/text/glyph.hpp> #include <mbgl/util/geometry.hpp> @@ -9,12 +10,12 @@ class GlyphSet { public: void insert(uint32_t id, SDFGlyph&&); const std::map<uint32_t, SDFGlyph> &getSDFs() const; - const Shaping getShaping(const std::u16string &string, float maxWidth, float lineHeight, + const Shaping getShaping(const std::u16string &string, const WritingDirection writingDirection, float maxWidth, float lineHeight, 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, - bool useBalancedIdeographicBreaking) const; + bool useBalancedIdeographicBreaking, const WritingDirection writingDirection) const; private: std::map<uint32_t, SDFGlyph> sdfs; diff --git a/test/util/merge_lines.test.cpp b/test/util/merge_lines.test.cpp index 30cd1af068..8383183e0a 100644 --- a/test/util/merge_lines.test.cpp +++ b/test/util/merge_lines.test.cpp @@ -9,21 +9,21 @@ const std::u16string bbb = u"b"; TEST(MergeLines, SameText) { // merges lines with the same text std::vector<mbgl::SymbolFeature> input1 = { - { {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, 0 }, - { {{{4, 0}, {5, 0}, {6, 0}}}, bbb, {}, 0 }, - { {{{8, 0}, {9, 0}}}, aaa, {}, 0 }, - { {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 }, - { {{{6, 0}, {7, 0}, {8, 0}}}, aaa, {}, 0 }, - { {{{5, 0}, {6, 0}}}, aaa, {}, 0 } + { {{{0, 0}, {1, 0}, {2, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{{4, 0}, {5, 0}, {6, 0}}}, bbb, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{{8, 0}, {9, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{{2, 0}, {3, 0}, {4, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{{6, 0}, {7, 0}, {8, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{{5, 0}, {6, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 } }; const std::vector<mbgl::SymbolFeature> expected1 = { - { {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 }, - { {{{4, 0}, {5, 0}, {6, 0}}}, bbb, {}, 0 }, - { {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, aaa, {}, 0 }, - { {{}}, aaa, {}, 0 }, - { {{}}, aaa, {}, 0 }, - { {{}}, aaa, {}, 0 } + { {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{{4, 0}, {5, 0}, {6, 0}}}, bbb, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 } }; mbgl::util::mergeLines(input1); @@ -36,15 +36,15 @@ TEST(MergeLines, SameText) { TEST(MergeLines, BothEnds) { // mergeLines handles merge from both ends std::vector<mbgl::SymbolFeature> input2 = { - { {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, 0 }, - { {{{4, 0}, {5, 0}, {6, 0}}}, aaa, {}, 0 }, - { {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 } + { {{{0, 0}, {1, 0}, {2, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{{4, 0}, {5, 0}, {6, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{{2, 0}, {3, 0}, {4, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 } }; const std::vector<mbgl::SymbolFeature> expected2 = { - { {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, aaa, {}, 0 }, - { {{}}, aaa, {}, 0 }, - { {{}}, aaa, {}, 0 } + { {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 } }; mbgl::util::mergeLines(input2); @@ -57,15 +57,15 @@ TEST(MergeLines, BothEnds) { TEST(MergeLines, CircularLines) { // mergeLines handles circular lines std::vector<mbgl::SymbolFeature> input3 = { - { {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, 0 }, - { {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 }, - { {{{4, 0}, {0, 0}}}, aaa, {}, 0 } + { {{{0, 0}, {1, 0}, {2, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{{2, 0}, {3, 0}, {4, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{{4, 0}, {0, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 } }; const std::vector<mbgl::SymbolFeature> expected3 = { - { {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, aaa, {}, 0 }, - { {{}}, aaa, {}, 0 }, - { {{}}, aaa, {}, 0 } + { {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 }, + { {{}}, aaa, mbgl::WritingDirection::LeftToRight, {}, 0 } }; mbgl::util::mergeLines(input3); |