Home | History | Annotate | Download | only in tests
      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 
     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 }
     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 
     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 
    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 
    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:
    193     static sk_sp<SkTypeface> Create() { return sk_sp<SkTypeface>(new SkEmptyTypeface()); }
    194 protected:
    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 
    230 static bool count_proc(SkTypeface* face, void* ctx) {
    231     int* count = static_cast<int*>(ctx);
    232     *count = *count + 1;
    233     return false;
    234 }
    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 
    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