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

[core] Advanced label formatting using ["text-section"] expression #13904

Closed
wants to merge 8 commits into from
2 changes: 1 addition & 1 deletion platform/default/src/mbgl/text/bidi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ std::vector<StyledText> BiDi::processStyledText(const StyledText& input, std::se
// Each time we see a change in style, render a reversed chunk
// of everything since the last change
std::size_t styleRunStart = logicalEnd;
uint8_t currentStyleIndex = styleIndices.at(styleRunStart - 1);
std::size_t currentStyleIndex = styleIndices.at(styleRunStart - 1);
for (std::size_t i = logicalEnd - 1; i >= logicalStart; i--) {
if (currentStyleIndex != styleIndices.at(i) || i == logicalStart) {
std::size_t styleRunEnd = i == logicalStart ? i : i + 1;
Expand Down
2 changes: 1 addition & 1 deletion platform/qt/src/bidi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ std::vector<StyledText> BiDi::processStyledText(const StyledText& input, std::se
for (std::size_t lineBreakPoint : lineBreakPoints) {
transformedLines.emplace_back(
input.first.substr(start, lineBreakPoint - start),
std::vector<uint8_t>(input.second.begin() + start, input.second.begin() + lineBreakPoint));
std::vector<std::size_t>(input.second.begin() + start, input.second.begin() + lineBreakPoint));
start = lineBreakPoint;
}

Expand Down
2 changes: 1 addition & 1 deletion src/mbgl/text/bidi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ std::u16string applyArabicShaping(const std::u16string&);
// the styling options to use for rendering that code point
// The data structure is intended to accomodate the reordering/interleaving
// of formatting that can happen when BiDi rearranges inputs
using StyledText = std::pair<std::u16string, std::vector<uint8_t>>;
using StyledText = std::pair<std::u16string, std::vector<std::size_t>>;

class BiDi : private util::noncopyable {
public:
Expand Down
8 changes: 5 additions & 3 deletions src/mbgl/text/glyph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace mbgl {

using GlyphID = char16_t;
using GlyphIDs = std::set<GlyphID>;

// Note: this only works for the BMP
GlyphRange getGlyphRange(GlyphID glyph);

Expand Down Expand Up @@ -59,8 +59,8 @@ using GlyphMap = std::map<FontStackHash, Glyphs>;

class PositionedGlyph {
public:
explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, bool vertical_, FontStackHash font_, float scale_)
: glyph(glyph_), x(x_), y(y_), vertical(vertical_), font(font_), scale(scale_)
explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, bool vertical_, FontStackHash font_, float scale_, std::size_t sectionIndex_ = 0)
: glyph(glyph_), x(x_), y(y_), vertical(vertical_), font(font_), scale(scale_), sectionIndex(sectionIndex_)
{}

GlyphID glyph = 0;
Expand All @@ -70,6 +70,8 @@ class PositionedGlyph {

FontStackHash font = 0;
float scale = 0.0;
// Maps positioned glyph to TaggedString section
std::size_t sectionIndex;
};

enum class WritingModeType : uint8_t;
Expand Down
2 changes: 1 addition & 1 deletion src/mbgl/text/quads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText,
br = util::matrixMultiply(matrix, br);
}

quads.emplace_back(tl, tr, bl, br, rect, shapedText.writingMode, glyphOffset);
quads.emplace_back(tl, tr, bl, br, rect, shapedText.writingMode, glyphOffset, positionedGlyph.sectionIndex);
}

return quads;
Expand Down
7 changes: 5 additions & 2 deletions src/mbgl/text/quads.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ class SymbolQuad {
Point<float> br_,
Rect<uint16_t> tex_,
WritingModeType writingMode_,
Point<float> glyphOffset_)
Point<float> glyphOffset_,
size_t sectionIndex_ = 0)
: tl(std::move(tl_)),
tr(std::move(tr_)),
bl(std::move(bl_)),
br(std::move(br_)),
tex(std::move(tex_)),
writingMode(writingMode_),
glyphOffset(glyphOffset_) {}
glyphOffset(glyphOffset_),
sectionIndex(sectionIndex_){}

Point<float> tl;
Point<float> tr;
Expand All @@ -36,6 +38,7 @@ class SymbolQuad {
Rect<uint16_t> tex;
WritingModeType writingMode;
Point<float> glyphOffset;
size_t sectionIndex;
};

using SymbolQuads = std::vector<SymbolQuad>;
Expand Down
7 changes: 4 additions & 3 deletions src/mbgl/text/shaping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,8 @@ void shapeLines(Shaping& shaping,

std::size_t lineStartIndex = shaping.positionedGlyphs.size();
for (std::size_t i = 0; i < line.length(); i++) {
const SectionOptions& section = line.getSection(i);
const std::size_t sectionIndex = line.getSectionIndex(i);
const SectionOptions& section = line.sectionAt(sectionIndex);
char16_t codePoint = line.getCharCodeAt(i);
auto glyphs = glyphMap.find(section.fontStackHash);
if (glyphs == glyphMap.end()) {
Expand All @@ -318,10 +319,10 @@ void shapeLines(Shaping& shaping,
const Glyph& glyph = **it->second;

if (writingMode == WritingModeType::Horizontal || !util::i18n::hasUprightVerticalOrientation(codePoint)) {
shaping.positionedGlyphs.emplace_back(codePoint, x, y + baselineOffset, false, section.fontStackHash, section.scale);
shaping.positionedGlyphs.emplace_back(codePoint, x, y + baselineOffset, false, section.fontStackHash, section.scale, sectionIndex);
x += glyph.metrics.advance * section.scale + spacing;
} else {
shaping.positionedGlyphs.emplace_back(codePoint, x, baselineOffset, true, section.fontStackHash, section.scale);
shaping.positionedGlyphs.emplace_back(codePoint, x, baselineOffset, true, section.fontStackHash, section.scale, sectionIndex);
x += verticalHeight * section.scale + spacing;
}
}
Expand Down
24 changes: 20 additions & 4 deletions src/mbgl/text/tagged_string.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#include <mbgl/text/tagged_string.hpp>
#include <mbgl/math/minmax.hpp>
#include <mbgl/util/i18n.hpp>

namespace mbgl {

void TaggedString::addSection(const std::u16string& sectionText, double scale, FontStackHash fontStack) {
void TaggedString::addSection(const std::u16string& sectionText, double scale, FontStack fontStack, const optional<FormattedSectionID>& id) {
styledText.first += sectionText;
sections.emplace_back(scale, fontStack);
sections.emplace_back(scale, fontStack, id);
styledText.second.resize(styledText.first.size(), sections.size() - 1);
}

Expand All @@ -19,14 +20,14 @@ void TaggedString::trim() {
std::size_t trailingWhitespace = styledText.first.find_last_not_of(u" \t\n\v\f\r") + 1;

styledText.first = styledText.first.substr(beginningWhitespace, trailingWhitespace - beginningWhitespace);
styledText.second = std::vector<uint8_t>(styledText.second.begin() + beginningWhitespace, styledText.second.begin() + trailingWhitespace);
styledText.second = std::vector<std::size_t>(styledText.second.begin() + beginningWhitespace, styledText.second.begin() + trailingWhitespace);
}
}

double TaggedString::getMaxScale() const {
double maxScale = 0.0;
for (std::size_t i = 0; i < styledText.first.length(); i++) {
maxScale = std::max(maxScale, getSection(i).scale);
maxScale = util::max(maxScale, getSection(i).scale);
}
return maxScale;
}
Expand All @@ -36,4 +37,19 @@ void TaggedString::verticalizePunctuation() {
styledText.first = util::i18n::verticalizePunctuation(styledText.first);
}

bool TaggedString::hasMultipleUniqueSections() const noexcept {
if (sections.size() < 2) {
return false;
}

const auto& id = sections.at(0).id;
for (std::size_t i = 1; i < sections.size(); ++i) {
if (id != sections.at(i).id) {
return true;
}
}

return false;
}

} // namespace mbgl
25 changes: 18 additions & 7 deletions src/mbgl/text/tagged_string.hpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
#pragma once

#include <mbgl/text/glyph.hpp>
#include <mbgl/text/bidi.hpp>
#include <mbgl/style/expression/formatted.hpp>
#include <mbgl/util/font_stack.hpp>

namespace mbgl {

using style::expression::FormattedSectionID;

struct SectionOptions {
SectionOptions(double scale_, FontStackHash fontStackHash_)
: scale(scale_), fontStackHash(fontStackHash_)
SectionOptions(double scale_, FontStack fontStack_, const optional<FormattedSectionID>& id_ = {})
alexshalamov marked this conversation as resolved.
Show resolved Hide resolved
: scale(scale_), fontStackHash(FontStackHasher()(fontStack_)), fontStack(std::move(fontStack_)), id(std::move(id_))
{}

double scale;
FontStackHash fontStackHash;
FontStack fontStack;
optional<FormattedSectionID> id;
};

/**
Expand All @@ -34,7 +39,7 @@ struct TaggedString {

TaggedString(std::u16string text_, SectionOptions options)
: styledText(std::move(text_),
std::vector<uint8_t>(text_.size(), 0)) {
std::vector<std::size_t>(text_.size(), 0)) {
sections.push_back(std::move(options));
}

Expand Down Expand Up @@ -71,7 +76,11 @@ struct TaggedString {
return styledText;
}

void addSection(const std::u16string& text, double scale, FontStackHash fontStack);
void addSection(const std::u16string& text,
double scale,
FontStack fontStack,
const optional<FormattedSectionID>& id = {});

const SectionOptions& sectionAt(std::size_t index) const {
return sections.at(index);
}
Expand All @@ -80,15 +89,17 @@ struct TaggedString {
return sections;
}

uint8_t getSectionIndex(std::size_t characterIndex) const {
std::size_t getSectionIndex(std::size_t characterIndex) const {
return styledText.second.at(characterIndex);
}

double getMaxScale() const;
void trim();

void verticalizePunctuation();


bool hasMultipleUniqueSections() const noexcept;

private:
StyledText styledText;
std::vector<SectionOptions> sections;
Expand Down
10 changes: 5 additions & 5 deletions test/text/bidi.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,27 +61,27 @@ TEST(BiDi, StyledText) {
std::vector<StyledText> expected;
StyledText input(
applyArabicShaping(u"مكتبة الإسكندرية‎‎ Maktabat al-Iskandarīyah"),
std::vector<uint8_t>{ 0,0,0,0,0,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,4,5,5,5,5,6,6,6,6,6,6,6,6,6,6,7,7,7 }
std::vector<std::size_t>{ 0,0,0,0,0,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,4,5,5,5,5,6,6,6,6,6,6,6,6,6,6,7,7,7 }
);

expected.emplace_back(StyledText(
u"ﺔﺒﺘﻜﻣ",
std::vector<uint8_t>{ 0,0,0,0,0 }
std::vector<std::size_t>{ 0,0,0,0,0 }
));
EXPECT_EQ(expected.rbegin()->first.size(), expected.rbegin()->second.size());
expected.emplace_back(StyledText(
u" ‎‎ﺔﻳﺭﺪﻨﻜﺳﻹﺍ ",
std::vector<uint8_t>{ 2,2,2,2,2,2,2,1,1,1,1,1,1 }
std::vector<std::size_t>{ 2,2,2,2,2,2,2,1,1,1,1,1,1 }
));
EXPECT_EQ(expected.rbegin()->first.size(), expected.rbegin()->second.size());
expected.emplace_back(StyledText(
u"Maktabat al-",
std::vector<uint8_t>{ 2,3,3,3,3,3,4,5,5,5,5,6 }
std::vector<std::size_t>{ 2,3,3,3,3,3,4,5,5,5,5,6 }
));
EXPECT_EQ(expected.rbegin()->first.size(), expected.rbegin()->second.size());
expected.emplace_back(StyledText(
u"Iskandarīyah",
std::vector<uint8_t>{ 6,6,6,6,6,6,6,6,6,7,7,7 }
std::vector<std::size_t>{ 6,6,6,6,6,6,6,6,6,7,7,7 }
));
EXPECT_EQ(expected.rbegin()->first.size(), expected.rbegin()->second.size());

Expand Down
10 changes: 5 additions & 5 deletions test/text/tagged_string.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ using namespace mbgl;
using namespace std::literals;

TEST(TaggedString, Trim) {
TaggedString basic(u" \t\ntrim that and not this \n\t", SectionOptions(1.0f, 0));
TaggedString basic(u" \t\ntrim that and not this \n\t", SectionOptions(1.0f, {}));
basic.trim();
EXPECT_EQ(basic.rawText(), u"trim that and not this");

TaggedString twoSections;
twoSections.addSection(u" \t\ntrim that", 1.5f, 1);
twoSections.addSection(u" and not this \n\t", 0.5f, 2);
twoSections.addSection(u" \t\ntrim that", 1.5f, {});
twoSections.addSection(u" and not this \n\t", 0.5f, {});

twoSections.trim();
EXPECT_EQ(twoSections.rawText(), u"trim that and not this");

TaggedString empty(u"\n\t\v \r \t\n", SectionOptions(1.0f, 0));
TaggedString empty(u"\n\t\v \r \t\n", SectionOptions(1.0f, {}));
empty.trim();
EXPECT_EQ(empty.rawText(), u"");

TaggedString noTrim(u"no trim!", SectionOptions(1.0f, 0));
TaggedString noTrim(u"no trim!", SectionOptions(1.0f, {}));
noTrim.trim();
EXPECT_EQ(noTrim.rawText(), u"no trim!");
}
Expand Down
2 changes: 1 addition & 1 deletion test/util/merge_lines.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class SymbolFeatureStub : public SymbolFeature {
SymbolFeature(std::make_unique<StubGeometryTileFeature>(std::move(id_), type_, std::move(geometry_), std::move(properties_)))
{
if (text_) {
formattedText = TaggedString(*text_, SectionOptions(1.0, 0));
formattedText = TaggedString(*text_, SectionOptions(1.0, {}));
}
icon = std::move(icon_);
index = index_;
Expand Down