summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzmiao <miao.zhao@mapbox.com>2019-09-26 22:28:04 +0300
committerzmiao <miao.zhao@mapbox.com>2019-10-06 19:48:08 +0300
commit358c79b31dabac987376406ca29dc42cda160d5e (patch)
tree1460f0f5bbd36e485b16986971fb76b30e1c231d
parent97c6ed20ee00df22dd737498107d656ec3ef7a67 (diff)
downloadqtlocation-mapboxgl-358c79b31dabac987376406ca29dc42cda160d5e.tar.gz
[core] Add hasBaseline flag for fontstack, change ascender/descendr type to int32
-rw-r--r--src/mbgl/text/glyph.hpp22
-rw-r--r--src/mbgl/text/glyph_atlas.cpp2
-rw-r--r--src/mbgl/text/glyph_manager.cpp7
-rw-r--r--src/mbgl/text/glyph_manager.hpp1
-rw-r--r--src/mbgl/text/glyph_pbf.cpp33
-rw-r--r--src/mbgl/text/glyph_pbf.hpp3
-rw-r--r--src/mbgl/text/shaping.cpp49
-rw-r--r--src/mbgl/tile/geometry_tile_worker.cpp9
-rw-r--r--test/text/glyph_manager.test.cpp20
-rw-r--r--test/text/glyph_pbf.test.cpp8
-rw-r--r--test/text/shaping.test.cpp81
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);
+ }
+}