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
24static 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}
80DEF_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
97DEF_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
140DEF_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
177DEF_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
189namespace {
190
191class SkEmptyTypeface : public SkTypeface {
192public:
193    static sk_sp<SkTypeface> Create() { return sk_sp<SkTypeface>(new SkEmptyTypeface()); }
194protected:
195    SkEmptyTypeface() : SkTypeface(SkFontStyle(), true) { }
196
197    SkStreamAsset* onOpenStream(int* ttcIndex) const override { return nullptr; }
198    SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
199                                           const SkDescriptor*) const override {
200        return nullptr;
201    }
202    void onFilterRec(SkScalerContextRec*) const override { }
203    std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override {
204        return nullptr;
205    }
206    void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { }
207    virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
208                                uint16_t glyphs[], int glyphCount) const override {
209        SK_ABORT("unimplemented");
210        return 0;
211    }
212    int onCountGlyphs() const override { return 0; }
213    int onGetUPEM() const override { return 0; }
214    void onGetFamilyName(SkString* familyName) const override { familyName->reset(); }
215    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override {
216        SK_ABORT("unimplemented");
217        return nullptr;
218    }
219    int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
220                                     int coordinateCount) const override
221    {
222        return 0;
223    }
224    int onGetTableTags(SkFontTableTag tags[]) const override { return 0; }
225    size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { return 0; }
226};
227
228}
229
230static bool count_proc(SkTypeface* face, void* ctx) {
231    int* count = static_cast<int*>(ctx);
232    *count = *count + 1;
233    return false;
234}
235static 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
242DEF_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