• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013 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 "include/core/SkData.h"
9 #include "include/core/SkFontMgr.h"
10 #include "include/core/SkRefCnt.h"
11 #include "include/core/SkStream.h"
12 #include "include/core/SkTypeface.h"
13 #include "include/ports/SkTypeface_win.h"
14 #include "include/private/SkFixed.h"
15 #include "src/core/SkAdvancedTypefaceMetrics.h"
16 #include "src/core/SkFontDescriptor.h"
17 #include "src/core/SkFontMgrPriv.h"
18 #include "src/core/SkFontPriv.h"
19 #include "src/core/SkTypefaceCache.h"
20 #include "src/sfnt/SkOTTable_OS_2.h"
21 #include "src/sfnt/SkSFNTHeader.h"
22 #include "src/utils/SkUTF.h"
23 #include "tests/Test.h"
24 #include "tools/Resources.h"
25 #include "tools/ToolUtils.h"
26 #include "tools/fonts/TestEmptyTypeface.h"
27 
28 #include <memory>
29 
TypefaceStyle_test(skiatest::Reporter * reporter,uint16_t weight,uint16_t width,SkData * data)30 static void TypefaceStyle_test(skiatest::Reporter* reporter,
31                                uint16_t weight, uint16_t width, SkData* data)
32 {
33     sk_sp<SkData> dataCopy;
34     if (!data->unique()) {
35         dataCopy = SkData::MakeWithCopy(data->data(), data->size());
36         data = dataCopy.get();
37     }
38     SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(data->writable_data());
39 
40     SkSFNTHeader::TableDirectoryEntry* tableEntry =
41         SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
42     SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr;
43     int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
44     for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
45         if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) {
46             os2TableEntry = tableEntry + tableEntryIndex;
47             break;
48         }
49     }
50     SkASSERT_RELEASE(os2TableEntry);
51 
52     size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset);
53     SkOTTableOS2_V0* os2Table = SkTAddOffset<SkOTTableOS2_V0>(sfntHeader, os2TableOffset);
54     os2Table->usWeightClass.value = SkEndian_SwapBE16(weight);
55     using WidthType = SkOTTableOS2_V0::WidthClass::Value;
56     os2Table->usWidthClass.value = static_cast<WidthType>(SkEndian_SwapBE16(width));
57 
58     sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromData(sk_ref_sp(data)));
59     if (!newTypeface) {
60         // Not all SkFontMgr can MakeFromStream().
61         return;
62     }
63 
64     SkFontStyle newStyle = newTypeface->fontStyle();
65 
66     //printf("%d, %f\n", weight, (newStyle.weight() - (float)0x7FFF) / (float)0x7FFF);
67     //printf("%d, %f\n", width , (newStyle.width()  - (float)0x7F)   / (float)0x7F);
68     //printf("%d, %d\n", weight, newStyle.weight());
69     //printf("%d, %d\n", width , newStyle.width());
70 
71     // Some back-ends (CG, GDI, DW) support OS/2 version A which uses 0 - 10 (but all differently).
72     REPORTER_ASSERT(reporter,
73                     newStyle.weight() == weight ||
74                     (weight <=   10 && newStyle.weight() == 100 * weight) ||
75                     (weight ==    4 && newStyle.weight() == 350) ||  // GDI weirdness
76                     (weight ==    5 && newStyle.weight() == 400) ||  // GDI weirdness
77                     (weight ==    0 && newStyle.weight() ==   1) ||  // DW weirdness
78                     (weight == 1000 && newStyle.weight() == 999)     // DW weirdness
79     );
80 
81     // Some back-ends (GDI) don't support width, ensure these always report 'medium'.
82     REPORTER_ASSERT(reporter,
83                     newStyle.width() == width ||
84                     newStyle.width() == 5);
85 }
DEF_TEST(TypefaceStyle,reporter)86 DEF_TEST(TypefaceStyle, reporter) {
87     std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("fonts/Em.ttf"));
88     if (!stream) {
89         REPORT_FAILURE(reporter, "fonts/Em.ttf", SkString("Cannot load resource"));
90         return;
91     }
92     sk_sp<SkData> data(SkData::MakeFromStream(stream.get(), stream->getLength()));
93 
94     using SkFS = SkFontStyle;
95     for (int weight = SkFS::kInvisible_Weight; weight <= SkFS::kExtraBlack_Weight; ++weight) {
96         TypefaceStyle_test(reporter, weight, 5, data.get());
97     }
98     for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltraExpanded_Width; ++width) {
99         TypefaceStyle_test(reporter, 400, width, data.get());
100     }
101 }
102 
DEF_TEST(TypefaceRoundTrip,reporter)103 DEF_TEST(TypefaceRoundTrip, reporter) {
104     sk_sp<SkTypeface> typeface(MakeResourceAsTypeface("fonts/7630.otf"));
105     if (!typeface) {
106         // Not all SkFontMgr can MakeFromStream().
107         return;
108     }
109 
110     int fontIndex;
111     std::unique_ptr<SkStreamAsset> stream = typeface->openStream(&fontIndex);
112 
113     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
114     sk_sp<SkTypeface> typeface2 = fm->makeFromStream(std::move(stream), fontIndex);
115     REPORTER_ASSERT(reporter, typeface2);
116 }
117 
DEF_TEST(FontDescriptorNegativeVariationSerialize,reporter)118 DEF_TEST(FontDescriptorNegativeVariationSerialize, reporter) {
119     SkFontDescriptor desc;
120     SkFixed axis = -SK_Fixed1;
121     auto font = std::make_unique<SkMemoryStream>("a", 1, false);
122     desc.setFontData(std::make_unique<SkFontData>(std::move(font), 0, &axis, 1));
123 
124     SkDynamicMemoryWStream stream;
125     desc.serialize(&stream);
126     SkFontDescriptor descD;
127     SkFontDescriptor::Deserialize(stream.detachAsStream().get(), &descD);
128     std::unique_ptr<SkFontData> fontData = descD.detachFontData();
129     if (!fontData) {
130         REPORT_FAILURE(reporter, "fontData", SkString());
131         return;
132     }
133 
134     if (fontData->getAxisCount() != 1) {
135         REPORT_FAILURE(reporter, "fontData->getAxisCount() != 1", SkString());
136         return;
137     }
138 
139     REPORTER_ASSERT(reporter, fontData->getAxis()[0] == -SK_Fixed1);
140 };
141 
DEF_TEST(TypefaceAxes,reporter)142 DEF_TEST(TypefaceAxes, reporter) {
143     std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
144     if (!distortable) {
145         REPORT_FAILURE(reporter, "distortable", SkString());
146         return;
147     }
148     constexpr int numberOfAxesInDistortable = 1;
149 
150     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
151     // The position may be over specified. If there are multiple values for a given axis,
152     // ensure the last one since that's what css-fonts-4 requires.
153     const SkFontArguments::VariationPosition::Coordinate position[] = {
154         { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
155         { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
156     };
157     SkFontArguments params;
158     params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
159     // TODO: if axes are set and the back-end doesn't support them, should we create the typeface?
160     sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
161 
162     if (!typeface) {
163         return;  // Not all SkFontMgr can makeFromStream().
164     }
165 
166     int count = typeface->getVariationDesignPosition(nullptr, 0);
167     if (count == -1) {
168         return;  // The number of axes is unknown.
169     }
170     REPORTER_ASSERT(reporter, count == numberOfAxesInDistortable);
171 
172     SkFontArguments::VariationPosition::Coordinate positionRead[numberOfAxesInDistortable];
173     count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
174     if (count == -1) {
175         return;  // The position cannot be determined.
176     }
177     REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(positionRead));
178 
179     REPORTER_ASSERT(reporter, positionRead[0].axis == position[1].axis);
180 
181     // Convert to fixed for "almost equal".
182     SkFixed fixedRead = SkScalarToFixed(positionRead[0].value);
183     SkFixed fixedOriginal = SkScalarToFixed(position[1].value);
184     REPORTER_ASSERT(reporter, SkTAbs(fixedRead - fixedOriginal) < 2 || // variation set correctly
185                               SkTAbs(fixedRead - SK_Fixed1    ) < 2);  // variation remained default
186 }
187 
DEF_TEST(TypefaceVariationIndex,reporter)188 DEF_TEST(TypefaceVariationIndex, reporter) {
189     std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
190     if (!distortable) {
191         REPORT_FAILURE(reporter, "distortable", SkString());
192         return;
193     }
194 
195     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
196     SkFontArguments params;
197     // The first named variation position in Distortable is 'Thin'.
198     params.setCollectionIndex(0x00010000);
199     sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
200     if (!typeface) {
201         // FreeType is the only weird thing that supports this, Skia just needs to make sure if it
202         // gets one of these things make sense.
203         return;
204     }
205 
206     int count = typeface->getVariationDesignPosition(nullptr, 0);
207     if (!(count == 1)) {
208         REPORT_FAILURE(reporter, "count == 1", SkString());
209         return;
210     }
211 
212     SkFontArguments::VariationPosition::Coordinate positionRead[1];
213     count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
214     if (count == -1) {
215         return;
216     }
217     if (!(count == 1)) {
218         REPORT_FAILURE(reporter, "count == 1", SkString());
219         return;
220     }
221     REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t'));
222     REPORTER_ASSERT(reporter, positionRead[0].value == 0.5);
223 }
224 
DEF_TEST(Typeface,reporter)225 DEF_TEST(Typeface, reporter) {
226 
227     sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));
228     sk_sp<SkTypeface> t2(SkTypeface::MakeDefault());
229 
230     REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get()));
231     REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t1.get()));
232     REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t2.get()));
233     REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), nullptr));
234     REPORTER_ASSERT(reporter, SkTypeface::Equal(t2.get(), nullptr));
235 }
236 
DEF_TEST(TypefaceAxesParameters,reporter)237 DEF_TEST(TypefaceAxesParameters, reporter) {
238     std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
239     if (!distortable) {
240         REPORT_FAILURE(reporter, "distortable", SkString());
241         return;
242     }
243     constexpr int numberOfAxesInDistortable = 1;
244     constexpr SkScalar minAxisInDistortable = 0.5;
245     constexpr SkScalar defAxisInDistortable = 1;
246     constexpr SkScalar maxAxisInDistortable = 2;
247     constexpr bool axisIsHiddenInDistortable = true;
248 
249     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
250 
251     SkFontArguments params;
252     sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
253 
254     if (!typeface) {
255         return;  // Not all SkFontMgr can makeFromStream().
256     }
257 
258     SkFontParameters::Variation::Axis parameter[numberOfAxesInDistortable];
259     int count = typeface->getVariationDesignParameters(parameter, SK_ARRAY_COUNT(parameter));
260     if (count == -1) {
261         return;
262     }
263 
264     REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(parameter));
265     REPORTER_ASSERT(reporter, parameter[0].min == minAxisInDistortable);
266     REPORTER_ASSERT(reporter, parameter[0].def == defAxisInDistortable);
267     REPORTER_ASSERT(reporter, parameter[0].max == maxAxisInDistortable);
268     REPORTER_ASSERT(reporter, parameter[0].tag == SkSetFourByteTag('w','g','h','t'));
269     // This seems silly, but allows MSAN to ensure that isHidden is initialized.
270     // With GDI or before macOS 10.12, Win10, or FreeType 2.8.1 the API for hidden is missing.
271     REPORTER_ASSERT(reporter, parameter[0].isHidden() == axisIsHiddenInDistortable ||
272                               parameter[0].isHidden() == false);
273 }
274 
count_proc(SkTypeface * face,void * ctx)275 static bool count_proc(SkTypeface* face, void* ctx) {
276     int* count = static_cast<int*>(ctx);
277     *count = *count + 1;
278     return false;
279 }
count(skiatest::Reporter * reporter,const SkTypefaceCache & cache)280 static int count(skiatest::Reporter* reporter, const SkTypefaceCache& cache) {
281     int count = 0;
282     sk_sp<SkTypeface> none = cache.findByProcAndRef(count_proc, &count);
283     REPORTER_ASSERT(reporter, none == nullptr);
284     return count;
285 }
286 
DEF_TEST(TypefaceCache,reporter)287 DEF_TEST(TypefaceCache, reporter) {
288     sk_sp<SkTypeface> t1(TestEmptyTypeface::Make());
289     {
290         SkTypefaceCache cache;
291         REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
292         {
293             sk_sp<SkTypeface> t0(TestEmptyTypeface::Make());
294             cache.add(t0);
295             REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
296             cache.add(t1);
297             REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
298             cache.purgeAll();
299             REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
300         }
301         REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
302         cache.purgeAll();
303         REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
304     }
305     REPORTER_ASSERT(reporter, t1->unique());
306 }
307 
check_serialize_behaviors(sk_sp<SkTypeface> tf,bool isLocalData,skiatest::Reporter * reporter)308 static void check_serialize_behaviors(sk_sp<SkTypeface> tf, bool isLocalData,
309                                       skiatest::Reporter* reporter) {
310     if (!tf) {
311         return;
312     }
313     auto data0 = tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
314     auto data1 = tf->serialize(SkTypeface::SerializeBehavior::kDontIncludeData);
315     auto data2 = tf->serialize(SkTypeface::SerializeBehavior::kIncludeDataIfLocal);
316 
317     REPORTER_ASSERT(reporter, data0->size() >= data1->size());
318 
319     if (isLocalData) {
320         REPORTER_ASSERT(reporter, data0->equals(data2.get()));
321     } else {
322         REPORTER_ASSERT(reporter, data1->equals(data2.get()));
323     }
324 }
325 
DEF_TEST(Typeface_serialize,reporter)326 DEF_TEST(Typeface_serialize, reporter) {
327     check_serialize_behaviors(SkTypeface::MakeDefault(), false, reporter);
328     check_serialize_behaviors(SkTypeface::MakeFromStream(
329                                          GetResourceAsStream("fonts/Distortable.ttf")),
330                               true, reporter);
331 
332 }
333 
DEF_TEST(Typeface_glyph_to_char,reporter)334 DEF_TEST(Typeface_glyph_to_char, reporter) {
335     SkFont font(ToolUtils::emoji_typeface(), 12);
336     SkASSERT(font.getTypeface());
337     char const * text = ToolUtils::emoji_sample_text();
338     size_t const textLen = strlen(text);
339     size_t const codepointCount = SkUTF::CountUTF8(text, textLen);
340     char const * const textEnd = text + textLen;
341     std::unique_ptr<SkUnichar[]> originalCodepoints(new SkUnichar[codepointCount]);
342     for (size_t i = 0; i < codepointCount; ++i) {
343         originalCodepoints[i] = SkUTF::NextUTF8(&text, textEnd);
344     }
345     std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[codepointCount]);
346     font.unicharsToGlyphs(originalCodepoints.get(), codepointCount, glyphs.get());
347 
348     std::unique_ptr<SkUnichar[]> newCodepoints(new SkUnichar[codepointCount]);
349     SkFontPriv::GlyphsToUnichars(font, glyphs.get(), codepointCount, newCodepoints.get());
350 
351     SkString familyName;
352     font.getTypeface()->getFamilyName(&familyName);
353     for (size_t i = 0; i < codepointCount; ++i) {
354 #if defined(SK_BUILD_FOR_WIN)
355         // GDI does not support character to glyph mapping outside BMP.
356         if (gSkFontMgr_DefaultFactory == &SkFontMgr_New_GDI &&
357             0xFFFF < originalCodepoints[i] && newCodepoints[i] == 0)
358         {
359             continue;
360         }
361 #endif
362         // If two codepoints map to the same glyph then this assert is not valid.
363         // However, the emoji test font should never have multiple characters map to the same glyph.
364         REPORTER_ASSERT(reporter, originalCodepoints[i] == newCodepoints[i],
365                         "name:%s i:%d original:%d new:%d glyph:%d", familyName.c_str(), i,
366                         originalCodepoints[i], newCodepoints[i], glyphs[i]);
367     }
368 }
369 
370 // This test makes sure the legacy typeface creation does not lose its specified
371 // style. See https://bugs.chromium.org/p/skia/issues/detail?id=8447 for more
372 // context.
DEF_TEST(LegacyMakeTypeface,reporter)373 DEF_TEST(LegacyMakeTypeface, reporter) {
374     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
375     sk_sp<SkTypeface> typeface1 = fm->legacyMakeTypeface(nullptr, SkFontStyle::Italic());
376     sk_sp<SkTypeface> typeface2 = fm->legacyMakeTypeface(nullptr, SkFontStyle::Bold());
377     sk_sp<SkTypeface> typeface3 = fm->legacyMakeTypeface(nullptr, SkFontStyle::BoldItalic());
378 
379     REPORTER_ASSERT(reporter, typeface1->isItalic());
380     REPORTER_ASSERT(reporter, !typeface1->isBold());
381     REPORTER_ASSERT(reporter, !typeface2->isItalic());
382     REPORTER_ASSERT(reporter, typeface2->isBold());
383     REPORTER_ASSERT(reporter, typeface3->isItalic());
384     REPORTER_ASSERT(reporter, typeface3->isBold());
385 }
386