diff options
Diffstat (limited to 'src/mbgl/text')
-rw-r--r-- | src/mbgl/text/bidi.hpp | 7 | ||||
-rw-r--r-- | src/mbgl/text/glyph.hpp | 10 | ||||
-rw-r--r-- | src/mbgl/text/glyph_atlas.cpp | 2 | ||||
-rw-r--r-- | src/mbgl/text/glyph_atlas.hpp | 2 | ||||
-rw-r--r-- | src/mbgl/text/glyph_manager.cpp | 2 | ||||
-rw-r--r-- | src/mbgl/text/glyph_manager.hpp | 2 | ||||
-rw-r--r-- | src/mbgl/text/quads.cpp | 20 | ||||
-rw-r--r-- | src/mbgl/text/quads.hpp | 2 | ||||
-rw-r--r-- | src/mbgl/text/shaping.cpp | 136 | ||||
-rw-r--r-- | src/mbgl/text/shaping.hpp | 5 | ||||
-rw-r--r-- | src/mbgl/text/tagged_string.cpp | 42 | ||||
-rw-r--r-- | src/mbgl/text/tagged_string.hpp | 97 |
12 files changed, 262 insertions, 65 deletions
diff --git a/src/mbgl/text/bidi.hpp b/src/mbgl/text/bidi.hpp index d90f3e5d1b..5ce2887db8 100644 --- a/src/mbgl/text/bidi.hpp +++ b/src/mbgl/text/bidi.hpp @@ -14,6 +14,10 @@ class BiDiImpl; std::u16string applyArabicShaping(const std::u16string&); +// StyledText pairs each code point in a string with an integer indicating +// 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>>; class BiDi : private util::noncopyable { @@ -21,7 +25,10 @@ public: BiDi(); ~BiDi(); + // Given text in logical ordering and a set of line break points, + // return a set of lines in visual order with bidi and line breaking applied std::vector<std::u16string> processText(const std::u16string&, std::set<std::size_t>); + // Same as processText but preserves per-code-point formatting information std::vector<StyledText> processStyledText(const StyledText&, std::set<std::size_t>); private: diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp index 2c03da308a..55cd50fd9b 100644 --- a/src/mbgl/text/glyph.hpp +++ b/src/mbgl/text/glyph.hpp @@ -55,17 +55,21 @@ public: }; using Glyphs = std::map<GlyphID, optional<Immutable<Glyph>>>; -using GlyphMap = std::map<FontStack, Glyphs>; +using GlyphMap = std::map<FontStackHash, Glyphs>; class PositionedGlyph { public: - explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, bool vertical_) - : glyph(glyph_), x(x_), y(y_), vertical(vertical_) {} + 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_) + {} GlyphID glyph = 0; float x = 0; float y = 0; bool vertical = false; + + FontStackHash font = 0; + float scale = 0.0; }; enum class WritingModeType : uint8_t; diff --git a/src/mbgl/text/glyph_atlas.cpp b/src/mbgl/text/glyph_atlas.cpp index 1b98ea36bf..da65aea8a9 100644 --- a/src/mbgl/text/glyph_atlas.cpp +++ b/src/mbgl/text/glyph_atlas.cpp @@ -14,7 +14,7 @@ GlyphAtlas makeGlyphAtlas(const GlyphMap& glyphs) { mapbox::ShelfPack pack(0, 0, options); for (const auto& glyphMapEntry : glyphs) { - const FontStack& fontStack = glyphMapEntry.first; + FontStackHash fontStack = glyphMapEntry.first; GlyphPositionMap& positions = result.positions[fontStack]; for (const auto& entry : glyphMapEntry.second) { diff --git a/src/mbgl/text/glyph_atlas.hpp b/src/mbgl/text/glyph_atlas.hpp index bb9115e4b4..9dd063ef69 100644 --- a/src/mbgl/text/glyph_atlas.hpp +++ b/src/mbgl/text/glyph_atlas.hpp @@ -12,7 +12,7 @@ struct GlyphPosition { }; using GlyphPositionMap = std::map<GlyphID, GlyphPosition>; -using GlyphPositions = std::map<FontStack, GlyphPositionMap>; +using GlyphPositions = std::map<FontStackHash, GlyphPositionMap>; class GlyphAtlas { public: diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp index 8e7cfe5ba7..c4a7a2de66 100644 --- a/src/mbgl/text/glyph_manager.cpp +++ b/src/mbgl/text/glyph_manager.cpp @@ -130,7 +130,7 @@ void GlyphManager::notify(GlyphRequestor& requestor, const GlyphDependencies& gl const FontStack& fontStack = dependency.first; const GlyphIDs& glyphIDs = dependency.second; - Glyphs& glyphs = response[fontStack]; + Glyphs& glyphs = response[FontStackHasher()(fontStack)]; Entry& entry = entries[fontStack]; for (const auto& glyphID : glyphIDs) { diff --git a/src/mbgl/text/glyph_manager.hpp b/src/mbgl/text/glyph_manager.hpp index 642471bbf2..831d84719c 100644 --- a/src/mbgl/text/glyph_manager.hpp +++ b/src/mbgl/text/glyph_manager.hpp @@ -62,7 +62,7 @@ private: std::map<GlyphID, Immutable<Glyph>> glyphs; }; - std::unordered_map<FontStack, Entry, FontStackHash> entries; + std::unordered_map<FontStack, Entry, FontStackHasher> entries; void requestRange(GlyphRequest&, const FontStack&, const GlyphRange&); void processResponse(const Response&, const FontStack&, const GlyphRange&); diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp index ec4461ac6d..9d582f14d6 100644 --- a/src/mbgl/text/quads.cpp +++ b/src/mbgl/text/quads.cpp @@ -94,7 +94,7 @@ SymbolQuad getIconQuad(const PositionedIcon& shapedIcon, SymbolQuads getGlyphQuads(const Shaping& shapedText, const SymbolLayoutProperties::Evaluated& layout, const style::SymbolPlacementType placement, - const GlyphPositionMap& positions) { + const GlyphPositions& positions) { const float textRotate = layout.get<TextRotate>() * util::DEG2RAD; const float oneEm = 24.0; @@ -105,8 +105,12 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText, SymbolQuads quads; for (const PositionedGlyph &positionedGlyph: shapedText.positionedGlyphs) { - auto positionsIt = positions.find(positionedGlyph.glyph); - if (positionsIt == positions.end()) + auto fontPositions = positions.find(positionedGlyph.font); + if (fontPositions == positions.end()) + continue; + + auto positionsIt = fontPositions->second.find(positionedGlyph.glyph); + if (positionsIt == fontPositions->second.end()) continue; const GlyphPosition& glyph = positionsIt->second; @@ -116,7 +120,7 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText, const float glyphPadding = 1.0f; const float rectBuffer = 3.0f + glyphPadding; - const float halfAdvance = glyph.metrics.advance / 2.0; + const float halfAdvance = glyph.metrics.advance * positionedGlyph.scale / 2.0; const bool alongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && placement != SymbolPlacementType::Point; const Point<float> glyphOffset = alongLine ? @@ -128,10 +132,10 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText, Point<float>{ positionedGlyph.x + halfAdvance + textOffset[0], positionedGlyph.y + textOffset[1] }; - const float x1 = glyph.metrics.left - rectBuffer - halfAdvance + builtInOffset.x; - const float y1 = -glyph.metrics.top - rectBuffer + builtInOffset.y; - const float x2 = x1 + rect.w; - const float y2 = y1 + rect.h; + const float x1 = (glyph.metrics.left - rectBuffer) * positionedGlyph.scale - halfAdvance + builtInOffset.x; + const float y1 = (-glyph.metrics.top - rectBuffer) * positionedGlyph.scale + builtInOffset.y; + const float x2 = x1 + rect.w * positionedGlyph.scale; + const float y2 = y1 + rect.h * positionedGlyph.scale; Point<float> tl{x1, y1}; Point<float> tr{x2, y1}; diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp index 33d003c935..44a35a5014 100644 --- a/src/mbgl/text/quads.hpp +++ b/src/mbgl/text/quads.hpp @@ -48,6 +48,6 @@ SymbolQuad getIconQuad(const PositionedIcon& shapedIcon, SymbolQuads getGlyphQuads(const Shaping& shapedText, const style::SymbolLayoutProperties::Evaluated&, style::SymbolPlacementType placement, - const GlyphPositionMap& positions); + const GlyphPositions& positions); } // namespace mbgl diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index a8232836b6..d6cbb2a4f8 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -91,7 +91,7 @@ void align(Shaping& shaping, // justify left = 0, right = 1, center = .5 void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs, - const Glyphs& glyphs, + const GlyphMap& glyphMap, std::size_t start, std::size_t end, float justify) { @@ -100,9 +100,13 @@ void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs, } PositionedGlyph& glyph = positionedGlyphs[end]; - auto it = glyphs.find(glyph.glyph); - if (it != glyphs.end() && it->second) { - const uint32_t lastAdvance = (*it->second)->metrics.advance; + auto glyphs = glyphMap.find(glyph.font); + if (glyphs == glyphMap.end()) { + return; + } + auto it = glyphs->second.find(glyph.glyph); + if (it != glyphs->second.end() && it->second) { + const float lastAdvance = (*it->second)->metrics.advance * glyph.scale; const float lineIndent = float(glyph.x + lastAdvance) * justify; for (std::size_t j = start; j <= end; j++) { @@ -111,17 +115,25 @@ void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs, } } -float determineAverageLineWidth(const std::u16string& logicalInput, +float determineAverageLineWidth(const TaggedString& logicalInput, const float spacing, float maxWidth, - const Glyphs& glyphs) { + const GlyphMap& glyphMap) { float totalWidth = 0; - for (char16_t chr : logicalInput) { - auto it = glyphs.find(chr); - if (it != glyphs.end() && it->second) { - totalWidth += (*it->second)->metrics.advance + spacing; + for (std::size_t i = 0; i < logicalInput.length(); i++) { + const SectionOptions& section = logicalInput.getSection(i); + char16_t codePoint = logicalInput.getCharCodeAt(i); + auto glyphs = glyphMap.find(section.fontStackHash); + if (glyphs == glyphMap.end()) { + continue; + } + auto it = glyphs->second.find(codePoint); + if (it == glyphs->second.end() || !it->second) { + continue; } + + totalWidth += (*it->second)->metrics.advance * section.scale + spacing; } int32_t targetLineCount = ::fmax(1, std::ceil(totalWidth / maxWidth)); @@ -209,11 +221,11 @@ std::set<std::size_t> leastBadBreaks(const PotentialBreak& lastLineBreak) { // We determine line breaks based on shaped text in logical order. Working in visual order would be // more intuitive, but we can't do that because the visual order may be changed by line breaks! -std::set<std::size_t> determineLineBreaks(const std::u16string& logicalInput, +std::set<std::size_t> determineLineBreaks(const TaggedString& logicalInput, const float spacing, float maxWidth, const WritingModeType writingMode, - const Glyphs& glyphs) { + const GlyphMap& glyphMap) { if (!maxWidth || writingMode != WritingModeType::Horizontal) { return {}; } @@ -222,40 +234,45 @@ std::set<std::size_t> determineLineBreaks(const std::u16string& logicalInput, return {}; } - const float targetWidth = determineAverageLineWidth(logicalInput, spacing, maxWidth, glyphs); + const float targetWidth = determineAverageLineWidth(logicalInput, spacing, maxWidth, glyphMap); std::list<PotentialBreak> potentialBreaks; float currentX = 0; - for (std::size_t i = 0; i < logicalInput.size(); i++) { - const char16_t codePoint = logicalInput[i]; - auto it = glyphs.find(codePoint); - if (it != glyphs.end() && it->second && !boost::algorithm::is_any_of(u" \t\n\v\f\r")(codePoint)) { - currentX += (*it->second)->metrics.advance + spacing; + for (std::size_t i = 0; i < logicalInput.length(); i++) { + const SectionOptions& section = logicalInput.getSection(i); + char16_t codePoint = logicalInput.getCharCodeAt(i); + auto glyphs = glyphMap.find(section.fontStackHash); + if (glyphs == glyphMap.end()) { + continue; + } + auto it = glyphs->second.find(codePoint); + if (it != glyphs->second.end() && it->second && !boost::algorithm::is_any_of(u" \t\n\v\f\r")(codePoint)) { + currentX += (*it->second)->metrics.advance * section.scale + spacing; } // Ideographic characters, spaces, and word-breaking punctuation that often appear without // surrounding spaces. - if ((i < logicalInput.size() - 1) && + if ((i < logicalInput.length() - 1) && (util::i18n::allowsWordBreaking(codePoint) || util::i18n::allowsIdeographicBreaking(codePoint))) { potentialBreaks.push_back(evaluateBreak(i+1, currentX, targetWidth, potentialBreaks, - calculatePenalty(codePoint, logicalInput[i+1]), + calculatePenalty(codePoint, logicalInput.getCharCodeAt(i+1)), false)); } } - return leastBadBreaks(evaluateBreak(logicalInput.size(), currentX, targetWidth, potentialBreaks, 0, true)); + return leastBadBreaks(evaluateBreak(logicalInput.length(), currentX, targetWidth, potentialBreaks, 0, true)); } void shapeLines(Shaping& shaping, - const std::vector<std::u16string>& lines, - const float spacing, - const float lineHeight, - const style::SymbolAnchorType textAnchor, - const style::TextJustifyType textJustify, - const float verticalHeight, - const WritingModeType writingMode, - const Glyphs& glyphs) { + std::vector<TaggedString>& lines, + const float spacing, + const float lineHeight, + const style::SymbolAnchorType textAnchor, + const style::TextJustifyType textJustify, + const float verticalHeight, + const WritingModeType writingMode, + const GlyphMap& glyphMap) { // the y offset *should* be part of the font metadata const int32_t yOffset = -17; @@ -265,13 +282,16 @@ void shapeLines(Shaping& shaping, float maxLineLength = 0; + const float justify = textJustify == style::TextJustifyType::Right ? 1 : textJustify == style::TextJustifyType::Left ? 0 : 0.5; - for (std::u16string line : lines) { + for (TaggedString& line : lines) { // Collapse whitespace so it doesn't throw off justification - boost::algorithm::trim_if(line, boost::algorithm::is_any_of(u" \t\n\v\f\r")); + line.trim(); + + const double lineMaxScale = line.getMaxScale(); if (line.empty()) { y += lineHeight; // Still need a line feed after empty line @@ -279,20 +299,31 @@ void shapeLines(Shaping& shaping, } std::size_t lineStartIndex = shaping.positionedGlyphs.size(); - for (char16_t chr : line) { - auto it = glyphs.find(chr); - if (it == glyphs.end() || !it->second) { + for (std::size_t i = 0; i < line.length(); i++) { + const SectionOptions& section = line.getSection(i); + char16_t codePoint = line.getCharCodeAt(i); + auto glyphs = glyphMap.find(section.fontStackHash); + if (glyphs == glyphMap.end()) { + continue; + } + auto it = glyphs->second.find(codePoint); + if (it == glyphs->second.end() || !it->second) { continue; } + // We don't know the baseline, but since we're laying out + // at 24 points, we can calculate how much it will move when + // we scale up or down. + const double baselineOffset = (lineMaxScale - section.scale) * 24; + const Glyph& glyph = **it->second; - if (writingMode == WritingModeType::Horizontal || !util::i18n::hasUprightVerticalOrientation(chr)) { - shaping.positionedGlyphs.emplace_back(chr, x, y, false); - x += glyph.metrics.advance + spacing; + if (writingMode == WritingModeType::Horizontal || !util::i18n::hasUprightVerticalOrientation(codePoint)) { + shaping.positionedGlyphs.emplace_back(codePoint, x, y + baselineOffset, false, section.fontStackHash, section.scale); + x += glyph.metrics.advance * section.scale + spacing; } else { - shaping.positionedGlyphs.emplace_back(chr, x, 0, true); - x += verticalHeight + spacing; + shaping.positionedGlyphs.emplace_back(codePoint, x, baselineOffset, true, section.fontStackHash, section.scale); + x += verticalHeight * section.scale + spacing; } } @@ -301,19 +332,19 @@ void shapeLines(Shaping& shaping, float lineLength = x - spacing; // Don't count trailing spacing maxLineLength = util::max(lineLength, maxLineLength); - justifyLine(shaping.positionedGlyphs, glyphs, lineStartIndex, + justifyLine(shaping.positionedGlyphs, glyphMap, lineStartIndex, shaping.positionedGlyphs.size() - 1, justify); } x = 0; - y += lineHeight; + y += lineHeight * lineMaxScale; } auto anchorAlign = getAnchorAlignment(textAnchor); align(shaping, justify, anchorAlign.horizontalAlign, anchorAlign.verticalAlign, maxLineLength, lineHeight, lines.size()); - const float height = lines.size() * lineHeight; + const float height = y - yOffset; // Calculate the bounding box shaping.top += -anchorAlign.verticalAlign * height; @@ -322,7 +353,7 @@ void shapeLines(Shaping& shaping, shaping.right = shaping.left + maxLineLength; } -const Shaping getShaping(const std::u16string& logicalInput, +const Shaping getShaping(const TaggedString& formattedString, const float maxWidth, const float lineHeight, const style::SymbolAnchorType textAnchor, @@ -332,12 +363,23 @@ const Shaping getShaping(const std::u16string& logicalInput, const float verticalHeight, const WritingModeType writingMode, BiDi& bidi, - const Glyphs& glyphs) { + const GlyphMap& glyphs) { Shaping shaping(translate.x, translate.y, writingMode); - std::vector<std::u16string> reorderedLines = - bidi.processText(logicalInput, - determineLineBreaks(logicalInput, spacing, maxWidth, writingMode, glyphs)); + std::vector<TaggedString> reorderedLines; + if (formattedString.sectionCount() == 1) { + auto untaggedLines = bidi.processText(formattedString.rawText(), + determineLineBreaks(formattedString, spacing, maxWidth, writingMode, glyphs)); + for (const auto& line : untaggedLines) { + reorderedLines.emplace_back(line, formattedString.sectionAt(0)); + } + } else { + auto processedLines = bidi.processStyledText(formattedString.getStyledText(), + determineLineBreaks(formattedString, spacing, maxWidth, writingMode, glyphs)); + for (const auto& line : processedLines) { + reorderedLines.emplace_back(line, formattedString.getSections()); + } + } shapeLines(shaping, reorderedLines, spacing, lineHeight, textAnchor, textJustify, verticalHeight, writingMode, glyphs); diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp index 0a961849e5..50ac893098 100644 --- a/src/mbgl/text/shaping.hpp +++ b/src/mbgl/text/shaping.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/text/glyph.hpp> +#include <mbgl/text/tagged_string.hpp> #include <mbgl/renderer/image_atlas.hpp> #include <mbgl/style/types.hpp> @@ -45,7 +46,7 @@ public: float angle() const { return _angle; } }; -const Shaping getShaping(const std::u16string& string, +const Shaping getShaping(const TaggedString& string, float maxWidth, float lineHeight, style::SymbolAnchorType textAnchor, @@ -55,6 +56,6 @@ const Shaping getShaping(const std::u16string& string, float verticalHeight, const WritingModeType, BiDi& bidi, - const Glyphs& glyphs); + const GlyphMap& glyphs); } // namespace mbgl diff --git a/src/mbgl/text/tagged_string.cpp b/src/mbgl/text/tagged_string.cpp new file mode 100644 index 0000000000..e199a7c962 --- /dev/null +++ b/src/mbgl/text/tagged_string.cpp @@ -0,0 +1,42 @@ +#include <mbgl/text/tagged_string.hpp> +#include <mbgl/util/i18n.hpp> + +#include <boost/algorithm/string.hpp> + +namespace mbgl { + +void TaggedString::addSection(const std::u16string& sectionText, double scale, FontStackHash fontStack) { + styledText.first += sectionText; + sections.emplace_back(scale, fontStack); + styledText.second.resize(styledText.first.size(), sections.size() - 1); +} + +void TaggedString::trim() { + auto whiteSpace = boost::algorithm::is_any_of(u" \t\n\v\f\r"); + std::size_t beginningWhitespace = styledText.first.find_first_not_of(u" \t\n\v\f\r"); + if (beginningWhitespace == std::u16string::npos) { + // Entirely whitespace + styledText.first.clear(); + styledText.second.clear(); + } else { + 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); + } +} + +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); + } + return maxScale; +} + +void TaggedString::verticalizePunctuation() { + // Relies on verticalization changing characters in place so that style indices don't need updating + styledText.first = util::i18n::verticalizePunctuation(styledText.first); +} + +} // namespace mbgl diff --git a/src/mbgl/text/tagged_string.hpp b/src/mbgl/text/tagged_string.hpp new file mode 100644 index 0000000000..476c2225f0 --- /dev/null +++ b/src/mbgl/text/tagged_string.hpp @@ -0,0 +1,97 @@ +#pragma once + +#include <mbgl/text/glyph.hpp> +#include <mbgl/text/bidi.hpp> + +namespace mbgl { + +struct SectionOptions { + SectionOptions(double scale_, FontStackHash fontStackHash_) + : scale(scale_), fontStackHash(fontStackHash_) + {} + + double scale; + FontStackHash fontStackHash; +}; + +/** + * A TaggedString is the shaping-code counterpart of the Formatted type + * Whereas Formatted matches the logical structure of a 'format' expression, + * a TaggedString represents the same data at a per-character level so that + * character-rearranging operations (e.g. BiDi) preserve formatting. + * Text is represented as: + * - A string of characters + * - A matching array of indices, pointing to: + * - An array of SectionsOptions, representing the evaluated formatting + * options of the original sections. + * + * Once the guts of a TaggedString have been re-arranged by BiDi, you can + * iterate over the contents in order, using getCharCodeAt and getSection + * to get the formatting options for each character in turn. + */ +struct TaggedString { + TaggedString() = default; + + TaggedString(std::u16string text_, SectionOptions options) + : styledText(std::move(text_), + std::vector<uint8_t>(text_.size(), 0)) { + sections.push_back(std::move(options)); + } + + TaggedString(StyledText styledText_, std::vector<SectionOptions> sections_) + : styledText(std::move(styledText_)) + , sections(std::move(sections_)) { + } + + std::size_t length() const { + return styledText.first.length(); + } + + std::size_t sectionCount() const { + return sections.size(); + } + + bool empty() const { + return styledText.first.empty(); + } + + const SectionOptions& getSection(std::size_t index) const { + return sections.at(styledText.second.at(index)); + } + + char16_t getCharCodeAt(std::size_t index) const { + return styledText.first[index]; + } + + const std::u16string& rawText() const { + return styledText.first; + } + + const StyledText& getStyledText() const { + return styledText; + } + + void addSection(const std::u16string& text, double scale, FontStackHash fontStack); + const SectionOptions& sectionAt(std::size_t index) const { + return sections.at(index); + } + + const std::vector<SectionOptions>& getSections() const { + return sections; + } + + uint8_t getSectionIndex(std::size_t characterIndex) const { + return styledText.second.at(characterIndex); + } + + double getMaxScale() const; + void trim(); + + void verticalizePunctuation(); + +private: + StyledText styledText; + std::vector<SectionOptions> sections; +}; + +} // namespace mbgl |