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