Flutter Engine
The Flutter Engine
Text.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include <unordered_map>
9
17#include "tests/Test.h"
18#include "tools/ToolUtils.h"
20
21using namespace skottie;
22
23namespace {
24
25class RecordMatchFamilyStyleSkFontMgr : public SkFontMgr {
26public:
27 const SkFontStyle* styleRequestedWhenMatchingFamily(const char* family) const {
28 auto s = fStyleRequestedWhenMatchingFamily.find(family);
29 return s != fStyleRequestedWhenMatchingFamily.end() ? &s->second : nullptr;
30 }
31
32private:
33 int onCountFamilies() const override { return 0; }
34 void onGetFamilyName(int index, SkString* familyName) const override {}
35 sk_sp<SkFontStyleSet> onCreateStyleSet(int index) const override { return nullptr; }
36
37 sk_sp<SkFontStyleSet> onMatchFamily(const char[]) const override { return nullptr; }
38
39 sk_sp<SkTypeface> onMatchFamilyStyle(const char family[],
40 const SkFontStyle& style) const override {
41 fStyleRequestedWhenMatchingFamily[family] = style;
42 return nullptr;
43 }
44 sk_sp<SkTypeface> onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle& style,
45 const char* bcp47[], int bcp47Count,
46 SkUnichar character) const override {
47 fStyleRequestedWhenMatchingFamily[familyName] = style;
48 return nullptr;
49 }
50
51 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int ttcIndex) const override {
52 return nullptr;
53 }
54 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>,
55 int ttcIndex) const override {
56 return nullptr;
57 }
58 sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset>,
59 const SkFontArguments&) const override {
60 return nullptr;
61 }
62 sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
63 return nullptr;
64 }
65
66 sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle) const override {
67 return nullptr;
68 }
69
70 mutable std::unordered_map<std::string, SkFontStyle> fStyleRequestedWhenMatchingFamily;
71};
72
73} // namespace
74
75// This test relies on Skottie internals/implementation details, and may need to
76// be updated in the future, if Skottie font resolution changes.
77DEF_TEST(Skottie_Text_Style, r) {
78 static constexpr char json[] =
79 R"({
80 "v": "5.2.1",
81 "w": 100,
82 "h": 100,
83 "fr": 10,
84 "ip": 0,
85 "op": 100,
86 "fonts": {
87 "list": [
88 { "fName" : "f1", "fFamily": "f1", "fStyle" : "Regular" },
89 { "fName" : "f2", "fFamily": "f2", "fStyle" : "Medium" },
90 { "fName" : "f3", "fFamily": "f3", "fStyle" : "Bold" },
91 { "fName" : "f4", "fFamily": "f4", "fStyle" : "Light" },
92 { "fName" : "f5", "fFamily": "f5", "fStyle" : "Extra" },
93 { "fName" : "f6", "fFamily": "f6", "fStyle" : "ExtraBold" },
94
95 { "fName" : "f7" , "fFamily": "f7" , "fStyle" : "Regular Italic" },
96 { "fName" : "f8" , "fFamily": "f8" , "fStyle" : "Medium Italic" },
97 { "fName" : "f9" , "fFamily": "f9" , "fStyle" : "Bold Italic" },
98 { "fName" : "f10", "fFamily": "f10", "fStyle" : "Light Oblique" },
99 { "fName" : "f11", "fFamily": "f11", "fStyle" : "Extra Oblique" },
100 { "fName" : "f12", "fFamily": "f12", "fStyle" : "Extrabold Oblique" },
101
102 { "fName" : "f13", "fFamily": "f13", "fStyle" : "Italic" },
103 { "fName" : "f14", "fFamily": "f14", "fStyle" : "Oblique" },
104 { "fName" : "f15", "fFamily": "f15", "fStyle" : "" }
105 ]
106 }
107 })";
108
109 SkMemoryStream stream(json, strlen(json));
110 auto fmgr = sk_make_sp<RecordMatchFamilyStyleSkFontMgr>();
111
112 auto anim = Animation::Builder()
113 .setFontManager(fmgr)
114 .setTextShapingFactory(SkShapers::BestAvailable())
115 .make(&stream);
116
117 REPORTER_ASSERT(r, anim);
118
119 static constexpr struct {
120 const char* family;
121 SkFontStyle::Weight weight;
122 SkFontStyle::Slant slant;
123 } expected[] = {
130
137
141 };
142
143 for (const auto& exp : expected) {
144 const auto* style = fmgr->styleRequestedWhenMatchingFamily(exp.family);
145 REPORTER_ASSERT(r, style);
146 REPORTER_ASSERT(r, style->weight() == exp.weight);
147 REPORTER_ASSERT(r, style->slant () == exp.slant );
148 }
149}
150
151DEF_TEST(Skottie_Text_LayoutError, r) {
152 // Text node properties:
153 // - scale to fit
154 // - box width: 100
155 // - min font size: 70
156 // - string: Foo Bar Baz
157 //
158 // Layout should fail with these unsatisfiable constraints.
159 static constexpr char json[] =
160 R"({
161 "v": "5.2.1",
162 "w": 100,
163 "h": 100,
164 "fr": 10,
165 "ip": 0,
166 "op": 100,
167 "fonts": {
168 "list": [{
169 "fFamily": "Arial",
170 "fName": "Arial",
171 "fStyle": "Bold"
172 }]
173 },
174 "layers": [{
175 "ty": 5,
176 "t": {
177 "d": {
178 "k": [{
179 "t": 0,
180 "s": {
181 "f": "Arial",
182 "t": "Foo Bar Baz",
183 "s": 24,
184 "fc": [1,1,1,1],
185 "lh": 70,
186 "ps": [0, 0],
187 "sz": [100, 100],
188 "mf": 70,
189 "rs": 1
190 }
191 }]
192 }
193 }
194 }]
195 })";
196
197 class Logger final : public skottie::Logger {
198 public:
199 const std::vector<SkString>& errors() const { return fErrors; }
200
201 private:
202 void log(Level lvl, const char message[], const char* = nullptr) override {
203 if (lvl == Level::kError) {
204 fErrors.emplace_back(message);
205 }
206 }
207
208 std::vector<SkString> fErrors;
209 };
210
211 class PortableRP final : public skresources::ResourceProvider {
212 private:
213 sk_sp<SkTypeface> loadTypeface(const char[], const char[]) const override {
215 }
216 };
217
218 SkMemoryStream stream(json, strlen(json));
219 auto logger = sk_make_sp<Logger>();
220
221 auto anim = Animation::Builder()
222 .setLogger(logger)
223 .setResourceProvider(sk_make_sp<PortableRP>())
224 .make(&stream);
225
226 REPORTER_ASSERT(r, anim);
227 REPORTER_ASSERT(r, logger->errors().size() == 1);
228 REPORTER_ASSERT(r, logger->errors()[0].startsWith("Text layout failed"));
229}
230
231DEF_TEST(Skottie_Text_FontFamily, r) {
232 static constexpr char json[] =
233 R"({
234 "v": "5.2.1",
235 "w": 100,
236 "h": 100,
237 "fr": 10,
238 "ip": 0,
239 "op": 100,
240 "fonts": {
241 "list": [{
242 "fFamily": "family_1",
243 "fName": "Font1",
244 "fStyle": "Bold"
245 }]
246 },
247 "layers": [{
248 "ty": 5,
249 "t": {
250 "d": {
251 "k": [{
252 "t": 0,
253 "s": {
254 "f": "Font1",
255 "t": "Foo Bar Baz",
256 "s": 24,
257 "fc": [1,1,1,1],
258 "lh": 70,
259 "ps": [0, 0],
260 "sz": [100, 100],
261 "mf": 70,
262 "rs": 0
263 }
264 }]
265 }
266 }
267 }]
268 })";
269
270 class TextObserver final : public PropertyObserver {
271 public:
272 const std::unique_ptr<TextPropertyHandle>& text() const { return fText; }
273
274 private:
275 void onTextProperty(const char node_name[],
276 const LazyHandle<TextPropertyHandle>& lh) override {
277 SkASSERT(!fText); // only one text prop in test
278 fText = lh();
279 }
280 std::unique_ptr<TextPropertyHandle> fText;
281 };
282
283 auto fmgr = sk_make_sp<RecordMatchFamilyStyleSkFontMgr>();
284 auto prop_observer = sk_make_sp<TextObserver>();
285
286 SkMemoryStream stream(json, strlen(json));
287 auto anim = Animation::Builder()
288 .setFontManager(fmgr)
289 .setPropertyObserver(prop_observer)
290 .setTextShapingFactory(SkShapers::BestAvailable())
291 .make(&stream);
292 REPORTER_ASSERT(r, anim);
293
294 // Original family name was passed to fontmgr.
295 const auto* style1 = fmgr->styleRequestedWhenMatchingFamily("family_1");
296 REPORTER_ASSERT(r, style1);
297
298 const auto& text_handle = prop_observer->text();
299 REPORTER_ASSERT(r, text_handle);
300 auto txt = (*text_handle).get();
301 txt.fFontFamily = "family_2";
302 (*text_handle).set(txt);
303
304 // Updated family name was passed to fontmgr.
305 const auto* style2 = fmgr->styleRequestedWhenMatchingFamily("family_2");
306 REPORTER_ASSERT(r, style2);
307}
#define SkASSERT(cond)
Definition: SkAssert.h:116
int32_t SkUnichar
Definition: SkTypes.h:175
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
virtual void onGetFamilyName(int index, SkString *familyName) const =0
virtual sk_sp< SkTypeface > onMakeFromData(sk_sp< SkData >, int ttcIndex) const =0
virtual sk_sp< SkFontStyleSet > onCreateStyleSet(int index) const =0
virtual sk_sp< SkTypeface > onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle &, const char *bcp47[], int bcp47Count, SkUnichar character) const =0
virtual sk_sp< SkFontStyleSet > onMatchFamily(const char familyName[]) const =0
virtual sk_sp< SkTypeface > onMakeFromStreamIndex(std::unique_ptr< SkStreamAsset >, int ttcIndex) const =0
virtual sk_sp< SkTypeface > onMakeFromStreamArgs(std::unique_ptr< SkStreamAsset >, const SkFontArguments &) const =0
virtual sk_sp< SkTypeface > onMatchFamilyStyle(const char familyName[], const SkFontStyle &) const =0
virtual sk_sp< SkTypeface > onLegacyMakeTypeface(const char familyName[], SkFontStyle) const =0
virtual sk_sp< SkTypeface > onMakeFromFile(const char path[], int ttcIndex) const =0
virtual int onCountFamilies() const =0
@ kExtraBold_Weight
Definition: SkFontStyle.h:27
virtual void onTextProperty(const char node_name[], const LazyHandle< TextPropertyHandle > &)
virtual sk_sp< SkTypeface > loadTypeface(const char[], const char[]) const
Definition: SkResources.h:206
struct MyStruct s
std::u16string text
Win32Message message
sk_sp< Factory > BestAvailable()
sk_sp< SkTypeface > CreatePortableTypeface(const char *name, SkFontStyle style)
DlVertices::Builder Builder
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_TEST(Skottie_Text_Style, r)
Definition: Text.cpp:77