• 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 "SkAdvancedTypefaceMetrics.h"
9 #include "SkData.h"
10 #include "SkFixed.h"
11 #include "SkFontMgr.h"
12 #include "SkMakeUnique.h"
13 #include "SkOTTable_OS_2.h"
14 #include "SkSFNTHeader.h"
15 #include "SkStream.h"
16 #include "SkRefCnt.h"
17 #include "SkTypeface.h"
18 #include "SkTypefaceCache.h"
19 #include "Resources.h"
20 #include "Test.h"
21 
22 #include <memory>
23 
TypefaceStyle_test(skiatest::Reporter * reporter,uint16_t weight,uint16_t width,SkData * data)24 static void TypefaceStyle_test(skiatest::Reporter* reporter,
25                                uint16_t weight, uint16_t width, SkData* data)
26 {
27     sk_sp<SkData> dataCopy;
28     if (!data->unique()) {
29         dataCopy = SkData::MakeWithCopy(data->data(), data->size());
30         data = dataCopy.get();
31     }
32     SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(data->writable_data());
33 
34     SkSFNTHeader::TableDirectoryEntry* tableEntry =
35         SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
36     SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr;
37     int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
38     for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
39         if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) {
40             os2TableEntry = tableEntry + tableEntryIndex;
41             break;
42         }
43     }
44     SkASSERT_RELEASE(os2TableEntry);
45 
46     size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset);
47     SkOTTableOS2_V0* os2Table = SkTAddOffset<SkOTTableOS2_V0>(sfntHeader, os2TableOffset);
48     os2Table->usWeightClass.value = SkEndian_SwapBE16(weight);
49     using WidthType = SkOTTableOS2_V0::WidthClass::Value;
50     os2Table->usWidthClass.value = static_cast<WidthType>(SkEndian_SwapBE16(width));
51 
52     sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromStream(new SkMemoryStream(sk_ref_sp(data))));
53     if (!newTypeface) {
54         // Not all SkFontMgr can MakeFromStream().
55         return;
56     }
57 
58     SkFontStyle newStyle = newTypeface->fontStyle();
59 
60     //printf("%d, %f\n", weight, (newStyle.weight() - (float)0x7FFF) / (float)0x7FFF);
61     //printf("%d, %f\n", width , (newStyle.width()  - (float)0x7F)   / (float)0x7F);
62     //printf("%d, %d\n", weight, newStyle.weight());
63     //printf("%d, %d\n", width , newStyle.width());
64 
65     // Some back-ends (CG, GDI, DW) support OS/2 version A which uses 0 - 10 (but all differently).
66     REPORTER_ASSERT(reporter,
67                     newStyle.weight() == weight ||
68                     (weight <=   10 && newStyle.weight() == 100 * weight) ||
69                     (weight ==    4 && newStyle.weight() == 350) ||  // GDI weirdness
70                     (weight ==    5 && newStyle.weight() == 400) ||  // GDI weirdness
71                     (weight ==    0 && newStyle.weight() ==   1) ||  // DW weirdness
72                     (weight == 1000 && newStyle.weight() == 999)     // DW weirdness
73     );
74 
75     // Some back-ends (GDI) don't support width, ensure these always report 'medium'.
76     REPORTER_ASSERT(reporter,
77                     newStyle.width() == width ||
78                     newStyle.width() == 5);
79 }
DEF_TEST(TypefaceStyle,reporter)80 DEF_TEST(TypefaceStyle, reporter) {
81     std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("fonts/Em.ttf"));
82     if (!stream) {
83         REPORT_FAILURE(reporter, "fonts/Em.ttf", SkString("Cannot load resource"));
84         return;
85     }
86     sk_sp<SkData> data(SkData::MakeFromStream(stream.get(), stream->getLength()));
87 
88     using SkFS = SkFontStyle;
89     for (int weight = SkFS::kInvisible_Weight; weight <= SkFS::kExtraBlack_Weight; ++weight) {
90         TypefaceStyle_test(reporter, weight, 5, data.get());
91     }
92     for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltraExpanded_Width; ++width) {
93         TypefaceStyle_test(reporter, 400, width, data.get());
94     }
95 }
96 
DEF_TEST(TypefaceAxes,reporter)97 DEF_TEST(TypefaceAxes, reporter) {
98     std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
99     if (!distortable) {
100         REPORT_FAILURE(reporter, "distortable", SkString());
101         return;
102     }
103     constexpr int numberOfAxesInDistortable = 1;
104 
105     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
106     // The position may be over specified. If there are multiple values for a given axis,
107     // ensure the last one since that's what css-fonts-4 requires.
108     const SkFontArguments::VariationPosition::Coordinate position[] = {
109         { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
110         { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
111     };
112     SkFontArguments params;
113     params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
114     // TODO: if axes are set and the back-end doesn't support them, should we create the typeface?
115     sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
116 
117     if (!typeface) {
118         // Not all SkFontMgr can makeFromStream().
119         return;
120     }
121 
122     int count = typeface->getVariationDesignPosition(nullptr, 0);
123     if (count == -1) {
124         return;
125     }
126     REPORTER_ASSERT(reporter, count == numberOfAxesInDistortable);
127 
128     SkFontArguments::VariationPosition::Coordinate positionRead[numberOfAxesInDistortable];
129     count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
130     REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(positionRead));
131 
132     REPORTER_ASSERT(reporter, positionRead[0].axis == position[1].axis);
133 
134     // Convert to fixed for "almost equal".
135     SkFixed fixedRead = SkScalarToFixed(positionRead[0].value);
136     SkFixed fixedOriginal = SkScalarToFixed(position[1].value);
137     REPORTER_ASSERT(reporter, fixedRead == fixedOriginal);
138 }
139 
DEF_TEST(TypefaceVariationIndex,reporter)140 DEF_TEST(TypefaceVariationIndex, reporter) {
141     std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
142     if (!distortable) {
143         REPORT_FAILURE(reporter, "distortable", SkString());
144         return;
145     }
146 
147     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
148     SkFontArguments params;
149     // The first named variation position in Distortable is 'Thin'.
150     params.setCollectionIndex(0x00010000);
151     sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
152     if (!typeface) {
153         // FreeType is the only weird thing that supports this, Skia just needs to make sure if it
154         // gets one of these things make sense.
155         return;
156     }
157 
158     int count = typeface->getVariationDesignPosition(nullptr, 0);
159     if (!(count == 1)) {
160         REPORT_FAILURE(reporter, "count == 1", SkString());
161         return;
162     }
163 
164     SkFontArguments::VariationPosition::Coordinate positionRead[1];
165     count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
166     if (count == -1) {
167         return;
168     }
169     if (!(count == 1)) {
170         REPORT_FAILURE(reporter, "count == 1", SkString());
171         return;
172     }
173     REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t'));
174     REPORTER_ASSERT(reporter, positionRead[0].value == 0.5);
175 }
176 
DEF_TEST(Typeface,reporter)177 DEF_TEST(Typeface, reporter) {
178 
179     sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));
180     sk_sp<SkTypeface> t2(SkTypeface::MakeDefault());
181 
182     REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get()));
183     REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t1.get()));
184     REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t2.get()));
185     REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), nullptr));
186     REPORTER_ASSERT(reporter, SkTypeface::Equal(t2.get(), nullptr));
187 }
188 
189 namespace {
190 
191 class SkEmptyTypeface : public SkTypeface {
192 public:
Create()193     static sk_sp<SkTypeface> Create() { return sk_sp<SkTypeface>(new SkEmptyTypeface()); }
194 protected:
SkEmptyTypeface()195     SkEmptyTypeface() : SkTypeface(SkFontStyle(), true) { }
196 
onOpenStream(int * ttcIndex) const197     SkStreamAsset* onOpenStream(int* ttcIndex) const override { return nullptr; }
onCreateScalerContext(const SkScalerContextEffects &,const SkDescriptor *) const198     SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
199                                            const SkDescriptor*) const override {
200         return nullptr;
201     }
onFilterRec(SkScalerContextRec *) const202     void onFilterRec(SkScalerContextRec*) const override { }
onGetAdvancedMetrics() const203     std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override {
204         return nullptr;
205     }
onGetFontDescriptor(SkFontDescriptor *,bool *) const206     void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { }
onCharsToGlyphs(const void * chars,Encoding encoding,uint16_t glyphs[],int glyphCount) const207     virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
208                                 uint16_t glyphs[], int glyphCount) const override {
209         SK_ABORT("unimplemented");
210         return 0;
211     }
onCountGlyphs() const212     int onCountGlyphs() const override { return 0; }
onGetUPEM() const213     int onGetUPEM() const override { return 0; }
onGetFamilyName(SkString * familyName) const214     void onGetFamilyName(SkString* familyName) const override { familyName->reset(); }
onCreateFamilyNameIterator() const215     SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override {
216         SK_ABORT("unimplemented");
217         return nullptr;
218     }
onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],int coordinateCount) const219     int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
220                                      int coordinateCount) const override
221     {
222         return 0;
223     }
onGetTableTags(SkFontTableTag tags[]) const224     int onGetTableTags(SkFontTableTag tags[]) const override { return 0; }
onGetTableData(SkFontTableTag,size_t,size_t,void *) const225     size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { return 0; }
226 };
227 
228 }
229 
count_proc(SkTypeface * face,void * ctx)230 static bool count_proc(SkTypeface* face, void* ctx) {
231     int* count = static_cast<int*>(ctx);
232     *count = *count + 1;
233     return false;
234 }
count(skiatest::Reporter * reporter,const SkTypefaceCache & cache)235 static int count(skiatest::Reporter* reporter, const SkTypefaceCache& cache) {
236     int count = 0;
237     SkTypeface* none = cache.findByProcAndRef(count_proc, &count);
238     REPORTER_ASSERT(reporter, none == nullptr);
239     return count;
240 }
241 
DEF_TEST(TypefaceCache,reporter)242 DEF_TEST(TypefaceCache, reporter) {
243     sk_sp<SkTypeface> t1(SkEmptyTypeface::Create());
244     {
245         SkTypefaceCache cache;
246         REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
247         {
248             sk_sp<SkTypeface> t0(SkEmptyTypeface::Create());
249             cache.add(t0.get());
250             REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
251             cache.add(t1.get());
252             REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
253             cache.purgeAll();
254             REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
255         }
256         REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
257         cache.purgeAll();
258         REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
259     }
260     REPORTER_ASSERT(reporter, t1->unique());
261 }
262