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

Commit

Permalink
[core] Add minimal line breaking support for RTL text.
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisLoer committed Nov 17, 2016
1 parent 9422cc8 commit 39a6d73
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 37 deletions.
2 changes: 2 additions & 0 deletions src/mbgl/layout/symbol_feature.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <mbgl/text/bidi.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/util/optional.hpp>

Expand All @@ -11,6 +12,7 @@ class SymbolFeature {
public:
GeometryCollection geometry;
optional<std::u16string> text;
optional<WritingDirection> writingDirection;
optional<std::string> icon;
std::size_t index;
};
Expand Down
5 changes: 4 additions & 1 deletion src/mbgl/layout/symbol_layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 5 additions & 2 deletions src/mbgl/text/bidi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <mbgl/text/bidi.hpp>
#include <unicode/ubidi.h>
#include <unicode/ubiditransform.h>
#include <unicode/ushape.h>

namespace mbgl {
Expand Down Expand Up @@ -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
9 changes: 6 additions & 3 deletions src/mbgl/text/bidi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
13 changes: 8 additions & 5 deletions src/mbgl/text/glyph_set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -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 {
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -112,15 +115,15 @@ 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) {

uint32_t lineLength = positionedGlyphs[lastSafeBreak + 1].x;
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;
}

Expand Down
5 changes: 3 additions & 2 deletions src/mbgl/text/glyph_set.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <mbgl/text/bidi.hpp>
#include <mbgl/text/glyph.hpp>
#include <mbgl/util/geometry.hpp>

Expand All @@ -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;
Expand Down
48 changes: 24 additions & 24 deletions test/util/merge_lines.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit 39a6d73

Please sign in to comment.