diff --git a/src/mbgl/layout/symbol_feature.hpp b/src/mbgl/layout/symbol_feature.hpp index 9e0eacaac51..b1ac3ffe78a 100644 --- a/src/mbgl/layout/symbol_feature.hpp +++ b/src/mbgl/layout/symbol_feature.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -11,6 +12,7 @@ class SymbolFeature { public: GeometryCollection geometry; optional text; + optional writingDirection; optional icon; std::size_t index; }; diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 00395fafc23..6556d65d3d0 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 2b6967110c1..4c127e9cabc 100644 --- a/src/mbgl/text/bidi.cpp +++ b/src/mbgl/text/bidi.cpp @@ -2,6 +2,7 @@ #include #include +#include #include 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(input.size())) == UBIDI_RTL; + return ubidi_getBaseDirection(input.c_str(), static_cast(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 030ac88ce2a..e29bf041e2a 100644 --- a/src/mbgl/text/bidi.hpp +++ b/src/mbgl/text/bidi.hpp @@ -3,17 +3,20 @@ #include #include -#include -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 f0e3991540c..67c2ab93e44 100644 --- a/src/mbgl/text/glyph_set.cpp +++ b/src/mbgl/text/glyph_set.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include @@ -31,7 +32,7 @@ const std::map &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 &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 &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 &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 004cae343d9..b4fcf4c3a4d 100644 --- a/src/mbgl/text/glyph_set.hpp +++ b/src/mbgl/text/glyph_set.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -9,12 +10,12 @@ class GlyphSet { public: void insert(uint32_t id, SDFGlyph&&); const std::map &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 &translate) const; void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign, float verticalAlign, float justify, const Point &translate, - bool useBalancedIdeographicBreaking) const; + bool useBalancedIdeographicBreaking, const WritingDirection writingDirection) const; private: std::map sdfs; diff --git a/test/util/merge_lines.test.cpp b/test/util/merge_lines.test.cpp index 30cd1af0682..8383183e0aa 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 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 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 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 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 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 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);