[core] Add minimal line breaking support for RTL text.
This commit is contained in:
committed by
John Firebaugh
parent
bceeba29a3
commit
d4fc66af39
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,17 +3,20 @@
|
||||
#include <string>
|
||||
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <unicode/ubiditransform.h>
|
||||
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user