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