Add HarfBuzz Text Shaping and Font Fallback Support (#3611)
Co-authored-by: alan wei chen <alanchenboy@gmail.com>
This commit is contained in:
@@ -1612,6 +1612,82 @@ TEST(Map, ObserveShaderRegistration) {
|
||||
EXPECT_EQ(observedRegistry, false);
|
||||
}
|
||||
|
||||
TEST(Map, ResourceError) {
|
||||
MapTest<> test;
|
||||
test.fileSource->glyphsResponse = [&](const Resource&) {
|
||||
Response response;
|
||||
response.error = std::make_unique<Response::Error>(Response::Error::Reason::Server, "Font file Server failed");
|
||||
return response;
|
||||
};
|
||||
|
||||
test.map.getStyle().loadJSON(
|
||||
R"(
|
||||
{
|
||||
"version": 8,
|
||||
"zoom": 0,
|
||||
"center": [-14.41400, 39.09187],
|
||||
"sources": {
|
||||
"mapbox": {
|
||||
"type": "geojson",
|
||||
"data": {
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"name": "ជនជាប់សង្ស័យ"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[
|
||||
-14.4195556640625,
|
||||
39.091699613104595
|
||||
],
|
||||
[
|
||||
102.3046875,
|
||||
39.36827914916014
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"glyphs": "local://glyphs/{fontstack}/{range}.pbf",
|
||||
"fonts": "local://glyphs/{fontstack}/{language}.pbf",
|
||||
"layers": [
|
||||
{
|
||||
"id": "background",
|
||||
"type": "background",
|
||||
"paint": {
|
||||
"background-color": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "lines-symbol",
|
||||
"type": "symbol",
|
||||
"source": "mapbox",
|
||||
"layout": {
|
||||
"text-field": "{name}",
|
||||
"symbol-placement": "point",
|
||||
"symbol-spacing": 150,
|
||||
"text-allow-overlap": true,
|
||||
"text-font": [ "abc" ],
|
||||
"text-size": 24
|
||||
}
|
||||
}
|
||||
]
|
||||
})");
|
||||
try {
|
||||
test.frontend.render(test.map);
|
||||
} catch (...) {
|
||||
auto error = std::current_exception(); // captur
|
||||
EXPECT_EQ(mbgl::util::toString(error), "Font file Server failed");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Map, StencilOverflow) {
|
||||
MapTest<> test;
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ public:
|
||||
|
||||
class StubGlyphRequestor : public GlyphRequestor {
|
||||
public:
|
||||
void onGlyphsAvailable(GlyphMap glyphs) override {
|
||||
void onGlyphsAvailable(GlyphMap glyphs, HBShapeRequests) override {
|
||||
if (glyphsAvailable) glyphsAvailable(std::move(glyphs));
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ TEST(GlyphManager, LoadingSuccess) {
|
||||
|
||||
test.observer.glyphsLoaded = [&](const FontStack& fontStack, const GlyphRange& range) {
|
||||
ASSERT_EQ(fontStack, FontStack{{"Test Stack"}});
|
||||
ASSERT_EQ(range, GlyphRange(0, 255));
|
||||
ASSERT_EQ(range, GlyphRange(0, 255, GlyphIDType::FontPBF));
|
||||
};
|
||||
|
||||
test.requestor.glyphsAvailable = [&](GlyphMap glyphs) {
|
||||
@@ -196,7 +196,7 @@ TEST(GlyphManager, LoadingSuccess) {
|
||||
test.end();
|
||||
};
|
||||
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{"Test Stack"}}, {u'a', u'å', u' '}}});
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{{"Test Stack"}}, {u'a', u'å', u' '}}}, {}});
|
||||
}
|
||||
|
||||
TEST(GlyphManager, LoadingFail) {
|
||||
@@ -211,7 +211,7 @@ TEST(GlyphManager, LoadingFail) {
|
||||
test.observer.glyphsError =
|
||||
[&](const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
|
||||
EXPECT_EQ(fontStack, FontStack({"Test Stack"}));
|
||||
EXPECT_EQ(glyphRange, GlyphRange(0, 255));
|
||||
EXPECT_EQ(glyphRange, GlyphRange(0, 255, GlyphIDType::FontPBF));
|
||||
|
||||
EXPECT_TRUE(error != nullptr);
|
||||
EXPECT_EQ(util::toString(error), "Failed by the test case");
|
||||
@@ -224,7 +224,7 @@ TEST(GlyphManager, LoadingFail) {
|
||||
test.end();
|
||||
};
|
||||
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{"Test Stack"}}, {u'a', u'å'}}});
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{{"Test Stack"}}, {u'a', u'å'}}}, {}});
|
||||
}
|
||||
|
||||
TEST(GlyphManager, LoadingCorrupted) {
|
||||
@@ -239,7 +239,7 @@ TEST(GlyphManager, LoadingCorrupted) {
|
||||
test.observer.glyphsError =
|
||||
[&](const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
|
||||
EXPECT_EQ(fontStack, FontStack({"Test Stack"}));
|
||||
EXPECT_EQ(glyphRange, GlyphRange(0, 255));
|
||||
EXPECT_EQ(glyphRange, GlyphRange(0, 255, GlyphIDType::FontPBF));
|
||||
|
||||
EXPECT_TRUE(error != nullptr);
|
||||
EXPECT_EQ(util::toString(error), "unknown pbf field type exception");
|
||||
@@ -252,7 +252,7 @@ TEST(GlyphManager, LoadingCorrupted) {
|
||||
test.end();
|
||||
};
|
||||
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{"Test Stack"}}, {u'a', u'å'}}});
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{{"Test Stack"}}, {u'a', u'å'}}}, {}});
|
||||
}
|
||||
|
||||
TEST(GlyphManager, LoadingCancel) {
|
||||
@@ -267,7 +267,7 @@ TEST(GlyphManager, LoadingCancel) {
|
||||
FAIL() << "Should never be called";
|
||||
};
|
||||
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{"Test Stack"}}, {u'a', u'å'}}});
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{{"Test Stack"}}, {u'a', u'å'}}}, {}});
|
||||
}
|
||||
|
||||
TEST(GlyphManager, LoadLocalCJKGlyph) {
|
||||
@@ -293,7 +293,7 @@ TEST(GlyphManager, LoadLocalCJKGlyph) {
|
||||
ASSERT_EQ(testPositions.count(u'中'), 1u);
|
||||
|
||||
Immutable<Glyph> glyph = *testPositions.at(u'中');
|
||||
EXPECT_EQ(glyph->id, u'中');
|
||||
EXPECT_EQ(glyph->id.complex.code, u'中');
|
||||
EXPECT_EQ(glyph->metrics.width, 24ul);
|
||||
EXPECT_EQ(glyph->metrics.height, 24ul);
|
||||
EXPECT_EQ(glyph->metrics.left, 0);
|
||||
@@ -309,7 +309,7 @@ TEST(GlyphManager, LoadLocalCJKGlyph) {
|
||||
test.end();
|
||||
};
|
||||
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{"Test Stack"}}, {u'中'}}});
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{{"Test Stack"}}, {u'中'}}}, {}});
|
||||
}
|
||||
|
||||
TEST(GlyphManager, LoadLocalCJKGlyphAfterLoadingRangeFromURL) {
|
||||
@@ -336,16 +336,15 @@ TEST(GlyphManager, LoadLocalCJKGlyphAfterLoadingRangeFromURL) {
|
||||
// instead of using the glyph recieved from the range
|
||||
// for the ideagraphic mark
|
||||
test.glyphManager.getGlyphs(test.requestor,
|
||||
GlyphDependencies{
|
||||
{{{"Test Stack"}}, {u'テ'}} // 0x30c6
|
||||
},
|
||||
GlyphDependencies{{{{{"Test Stack"}}, {u'テ'}}}, // 0x30c6
|
||||
{}},
|
||||
test.fileSource);
|
||||
} else {
|
||||
ASSERT_EQ(testPositions.size(), 1u);
|
||||
ASSERT_EQ(testPositions.count(u'テ'), 1u);
|
||||
|
||||
Immutable<Glyph> glyph = *testPositions.at(u'テ');
|
||||
EXPECT_EQ(glyph->id, u'テ');
|
||||
EXPECT_EQ(glyph->id.complex.code, u'テ');
|
||||
EXPECT_EQ(glyph->metrics.width, 24ul);
|
||||
EXPECT_EQ(glyph->metrics.height, 24ul);
|
||||
EXPECT_EQ(glyph->metrics.left, 0);
|
||||
@@ -358,9 +357,8 @@ TEST(GlyphManager, LoadLocalCJKGlyphAfterLoadingRangeFromURL) {
|
||||
};
|
||||
|
||||
test.run("test/fixtures/resources/glyphs-12244-12543.pbf",
|
||||
GlyphDependencies{
|
||||
{{{"Test Stack"}}, {u'々'}} // 0x3005
|
||||
});
|
||||
GlyphDependencies{{{{{"Test Stack"}}, {u'々'}}}, // 0x3005
|
||||
{}});
|
||||
}
|
||||
|
||||
TEST(GlyphManager, LoadingInvalid) {
|
||||
@@ -380,7 +378,7 @@ TEST(GlyphManager, LoadingInvalid) {
|
||||
|
||||
test.observer.glyphsLoaded = [&](const FontStack& fontStack, const GlyphRange& range) {
|
||||
ASSERT_EQ(fontStack, FontStack{{"Test Stack"}});
|
||||
ASSERT_EQ(range, GlyphRange(0, 255));
|
||||
ASSERT_EQ(range, GlyphRange(0, 255, GlyphIDType::FontPBF));
|
||||
};
|
||||
|
||||
test.requestor.glyphsAvailable = [&](GlyphMap glyphs) {
|
||||
@@ -393,7 +391,7 @@ TEST(GlyphManager, LoadingInvalid) {
|
||||
test.end();
|
||||
};
|
||||
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{"Test Stack"}}, {u'A', u'E'}}});
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{{"Test Stack"}}, {u'A', u'E'}}}, {}});
|
||||
}
|
||||
|
||||
TEST(GlyphManager, ImmediateFileSource) {
|
||||
@@ -436,5 +434,5 @@ TEST(GlyphManager, ImmediateFileSource) {
|
||||
test.end();
|
||||
};
|
||||
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{"Test Stack"}}, {u'a', u'å', u' '}}});
|
||||
test.run("test/fixtures/resources/glyphs.pbf", GlyphDependencies{{{{{"Test Stack"}}, {u'a', u'å', u' '}}}, {}});
|
||||
}
|
||||
|
||||
@@ -7,11 +7,12 @@ 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"));
|
||||
auto sdfs = parseGlyphPBF(GlyphRange{0, 255, GlyphIDType::FontPBF},
|
||||
util::read_file("test/fixtures/resources/fake_glyphs-0-255.pbf"));
|
||||
EXPECT_TRUE(sdfs.size() == 1);
|
||||
|
||||
auto& sdf = sdfs[0];
|
||||
EXPECT_EQ(69u, sdf.id);
|
||||
EXPECT_EQ(69u, sdf.id.complex.code);
|
||||
AlphaImage expected({7, 7});
|
||||
expected.fill('x');
|
||||
EXPECT_EQ(expected, sdf.bitmap);
|
||||
|
||||
@@ -24,7 +24,7 @@ TEST(Shaping, ZWSP) {
|
||||
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);
|
||||
const SectionOptions sectionOptions(1.0f, fontStack, GlyphIDType::FontPBF, 0);
|
||||
const float layoutTextSize = 16.0f;
|
||||
const float layoutTextSizeAtBucketZoomLevel = 16.0f;
|
||||
GlyphMap glyphs = {{FontStackHasher()(fontStack), {{u'中', std::move(immutableGlyph)}}}};
|
||||
|
||||
@@ -6,22 +6,22 @@
|
||||
using namespace mbgl;
|
||||
|
||||
TEST(TaggedString, Trim) {
|
||||
TaggedString basic(u" \t\ntrim that and not this \n\t", SectionOptions(1.0f, {}));
|
||||
TaggedString basic(u" \t\ntrim that and not this \n\t", SectionOptions(1.0f, {}, GlyphIDType::FontPBF, 0));
|
||||
basic.trim();
|
||||
EXPECT_EQ(basic.rawText(), u"trim that and not this");
|
||||
|
||||
TaggedString twoSections;
|
||||
twoSections.addTextSection(u" \t\ntrim that", 1.5f, {});
|
||||
twoSections.addTextSection(u" and not this \n\t", 0.5f, {});
|
||||
twoSections.addTextSection(u" \t\ntrim that", 1.5f, {}, GlyphIDType::FontPBF, false, {});
|
||||
twoSections.addTextSection(u" and not this \n\t", 0.5f, {}, GlyphIDType::FontPBF, false, {});
|
||||
|
||||
twoSections.trim();
|
||||
EXPECT_EQ(twoSections.rawText(), u"trim that and not this");
|
||||
|
||||
TaggedString empty(u"\n\t\v \r \t\n", SectionOptions(1.0f, {}));
|
||||
TaggedString empty(u"\n\t\v \r \t\n", SectionOptions(1.0f, {}, GlyphIDType::FontPBF, 0));
|
||||
empty.trim();
|
||||
EXPECT_EQ(empty.rawText(), u"");
|
||||
|
||||
TaggedString noTrim(u"no trim!", SectionOptions(1.0f, {}));
|
||||
TaggedString noTrim(u"no trim!", SectionOptions(1.0f, {}, GlyphIDType::FontPBF, 0));
|
||||
noTrim.trim();
|
||||
EXPECT_EQ(noTrim.rawText(), u"no trim!");
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
: SymbolFeature(std::make_unique<StubGeometryTileFeature>(
|
||||
std::move(id_), type_, std::move(geometry_), std::move(properties_))) {
|
||||
if (text_) {
|
||||
formattedText = TaggedString(*text_, SectionOptions(1.0, {}));
|
||||
formattedText = TaggedString(*text_, SectionOptions(1.0, {}, GlyphIDType::FontPBF, 0));
|
||||
}
|
||||
icon = std::move(icon_);
|
||||
index = index_;
|
||||
|
||||
Reference in New Issue
Block a user