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 "SkFontDescriptor.h"
     12 #include "SkFontMgr.h"
     13 #include "SkMakeUnique.h"
     14 #include "SkOTTable_OS_2.h"
     15 #include "SkSFNTHeader.h"
     16 #include "SkStream.h"
     17 #include "SkRefCnt.h"
     18 #include "SkTestEmptyTypeface.h"
     19 #include "SkTypeface.h"
     20 #include "SkTypefaceCache.h"
     21 #include "Resources.h"
     22 #include "Test.h"
     23 
     24 #include <memory>
     25 
     26 static void TypefaceStyle_test(skiatest::Reporter* reporter,
     27                                uint16_t weight, uint16_t width, SkData* data)
     28 {
     29     sk_sp<SkData> dataCopy;
     30     if (!data->unique()) {
     31         dataCopy = SkData::MakeWithCopy(data->data(), data->size());
     32         data = dataCopy.get();
     33     }
     34     SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(data->writable_data());
     35 
     36     SkSFNTHeader::TableDirectoryEntry* tableEntry =
     37         SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
     38     SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr;
     39     int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
     40     for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
     41         if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) {
     42             os2TableEntry = tableEntry + tableEntryIndex;
     43             break;
     44         }
     45     }
     46     SkASSERT_RELEASE(os2TableEntry);
     47 
     48     size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset);
     49     SkOTTableOS2_V0* os2Table = SkTAddOffset<SkOTTableOS2_V0>(sfntHeader, os2TableOffset);
     50     os2Table->usWeightClass.value = SkEndian_SwapBE16(weight);
     51     using WidthType = SkOTTableOS2_V0::WidthClass::Value;
     52     os2Table->usWidthClass.value = static_cast<WidthType>(SkEndian_SwapBE16(width));
     53 
     54     sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromData(sk_ref_sp(data)));
     55     if (!newTypeface) {
     56         // Not all SkFontMgr can MakeFromStream().
     57         return;
     58     }
     59 
     60     SkFontStyle newStyle = newTypeface->fontStyle();
     61 
     62     //printf("%d, %f\n", weight, (newStyle.weight() - (float)0x7FFF) / (float)0x7FFF);
     63     //printf("%d, %f\n", width , (newStyle.width()  - (float)0x7F)   / (float)0x7F);
     64     //printf("%d, %d\n", weight, newStyle.weight());
     65     //printf("%d, %d\n", width , newStyle.width());
     66 
     67     // Some back-ends (CG, GDI, DW) support OS/2 version A which uses 0 - 10 (but all differently).
     68     REPORTER_ASSERT(reporter,
     69                     newStyle.weight() == weight ||
     70                     (weight <=   10 && newStyle.weight() == 100 * weight) ||
     71                     (weight ==    4 && newStyle.weight() == 350) ||  // GDI weirdness
     72                     (weight ==    5 && newStyle.weight() == 400) ||  // GDI weirdness
     73                     (weight ==    0 && newStyle.weight() ==   1) ||  // DW weirdness
     74                     (weight == 1000 && newStyle.weight() == 999)     // DW weirdness
     75     );
     76 
     77     // Some back-ends (GDI) don't support width, ensure these always report 'medium'.
     78     REPORTER_ASSERT(reporter,
     79                     newStyle.width() == width ||
     80                     newStyle.width() == 5);
     81 }
     82 DEF_TEST(TypefaceStyle, reporter) {
     83     std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("fonts/Em.ttf"));
     84     if (!stream) {
     85         REPORT_FAILURE(reporter, "fonts/Em.ttf", SkString("Cannot load resource"));
     86         return;
     87     }
     88     sk_sp<SkData> data(SkData::MakeFromStream(stream.get(), stream->getLength()));
     89 
     90     using SkFS = SkFontStyle;
     91     for (int weight = SkFS::kInvisible_Weight; weight <= SkFS::kExtraBlack_Weight; ++weight) {
     92         TypefaceStyle_test(reporter, weight, 5, data.get());
     93     }
     94     for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltraExpanded_Width; ++width) {
     95         TypefaceStyle_test(reporter, 400, width, data.get());
     96     }
     97 }
     98 
     99 DEF_TEST(TypefaceRoundTrip, reporter) {
    100     sk_sp<SkTypeface> typeface(MakeResourceAsTypeface("fonts/7630.otf"));
    101     if (!typeface) {
    102         // Not all SkFontMgr can MakeFromStream().
    103         return;
    104     }
    105 
    106     int fontIndex;
    107     std::unique_ptr<SkStreamAsset> stream(typeface->openStream(&fontIndex));
    108 
    109     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
    110     sk_sp<SkTypeface> typeface2 = fm->makeFromStream(std::move(stream), fontIndex);
    111     REPORTER_ASSERT(reporter, typeface2);
    112 }
    113 
    114 DEF_TEST(FontDescriptorNegativeVariationSerialize, reporter) {
    115     SkFontDescriptor desc;
    116     SkFixed axis = -SK_Fixed1;
    117     auto font = skstd::make_unique<SkMemoryStream>("a", 1, false);
    118     desc.setFontData(skstd::make_unique<SkFontData>(std::move(font), 0, &axis, 1));
    119 
    120     SkDynamicMemoryWStream stream;
    121     desc.serialize(&stream);
    122     SkFontDescriptor descD;
    123     SkFontDescriptor::Deserialize(stream.detachAsStream().get(), &descD);
    124     std::unique_ptr<SkFontData> fontData = descD.detachFontData();
    125     if (!fontData) {
    126         REPORT_FAILURE(reporter, "fontData", SkString());
    127         return;
    128     }
    129 
    130     if (fontData->getAxisCount() != 1) {
    131         REPORT_FAILURE(reporter, "fontData->getAxisCount() != 1", SkString());
    132         return;
    133     }
    134 
    135     REPORTER_ASSERT(reporter, fontData->getAxis()[0] == -SK_Fixed1);
    136 };
    137 
    138 DEF_TEST(TypefaceAxes, reporter) {
    139     std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
    140     if (!distortable) {
    141         REPORT_FAILURE(reporter, "distortable", SkString());
    142         return;
    143     }
    144     constexpr int numberOfAxesInDistortable = 1;
    145 
    146     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
    147     // The position may be over specified. If there are multiple values for a given axis,
    148     // ensure the last one since that's what css-fonts-4 requires.
    149     const SkFontArguments::VariationPosition::Coordinate position[] = {
    150         { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
    151         { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
    152     };
    153     SkFontArguments params;
    154     params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
    155     // TODO: if axes are set and the back-end doesn't support them, should we create the typeface?
    156     sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
    157 
    158     if (!typeface) {
    159         // Not all SkFontMgr can makeFromStream().
    160         return;
    161     }
    162 
    163     int count = typeface->getVariationDesignPosition(nullptr, 0);
    164     if (count == -1) {
    165         return;
    166     }
    167     REPORTER_ASSERT(reporter, count == numberOfAxesInDistortable);
    168 
    169     SkFontArguments::VariationPosition::Coordinate positionRead[numberOfAxesInDistortable];
    170     count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
    171     REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(positionRead));
    172 
    173     REPORTER_ASSERT(reporter, positionRead[0].axis == position[1].axis);
    174 
    175     // Convert to fixed for "almost equal".
    176     SkFixed fixedRead = SkScalarToFixed(positionRead[0].value);
    177     SkFixed fixedOriginal = SkScalarToFixed(position[1].value);
    178     REPORTER_ASSERT(reporter, SkTAbs(fixedRead - fixedOriginal) < 2);
    179 }
    180 
    181 DEF_TEST(TypefaceVariationIndex, reporter) {
    182     std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
    183     if (!distortable) {
    184         REPORT_FAILURE(reporter, "distortable", SkString());
    185         return;
    186     }
    187 
    188     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
    189     SkFontArguments params;
    190     // The first named variation position in Distortable is 'Thin'.
    191     params.setCollectionIndex(0x00010000);
    192     sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
    193     if (!typeface) {
    194         // FreeType is the only weird thing that supports this, Skia just needs to make sure if it
    195         // gets one of these things make sense.
    196         return;
    197     }
    198 
    199     int count = typeface->getVariationDesignPosition(nullptr, 0);
    200     if (!(count == 1)) {
    201         REPORT_FAILURE(reporter, "count == 1", SkString());
    202         return;
    203     }
    204 
    205     SkFontArguments::VariationPosition::Coordinate positionRead[1];
    206     count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
    207     if (count == -1) {
    208         return;
    209     }
    210     if (!(count == 1)) {
    211         REPORT_FAILURE(reporter, "count == 1", SkString());
    212         return;
    213     }
    214     REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t'));
    215     REPORTER_ASSERT(reporter, positionRead[0].value == 0.5);
    216 }
    217 
    218 DEF_TEST(Typeface, reporter) {
    219 
    220     sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));
    221     sk_sp<SkTypeface> t2(SkTypeface::MakeDefault());
    222 
    223     REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get()));
    224     REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t1.get()));
    225     REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t2.get()));
    226     REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), nullptr));
    227     REPORTER_ASSERT(reporter, SkTypeface::Equal(t2.get(), nullptr));
    228 }
    229 
    230 DEF_TEST(TypefaceAxesParameters, reporter) {
    231     std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
    232     if (!distortable) {
    233         REPORT_FAILURE(reporter, "distortable", SkString());
    234         return;
    235     }
    236     constexpr int numberOfAxesInDistortable = 1;
    237     constexpr SkScalar minAxisInDistortable = 0.5;
    238     constexpr SkScalar defAxisInDistortable = 1;
    239     constexpr SkScalar maxAxisInDistortable = 2;
    240     constexpr bool axisIsHiddenInDistortable = false;
    241 
    242     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
    243 
    244     SkFontArguments params;
    245     sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
    246 
    247     if (!typeface) {
    248         // Not all SkFontMgr can makeFromStream().
    249         return;
    250     }
    251 
    252     SkFontParameters::Variation::Axis parameter[numberOfAxesInDistortable];
    253     int count = typeface->getVariationDesignParameters(parameter, SK_ARRAY_COUNT(parameter));
    254     if (count == -1) {
    255         return;
    256     }
    257 
    258     REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(parameter));
    259     REPORTER_ASSERT(reporter, parameter[0].min == minAxisInDistortable);
    260     REPORTER_ASSERT(reporter, parameter[0].def == defAxisInDistortable);
    261     REPORTER_ASSERT(reporter, parameter[0].max == maxAxisInDistortable);
    262     REPORTER_ASSERT(reporter, parameter[0].tag == SkSetFourByteTag('w','g','h','t'));
    263     REPORTER_ASSERT(reporter, parameter[0].isHidden() == axisIsHiddenInDistortable);
    264 
    265 }
    266 
    267 static bool count_proc(SkTypeface* face, void* ctx) {
    268     int* count = static_cast<int*>(ctx);
    269     *count = *count + 1;
    270     return false;
    271 }
    272 static int count(skiatest::Reporter* reporter, const SkTypefaceCache& cache) {
    273     int count = 0;
    274     SkTypeface* none = cache.findByProcAndRef(count_proc, &count);
    275     REPORTER_ASSERT(reporter, none == nullptr);
    276     return count;
    277 }
    278 
    279 DEF_TEST(TypefaceCache, reporter) {
    280     sk_sp<SkTypeface> t1(SkTestEmptyTypeface::Make());
    281     {
    282         SkTypefaceCache cache;
    283         REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
    284         {
    285             sk_sp<SkTypeface> t0(SkTestEmptyTypeface::Make());
    286             cache.add(t0.get());
    287             REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
    288             cache.add(t1.get());
    289             REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
    290             cache.purgeAll();
    291             REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
    292         }
    293         REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
    294         cache.purgeAll();
    295         REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
    296     }
    297     REPORTER_ASSERT(reporter, t1->unique());
    298 }
    299 
    300 static void check_serialize_behaviors(sk_sp<SkTypeface> tf, bool isLocalData,
    301                                       skiatest::Reporter* reporter) {
    302     if (!tf) {
    303         return;
    304     }
    305     auto data0 = tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
    306     auto data1 = tf->serialize(SkTypeface::SerializeBehavior::kDontIncludeData);
    307     auto data2 = tf->serialize(SkTypeface::SerializeBehavior::kIncludeDataIfLocal);
    308 
    309     REPORTER_ASSERT(reporter, data0->size() >= data1->size());
    310 
    311     if (isLocalData) {
    312         REPORTER_ASSERT(reporter, data0->equals(data2.get()));
    313     } else {
    314         REPORTER_ASSERT(reporter, data1->equals(data2.get()));
    315     }
    316 }
    317 
    318 DEF_TEST(Typeface_serialize, reporter) {
    319     check_serialize_behaviors(SkTypeface::MakeDefault(), false, reporter);
    320     check_serialize_behaviors(SkTypeface::MakeFromStream(
    321                                          GetResourceAsStream("fonts/Distortable.ttf")),
    322                               true, reporter);
    323 
    324 }
    325 
    326