diff options
author | zmiao <miao.zhao@mapbox.com> | 2019-09-26 22:28:04 +0300 |
---|---|---|
committer | zmiao <miao.zhao@mapbox.com> | 2019-10-06 19:48:08 +0300 |
commit | 358c79b31dabac987376406ca29dc42cda160d5e (patch) | |
tree | 1460f0f5bbd36e485b16986971fb76b30e1c231d | |
parent | 97c6ed20ee00df22dd737498107d656ec3ef7a67 (diff) | |
download | qtlocation-mapboxgl-358c79b31dabac987376406ca29dc42cda160d5e.tar.gz |
[core] Add hasBaseline flag for fontstack, change ascender/descendr type to int32
-rw-r--r-- | src/mbgl/text/glyph.hpp | 22 | ||||
-rw-r--r-- | src/mbgl/text/glyph_atlas.cpp | 2 | ||||
-rw-r--r-- | src/mbgl/text/glyph_manager.cpp | 7 | ||||
-rw-r--r-- | src/mbgl/text/glyph_manager.hpp | 1 | ||||
-rw-r--r-- | src/mbgl/text/glyph_pbf.cpp | 33 | ||||
-rw-r--r-- | src/mbgl/text/glyph_pbf.hpp | 3 | ||||
-rw-r--r-- | src/mbgl/text/shaping.cpp | 49 | ||||
-rw-r--r-- | src/mbgl/tile/geometry_tile_worker.cpp | 9 | ||||
-rw-r--r-- | test/text/glyph_manager.test.cpp | 20 | ||||
-rw-r--r-- | test/text/glyph_pbf.test.cpp | 8 | ||||
-rw-r--r-- | test/text/shaping.test.cpp | 81 |
11 files changed, 154 insertions, 81 deletions
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp index c56247f205..73e9ec9715 100644 --- a/src/mbgl/text/glyph.hpp +++ b/src/mbgl/text/glyph.hpp @@ -25,21 +25,18 @@ using GlyphIDs = std::set<GlyphID>; GlyphRange getGlyphRange(GlyphID glyph); struct GlyphMetrics { - uint32_t width = 0; - uint32_t height = 0; + uint32_t width = 0U; + uint32_t height = 0U; int32_t left = 0; int32_t top = 0; - uint32_t advance = 0; - double ascender = 0.0; - double descender = 0.0; + uint32_t advance = 0U; + int32_t ascender = 0; + int32_t descender = 0; }; inline bool operator==(const GlyphMetrics& lhs, const GlyphMetrics& rhs) { - return lhs.width == rhs.width && - lhs.height == rhs.height && - lhs.left == rhs.left && - lhs.top == rhs.top && - lhs.advance == rhs.advance; + return lhs.width == rhs.width && lhs.height == rhs.height && lhs.left == rhs.left && lhs.top == rhs.top && + lhs.advance == rhs.advance && lhs.ascender == rhs.ascender && lhs.descender == rhs.descender; } class Glyph { @@ -57,7 +54,10 @@ public: GlyphMetrics metrics; }; -using Glyphs = std::map<GlyphID, optional<Immutable<Glyph>>>; +struct Glyphs { + std::map<GlyphID, optional<Immutable<Glyph>>> glyphs; + bool hasBaseline; +}; using GlyphMap = std::map<FontStackHash, Glyphs>; class PositionedGlyph { diff --git a/src/mbgl/text/glyph_atlas.cpp b/src/mbgl/text/glyph_atlas.cpp index da65aea8a9..601582cc49 100644 --- a/src/mbgl/text/glyph_atlas.cpp +++ b/src/mbgl/text/glyph_atlas.cpp @@ -17,7 +17,7 @@ GlyphAtlas makeGlyphAtlas(const GlyphMap& glyphs) { FontStackHash fontStack = glyphMapEntry.first; GlyphPositionMap& positions = result.positions[fontStack]; - for (const auto& entry : glyphMapEntry.second) { + for (const auto& entry : glyphMapEntry.second.glyphs) { if (entry.second && (*entry.second)->bitmap.valid()) { const Glyph& glyph = **entry.second; diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp index 35ea1031d5..8b37b652ab 100644 --- a/src/mbgl/text/glyph_manager.cpp +++ b/src/mbgl/text/glyph_manager.cpp @@ -91,7 +91,7 @@ void GlyphManager::processResponse(const Response& res, const FontStack& fontSta std::vector<Glyph> glyphs; try { - glyphs = parseGlyphPBF(range, *res.data); + std::tie(glyphs, entry.hasBaseline) = parseGlyphPBF(range, *res.data); } catch (...) { observer->onGlyphsError(fontStack, range, std::current_exception()); return; @@ -134,13 +134,14 @@ void GlyphManager::notify(GlyphRequestor& requestor, const GlyphDependencies& gl Glyphs& glyphs = response[FontStackHasher()(fontStack)]; Entry& entry = entries[fontStack]; + glyphs.hasBaseline = entry.hasBaseline; for (const auto& glyphID : glyphIDs) { auto it = entry.glyphs.find(glyphID); if (it != entry.glyphs.end()) { - glyphs.emplace(*it); + glyphs.glyphs.emplace(*it); } else { - glyphs.emplace(glyphID, std::experimental::nullopt); + glyphs.glyphs.emplace(glyphID, std::experimental::nullopt); } } } diff --git a/src/mbgl/text/glyph_manager.hpp b/src/mbgl/text/glyph_manager.hpp index 8603a320d2..1955fb752c 100644 --- a/src/mbgl/text/glyph_manager.hpp +++ b/src/mbgl/text/glyph_manager.hpp @@ -61,6 +61,7 @@ private: struct Entry { std::map<GlyphRange, GlyphRequest> ranges; std::map<GlyphID, Immutable<Glyph>> glyphs; + bool hasBaseline; }; std::unordered_map<FontStack, Entry, FontStackHasher> entries; diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp index b4abfdeb39..4658bc69da 100644 --- a/src/mbgl/text/glyph_pbf.cpp +++ b/src/mbgl/text/glyph_pbf.cpp @@ -4,14 +4,14 @@ namespace mbgl { -std::vector<Glyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string& data) { - std::vector<Glyph> result; - result.reserve(256); +std::pair<std::vector<Glyph>, bool> parseGlyphPBF(const GlyphRange& glyphRange, const std::string& data) { + std::vector<Glyph> glyphs; + glyphs.reserve(256); protozero::pbf_reader glyphs_pbf(data); - + bool hasBaseline{true}; while (glyphs_pbf.next(1)) { - auto readGlyphMetrics = [glyphRange, &result](protozero::pbf_reader& fontstack_pbf) { + auto readGlyphMetrics = [glyphRange, &glyphs](protozero::pbf_reader& fontstack_pbf) { auto glyph_pbf = fontstack_pbf.get_message(); Glyph glyph; protozero::data_view glyphData; @@ -78,10 +78,10 @@ std::vector<Glyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string glyph.bitmap = AlphaImage(size, reinterpret_cast<const uint8_t*>(glyphData.data()), glyphData.size()); } - result.push_back(std::move(glyph)); + glyphs.push_back(std::move(glyph)); }; - double ascender{0.0}, descender{0.0}; + int32_t ascender{0}, descender{0}; uint16_t count{0}; auto fontstack_pbf = glyphs_pbf.get_message(); while (fontstack_pbf.next()) { @@ -92,11 +92,11 @@ std::vector<Glyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string break; } case 4: { - ascender = fontstack_pbf.get_double(); + ascender = fontstack_pbf.get_sint32(); break; } case 5: { - descender = fontstack_pbf.get_double(); + descender = fontstack_pbf.get_sint32(); break; } default: { @@ -105,16 +105,19 @@ std::vector<Glyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string } } } - if (ascender != 0.0 || descender != 0.0) { - assert(count <= result.size()); - for (uint16_t i = result.size() - count; i <= result.size() - 1; ++i) { - result[i].metrics.ascender = ascender; - result[i].metrics.descender = descender; + if (hasBaseline && (ascender != 0.0 || descender != 0.0)) { + assert(count <= glyphs.size()); + const auto lastIndex = glyphs.size() - 1; + for (uint16_t i = glyphs.size() - count; i <= lastIndex; ++i) { + glyphs[i].metrics.ascender = ascender; + glyphs[i].metrics.descender = descender; } + } else { + hasBaseline = false; } } - return result; + return std::make_pair(std::move(glyphs), hasBaseline); } } // namespace mbgl diff --git a/src/mbgl/text/glyph_pbf.hpp b/src/mbgl/text/glyph_pbf.hpp index 28a28b4114..6adbb9137f 100644 --- a/src/mbgl/text/glyph_pbf.hpp +++ b/src/mbgl/text/glyph_pbf.hpp @@ -4,10 +4,11 @@ #include <mbgl/text/glyph_range.hpp> #include <string> +#include <utility> #include <vector> namespace mbgl { -std::vector<Glyph> parseGlyphPBF(const GlyphRange&, const std::string& data); +std::pair<std::vector<Glyph>, bool> parseGlyphPBF(const GlyphRange&, const std::string& data); } // namespace mbgl diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index efcf17873e..ff0f7dee15 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -144,8 +144,8 @@ void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs, if (glyphs == glyphMap.end()) { return; } - auto it = glyphs->second.find(glyph.glyph); - if (it != glyphs->second.end() && it->second) { + auto it = glyphs->second.glyphs.find(glyph.glyph); + if (it != glyphs->second.glyphs.end() && it->second) { const float lastAdvance = (*it->second)->metrics.advance * glyph.scale; const float lineIndent = float(glyph.x + lastAdvance) * justify; @@ -168,11 +168,11 @@ float determineAverageLineWidth(const TaggedString& logicalInput, if (glyphs == glyphMap.end()) { continue; } - auto it = glyphs->second.find(codePoint); - if (it == glyphs->second.end() || !it->second) { + auto it = glyphs->second.glyphs.find(codePoint); + if (it == glyphs->second.glyphs.end() || !it->second) { continue; } - + totalWidth += (*it->second)->metrics.advance * section.scale + spacing; } @@ -294,11 +294,11 @@ std::set<std::size_t> determineLineBreaks(const TaggedString& logicalInput, if (glyphs == glyphMap.end()) { continue; } - auto it = glyphs->second.find(codePoint); - if (it != glyphs->second.end() && it->second && !util::i18n::isWhitespace(codePoint)) { + auto it = glyphs->second.glyphs.find(codePoint); + if (it != glyphs->second.glyphs.end() && it->second && !util::i18n::isWhitespace(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.length() - 1) { @@ -325,28 +325,19 @@ void shapeLines(Shaping& shaping, const WritingModeType writingMode, const GlyphMap& glyphMap, bool allowVerticalPlacement) { - float x = 0; - float y = 0; - float maxLineLength = 0; + float x = 0.0f; + float y = 0.0f; + float maxLineLength = 0.0f; bool hasBaseline{false}; - for (std::size_t i = 0; i < lines.size(); ++i) { - TaggedString& line = lines[i]; - line.trim(); - for (std::size_t j = 0; j < line.length(); ++j) { - const std::size_t sectionIndex = line.getSectionIndex(j); - const SectionOptions& section = line.sectionAt(sectionIndex); - char16_t codePoint = line.getCharCodeAt(i); + for (const auto& line : lines) { + const auto& sections = line.getSections(); + for (const auto& section : sections) { 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; - } - const Glyph& glyph = **it->second; - hasBaseline = glyph.metrics.ascender != 0 && glyph.metrics.descender != 0; + hasBaseline = glyphs->second.hasBaseline; if (!hasBaseline) break; } if (!hasBaseline) break; @@ -366,7 +357,7 @@ void shapeLines(Shaping& shaping, y += lineHeight; // Still need a line feed after empty line continue; } - + std::size_t lineStartIndex = shaping.positionedGlyphs.size(); for (std::size_t i = 0; i < line.length(); i++) { const std::size_t sectionIndex = line.getSectionIndex(i); @@ -376,8 +367,8 @@ void shapeLines(Shaping& shaping, if (glyphs == glyphMap.end()) { continue; } - auto it = glyphs->second.find(codePoint); - if (it == glyphs->second.end() || !it->second) { + auto it = glyphs->second.glyphs.find(codePoint); + if (it == glyphs->second.glyphs.end() || !it->second) { continue; } @@ -386,7 +377,7 @@ void shapeLines(Shaping& shaping, // In order to make different fonts aligned, they must share a general baseline that starts from the midline // of each font face. Baseline offset is the vertical distance from font face's baseline to its top most // position, which is the half size of the sum (ascender + descender). Since glyph's position is counted - // from the top left corner, the negative shift is needed. So different fonts shares the same baseline but + // from the top left corner, the negative shift is needed. So different fonts share the same baseline but // with different offset shift. If font's baseline is not applicable, fall back to use a default baseline // offset, see shaping.yOffset. Since we're laying out at 24 points, we need also calculate how much it will // move when we scale up or down. @@ -410,7 +401,7 @@ void shapeLines(Shaping& shaping, x += util::ONE_EM * section.scale + spacing; } } - + // Only justify if we placed at least one glyph if (shaping.positionedGlyphs.size() != lineStartIndex) { float lineLength = x - spacing; // Don't count trailing spacing diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 7815e62b75..5ac168de9d 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -259,18 +259,20 @@ void GeometryTileWorker::onGlyphsAvailable(GlyphMap newGlyphMap) { Glyphs& newGlyphs = newFontGlyphs.second; Glyphs& glyphs = glyphMap[fontStack]; + glyphs.hasBaseline = + glyphs.glyphs.empty() ? newGlyphs.hasBaseline : glyphs.hasBaseline && newGlyphs.hasBaseline; for (auto& pendingGlyphDependency : pendingGlyphDependencies) { // Linear lookup here to handle reverse of FontStackHash -> FontStack, // since dependencies need the full font stack name to make a request // There should not be many fontstacks to look through if (FontStackHasher()(pendingGlyphDependency.first) == fontStack) { GlyphIDs& pendingGlyphIDs = pendingGlyphDependency.second; - for (auto& newGlyph : newGlyphs) { + for (auto& newGlyph : newGlyphs.glyphs) { const GlyphID& glyphID = newGlyph.first; optional<Immutable<Glyph>>& glyph = newGlyph.second; if (pendingGlyphIDs.erase(glyphID)) { - glyphs.emplace(glyphID, std::move(glyph)); + glyphs.glyphs.emplace(glyphID, std::move(glyph)); } } } @@ -294,7 +296,8 @@ void GeometryTileWorker::requestNewGlyphs(const GlyphDependencies& glyphDependen for (auto& fontDependencies : glyphDependencies) { auto fontGlyphs = glyphMap.find(FontStackHasher()(fontDependencies.first)); for (auto glyphID : fontDependencies.second) { - if (fontGlyphs == glyphMap.end() || fontGlyphs->second.find(glyphID) == fontGlyphs->second.end()) { + if (fontGlyphs == glyphMap.end() || + fontGlyphs->second.glyphs.find(glyphID) == fontGlyphs->second.glyphs.end()) { pendingGlyphDependencies[fontDependencies.first].insert(glyphID); } } diff --git a/test/text/glyph_manager.test.cpp b/test/text/glyph_manager.test.cpp index 1e9aef38ed..cdb98702cf 100644 --- a/test/text/glyph_manager.test.cpp +++ b/test/text/glyph_manager.test.cpp @@ -32,6 +32,8 @@ public: stub.metrics.left = 0; stub.metrics.top = -8; stub.metrics.advance = 24; + stub.metrics.ascender = 0; + stub.metrics.descender = 0; stub.bitmap = AlphaImage(Size(30, 30), stubBitmap, stubBitmapLength); @@ -106,8 +108,8 @@ TEST(GlyphManager, LoadingSuccess) { ASSERT_EQ(range, GlyphRange(0, 255)); }; - test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) { - const auto& testPositions = glyphs.at(FontStackHasher()({{"Test Stack"}})); + test.requestor.glyphsAvailable = [&](GlyphMap glyphs) { + const auto& testPositions = glyphs.at(FontStackHasher()({{"Test Stack"}})).glyphs; ASSERT_EQ(testPositions.size(), 3u); ASSERT_EQ(testPositions.count(u'a'), 1u); @@ -223,8 +225,8 @@ TEST(GlyphManager, LoadLocalCJKGlyph) { test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) { EXPECT_EQ(glyphResponses, 0); // Local generation should prevent requesting any glyphs - - const auto& testPositions = glyphs.at(FontStackHasher()({{"Test Stack"}})); + + const auto& testPositions = glyphs.at(FontStackHasher()({{"Test Stack"}})).glyphs; ASSERT_EQ(testPositions.size(), 1u); ASSERT_EQ(testPositions.count(u'中'), 1u); @@ -264,9 +266,9 @@ TEST(GlyphManager, LoadLocalCJKGlyphAfterLoadingRangeFromURL) { return response; }; - - test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) { - const auto& testPositions = glyphs.at(FontStackHasher()({{"Test Stack"}})); + + test.requestor.glyphsAvailable = [&](GlyphMap glyphs) { + const auto& testPositions = glyphs.at(FontStackHasher()({{"Test Stack"}})).glyphs; if (firstGlyphResponse == true) { firstGlyphResponse = false; @@ -325,8 +327,8 @@ TEST(GlyphManager, LoadingInvalid) { ASSERT_EQ(range, GlyphRange(0, 255)); }; - test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) { - const auto& testPositions = glyphs.at(FontStackHasher()({{"Test Stack"}})); + test.requestor.glyphsAvailable = [&](GlyphMap glyphs) { + const auto& testPositions = glyphs.at(FontStackHasher()({{"Test Stack"}})).glyphs; ASSERT_EQ(testPositions.size(), 2u); ASSERT_FALSE(bool(testPositions.at(u'A'))); diff --git a/test/text/glyph_pbf.test.cpp b/test/text/glyph_pbf.test.cpp index c222ec1dd9..1f87403982 100644 --- a/test/text/glyph_pbf.test.cpp +++ b/test/text/glyph_pbf.test.cpp @@ -8,9 +8,9 @@ using namespace mbgl; TEST(GlyphPBF, Parsing) { // The fake glyphs contain a number of invalid glyphs, which should be skipped by the parser. auto sdfs = parseGlyphPBF(GlyphRange { 0, 255 }, util::read_file("test/fixtures/resources/fake_glyphs-0-255.pbf")); - EXPECT_TRUE(sdfs.size() == 1); - - auto& sdf = sdfs[0]; + ASSERT_EQ(1, sdfs.first.size()); + EXPECT_FALSE(sdfs.second); + const auto& sdf = sdfs.first[0]; EXPECT_EQ(69u, sdf.id); AlphaImage expected({7, 7}); expected.fill('x'); @@ -20,4 +20,6 @@ TEST(GlyphPBF, Parsing) { EXPECT_EQ(20, sdf.metrics.left); EXPECT_EQ(2, sdf.metrics.top); EXPECT_EQ(8u, sdf.metrics.advance); + EXPECT_EQ(0, sdf.metrics.ascender); + EXPECT_EQ(0, sdf.metrics.descender); } diff --git a/test/text/shaping.test.cpp b/test/text/shaping.test.cpp index b22cd7da36..e034ce1503 100644 --- a/test/text/shaping.test.cpp +++ b/test/text/shaping.test.cpp @@ -17,22 +17,25 @@ TEST(Shaping, ZWSP) { glyph.metrics.left = 2; glyph.metrics.top = -8; glyph.metrics.advance = 21; + glyph.metrics.ascender = 0; + glyph.metrics.descender = 0; BiDi bidi; auto immutableGlyph = Immutable<Glyph>(makeMutable<Glyph>(std::move(glyph))); const std::vector<std::string> fontStack{{"font-stack"}}; const SectionOptions sectionOptions(1.0f, fontStack); - GlyphMap glyphs = { - { FontStackHasher()(fontStack), {{u'中', std::move(immutableGlyph)}} } - }; + Glyphs glyphData; + glyphData.glyphs.emplace(u'中', std::move(immutableGlyph)); + glyphData.hasBaseline = false; + GlyphMap glyphs = {{FontStackHasher()(fontStack), std::move(glyphData)}}; - const auto testGetShaping = [&] (const TaggedString& string, unsigned maxWidthInChars) { + const auto testGetShaping = [&](const TaggedString& string, unsigned maxWidthInChars) { return getShaping(string, maxWidthInChars * ONE_EM, - ONE_EM, // lineHeight + ONE_EM, // lineHeight style::SymbolAnchorType::Center, style::TextJustifyType::Center, - 0, // spacing + 0, // spacing {{0.0f, 0.0f}}, // translate WritingModeType::Horizontal, bidi, @@ -80,6 +83,11 @@ TEST(Shaping, ZWSP) { ASSERT_EQ(shaping.left, -21); ASSERT_EQ(shaping.right, 21); ASSERT_EQ(shaping.writingMode, WritingModeType::Horizontal); + ASSERT_EQ(2, shaping.positionedGlyphs.size()); + EXPECT_FLOAT_EQ(-21, shaping.positionedGlyphs[0].x); + EXPECT_FLOAT_EQ(-17, shaping.positionedGlyphs[0].y); + EXPECT_FLOAT_EQ(0, shaping.positionedGlyphs[1].x); + EXPECT_FLOAT_EQ(-17, shaping.positionedGlyphs[1].y); } // 5 'new' lines. @@ -94,3 +102,64 @@ TEST(Shaping, ZWSP) { ASSERT_EQ(shaping.writingMode, WritingModeType::Horizontal); } } + +TEST(Shaping, FontWithBaseline) { + Glyph glyph1; + glyph1.id = u'阳'; + glyph1.metrics.width = 18; + glyph1.metrics.height = 19; + glyph1.metrics.left = 2; + glyph1.metrics.top = -8; + glyph1.metrics.advance = 21; + glyph1.metrics.ascender = 26; + glyph1.metrics.descender = -6; + + Glyph glyph2; + glyph2.id = u'光'; + glyph2.metrics.width = 18; + glyph2.metrics.height = 18; + glyph2.metrics.left = 2; + glyph2.metrics.top = -8; + glyph2.metrics.advance = 21; + glyph2.metrics.ascender = 25; + glyph2.metrics.descender = -5; + + BiDi bidi; + const std::vector<std::string> fontStack{{"font-stack"}}; + const SectionOptions sectionOptions(1.0f, fontStack); + Glyphs glyphData; + glyphData.glyphs.emplace(u'阳', Immutable<Glyph>(makeMutable<Glyph>(std::move(glyph1)))); + glyphData.glyphs.emplace(u'光', Immutable<Glyph>(makeMutable<Glyph>(std::move(glyph2)))); + glyphData.hasBaseline = true; + GlyphMap glyphs = {{FontStackHasher()(fontStack), std::move(glyphData)}}; + + const auto testGetShaping = [&](const TaggedString& string, unsigned maxWidthInChars) { + return getShaping(string, + maxWidthInChars * ONE_EM, + ONE_EM, // lineHeight + style::SymbolAnchorType::Center, + style::TextJustifyType::Center, + 0, // spacing + {{0.0f, 0.0f}}, // translate + WritingModeType::Horizontal, + bidi, + glyphs, + /*allowVerticalPlacement*/ false); + }; + + { + TaggedString string(u"阳光\u200b", sectionOptions); + auto shaping = testGetShaping(string, 5); + ASSERT_EQ(shaping.lineCount, 1); + ASSERT_EQ(shaping.top, -12); + ASSERT_EQ(shaping.bottom, 12); + ASSERT_EQ(shaping.left, -21); + ASSERT_EQ(shaping.right, 21); + ASSERT_EQ(shaping.writingMode, WritingModeType::Horizontal); + ASSERT_EQ(2, shaping.positionedGlyphs.size()); + EXPECT_FLOAT_EQ(-21, shaping.positionedGlyphs[0].x); + EXPECT_FLOAT_EQ(-16, shaping.positionedGlyphs[0].y); + EXPECT_FLOAT_EQ(0, shaping.positionedGlyphs[1].x); + EXPECT_FLOAT_EQ(-15, shaping.positionedGlyphs[1].y); + } +} |