• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
10 #include "include/core/SkFontMgr.h"
11 #include "include/core/SkFontStyle.h"
12 #include "modules/skottie/include/Skottie.h"
13 #include "modules/skottie/include/SkottieProperty.h"
14 #include "tests/Test.h"
15 #include "tools/ToolUtils.h"
16 
17 using namespace skottie;
18 
19 namespace {
20 
21 class RecordMatchFamilyStyleSkFontMgr : public SkFontMgr {
22 public:
styleRequestedWhenMatchingFamily(const char * family) const23     const SkFontStyle* styleRequestedWhenMatchingFamily(const char* family) const {
24         auto s = fStyleRequestedWhenMatchingFamily.find(family);
25         return s != fStyleRequestedWhenMatchingFamily.end() ? &s->second : nullptr;
26     }
27 
28 private:
onCountFamilies() const29     int onCountFamilies() const override { return 0; }
onGetFamilyName(int index,SkString * familyName) const30     void onGetFamilyName(int index, SkString* familyName) const override {}
onCreateStyleSet(int index) const31     SkFontStyleSet* onCreateStyleSet(int index) const override { return nullptr; }
32 
onMatchFamily(const char[]) const33     SkFontStyleSet* onMatchFamily(const char[]) const override { return nullptr; }
34 
onMatchFamilyStyle(const char family[],const SkFontStyle & style) const35     SkTypeface* onMatchFamilyStyle(const char family[], const SkFontStyle& style) const override {
36         SkASSERT(fStyleRequestedWhenMatchingFamily.find(family) ==
37                  fStyleRequestedWhenMatchingFamily.end());
38         fStyleRequestedWhenMatchingFamily[family] = style;
39         return nullptr;
40     }
onMatchFamilyStyleCharacter(const char familyName[],const SkFontStyle &,const char * bcp47[],int bcp47Count,SkUnichar character) const41     SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
42                                             const char* bcp47[], int bcp47Count,
43                                             SkUnichar character) const override {
44         return nullptr;
45     }
46 
onMakeFromData(sk_sp<SkData>,int ttcIndex) const47     sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int ttcIndex) const override {
48         return nullptr;
49     }
onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>,int ttcIndex) const50     sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>,
51                                             int ttcIndex) const override {
52         return nullptr;
53     }
onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset>,const SkFontArguments &) const54     sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset>,
55                                            const SkFontArguments&) const override {
56         return nullptr;
57     }
onMakeFromFile(const char path[],int ttcIndex) const58     sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
59         return nullptr;
60     }
61 
onLegacyMakeTypeface(const char familyName[],SkFontStyle) const62     sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle) const override {
63         return nullptr;
64     }
65 
66     mutable std::unordered_map<std::string, SkFontStyle> fStyleRequestedWhenMatchingFamily;
67 };
68 
69 } // namespace
70 
71 // This test relies on Skottie internals/implementation details, and may need to
72 // be updated in the future, if Skottie font resolution changes.
DEF_TEST(Skottie_Text_Style,r)73 DEF_TEST(Skottie_Text_Style, r) {
74     static constexpr char json[] =
75         R"({
76              "v": "5.2.1",
77              "w": 100,
78              "h": 100,
79              "fr": 10,
80              "ip": 0,
81              "op": 100,
82              "fonts": {
83                "list": [
84                  { "fName"  : "f1", "fFamily": "f1", "fStyle" : "Regular"   },
85                  { "fName"  : "f2", "fFamily": "f2", "fStyle" : "Medium"    },
86                  { "fName"  : "f3", "fFamily": "f3", "fStyle" : "Bold"      },
87                  { "fName"  : "f4", "fFamily": "f4", "fStyle" : "Light"     },
88                  { "fName"  : "f5", "fFamily": "f5", "fStyle" : "Extra"     },
89                  { "fName"  : "f6", "fFamily": "f6", "fStyle" : "ExtraBold" },
90 
91                  { "fName"  : "f7" , "fFamily": "f7" , "fStyle" : "Regular Italic"    },
92                  { "fName"  : "f8" , "fFamily": "f8" , "fStyle" : "Medium Italic"     },
93                  { "fName"  : "f9" , "fFamily": "f9" , "fStyle" : "Bold Italic"       },
94                  { "fName"  : "f10", "fFamily": "f10", "fStyle" : "Light Oblique"     },
95                  { "fName"  : "f11", "fFamily": "f11", "fStyle" : "Extra Oblique"     },
96                  { "fName"  : "f12", "fFamily": "f12", "fStyle" : "Extrabold Oblique" },
97 
98                  { "fName"  : "f13", "fFamily": "f13", "fStyle" : "Italic"  },
99                  { "fName"  : "f14", "fFamily": "f14", "fStyle" : "Oblique" },
100                  { "fName"  : "f15", "fFamily": "f15", "fStyle" : ""        }
101                ]
102              }
103            })";
104 
105     SkMemoryStream stream(json, strlen(json));
106     auto fmgr = sk_make_sp<RecordMatchFamilyStyleSkFontMgr>();
107 
108     auto anim = Animation::Builder()
109                     .setFontManager(fmgr)
110                     .make(&stream);
111 
112     REPORTER_ASSERT(r, anim);
113 
114     static constexpr struct {
115         const char*         family;
116         SkFontStyle::Weight weight;
117         SkFontStyle::Slant  slant;
118     } expected[] = {
119         { "f1" , SkFontStyle::kNormal_Weight   , SkFontStyle::kUpright_Slant },
120         { "f2" , SkFontStyle::kMedium_Weight   , SkFontStyle::kUpright_Slant },
121         { "f3" , SkFontStyle::kBold_Weight     , SkFontStyle::kUpright_Slant },
122         { "f4" , SkFontStyle::kLight_Weight    , SkFontStyle::kUpright_Slant },
123         { "f5" , SkFontStyle::kExtraBold_Weight, SkFontStyle::kUpright_Slant },
124         { "f6" , SkFontStyle::kExtraBold_Weight, SkFontStyle::kUpright_Slant },
125 
126         { "f7" , SkFontStyle::kNormal_Weight   , SkFontStyle::kItalic_Slant  },
127         { "f8" , SkFontStyle::kMedium_Weight   , SkFontStyle::kItalic_Slant  },
128         { "f9" , SkFontStyle::kBold_Weight     , SkFontStyle::kItalic_Slant  },
129         { "f10", SkFontStyle::kLight_Weight    , SkFontStyle::kOblique_Slant },
130         { "f11", SkFontStyle::kExtraBold_Weight, SkFontStyle::kOblique_Slant },
131         { "f12", SkFontStyle::kExtraBold_Weight, SkFontStyle::kOblique_Slant },
132 
133         { "f13", SkFontStyle::kNormal_Weight   , SkFontStyle::kItalic_Slant  },
134         { "f14", SkFontStyle::kNormal_Weight   , SkFontStyle::kOblique_Slant },
135         { "f15", SkFontStyle::kNormal_Weight   , SkFontStyle::kUpright_Slant },
136     };
137 
138     for (const auto& exp : expected) {
139         const auto* style = fmgr->styleRequestedWhenMatchingFamily(exp.family);
140         REPORTER_ASSERT(r, style);
141         REPORTER_ASSERT(r, style->weight() == exp.weight);
142         REPORTER_ASSERT(r, style->slant () == exp.slant );
143     }
144 }
145 
DEF_TEST(Skottie_Text_LayoutError,r)146 DEF_TEST(Skottie_Text_LayoutError, r) {
147     // Text node properties:
148     //   - scale to fit
149     //   - box width: 100
150     //   - min font size: 70
151     //   - string: Foo Bar Baz
152     //
153     // Layout should fail with these unsatisfiable constraints.
154     static constexpr char json[] =
155         R"({
156              "v": "5.2.1",
157              "w": 100,
158              "h": 100,
159              "fr": 10,
160              "ip": 0,
161              "op": 100,
162              "fonts": {
163                "list": [{
164                  "fFamily": "Arial",
165                  "fName": "Arial",
166                  "fStyle": "Bold"
167                }]
168              },
169              "layers": [{
170                "ty": 5,
171                "t": {
172                  "d": {
173                    "k": [{
174                      "t": 0,
175                      "s": {
176                        "f": "Arial",
177                        "t": "Foo Bar Baz",
178                        "s": 24,
179                        "fc": [1,1,1,1],
180                        "lh": 70,
181                        "ps": [0, 0],
182                        "sz": [100, 100],
183                        "mf": 70,
184                        "rs": 1
185                      }
186                    }]
187                  }
188                }
189              }]
190            })";
191 
192     class Logger final : public skottie::Logger {
193     public:
194         const std::vector<SkString>& errors() const { return fErrors; }
195 
196     private:
197         void log(Level lvl, const char message[], const char* = nullptr) override {
198             if (lvl == Level::kError) {
199                 fErrors.emplace_back(message);
200             }
201         }
202 
203         std::vector<SkString> fErrors;
204     };
205 
206     class PortableRP final : public skresources::ResourceProvider {
207     private:
208         sk_sp<SkTypeface> loadTypeface(const char[], const char[]) const override {
209             return ToolUtils::create_portable_typeface("Serif", SkFontStyle());
210         }
211     };
212 
213     SkMemoryStream stream(json, strlen(json));
214     auto logger = sk_make_sp<Logger>();
215 
216     auto anim = Animation::Builder()
217                     .setLogger(logger)
218                     .setResourceProvider(sk_make_sp<PortableRP>())
219                     .make(&stream);
220 
221     REPORTER_ASSERT(r, anim);
222     REPORTER_ASSERT(r, logger->errors().size() == 1);
223     REPORTER_ASSERT(r, logger->errors()[0].startsWith("Text layout failed"));
224 }
225