skia2/tests/TypefaceTest.cpp
Ben Wagner 2478c70a03 Use current variation position in cloning typeface.
The FreeType and CoreText ports would use the default value for an axis
instead of the current value for any unspecified axes. Change this so
that when cloning a typeface any unspecified axes preferentially use the
current axis value if it is known.

Also adds a test that unspecified axes are not changed when cloning.

Change-Id: I751ee5517f1d6b827c6d4ab245e9d681c8df6b42
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/370456
Reviewed-by: Herb Derby <herb@google.com>
Reviewed-by: Dominik Röttsches <drott@chromium.org>
Commit-Queue: Ben Wagner <bungeman@google.com>
2021-02-16 18:06:19 +00:00

604 lines
25 KiB
C++

/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkData.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkStream.h"
#include "include/core/SkTypeface.h"
#include "include/ports/SkTypeface_win.h"
#include "include/private/SkFixed.h"
#include "src/core/SkAdvancedTypefaceMetrics.h"
#include "src/core/SkFontDescriptor.h"
#include "src/core/SkFontMgrPriv.h"
#include "src/core/SkFontPriv.h"
#include "src/core/SkTypefaceCache.h"
#include "src/sfnt/SkOTTable_OS_2.h"
#include "src/sfnt/SkSFNTHeader.h"
#include "src/utils/SkUTF.h"
#include "tests/Test.h"
#include "tools/Resources.h"
#include "tools/ToolUtils.h"
#include "tools/fonts/TestEmptyTypeface.h"
#include <memory>
static void TypefaceStyle_test(skiatest::Reporter* reporter,
uint16_t weight, uint16_t width, SkData* data)
{
sk_sp<SkData> dataCopy;
if (!data->unique()) {
dataCopy = SkData::MakeWithCopy(data->data(), data->size());
data = dataCopy.get();
}
SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(data->writable_data());
SkSFNTHeader::TableDirectoryEntry* tableEntry =
SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr;
int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) {
os2TableEntry = tableEntry + tableEntryIndex;
break;
}
}
SkASSERT_RELEASE(os2TableEntry);
size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset);
SkOTTableOS2_V0* os2Table = SkTAddOffset<SkOTTableOS2_V0>(sfntHeader, os2TableOffset);
os2Table->usWeightClass.value = SkEndian_SwapBE16(weight);
using WidthType = SkOTTableOS2_V0::WidthClass::Value;
os2Table->usWidthClass.value = static_cast<WidthType>(SkEndian_SwapBE16(width));
sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromData(sk_ref_sp(data)));
if (!newTypeface) {
// Not all SkFontMgr can MakeFromStream().
return;
}
SkFontStyle newStyle = newTypeface->fontStyle();
//printf("%d, %f\n", weight, (newStyle.weight() - (float)0x7FFF) / (float)0x7FFF);
//printf("%d, %f\n", width , (newStyle.width() - (float)0x7F) / (float)0x7F);
//printf("%d, %d\n", weight, newStyle.weight());
//printf("%d, %d\n", width , newStyle.width());
// Some back-ends (CG, GDI, DW) support OS/2 version A which uses 0 - 10 (but all differently).
REPORTER_ASSERT(reporter,
newStyle.weight() == weight ||
(weight <= 10 && newStyle.weight() == 100 * weight) ||
(weight == 4 && newStyle.weight() == 350) || // GDI weirdness
(weight == 5 && newStyle.weight() == 400) || // GDI weirdness
(weight == 0 && newStyle.weight() == 1) || // DW weirdness
(weight == 1000 && newStyle.weight() == 999) // DW weirdness
);
// Some back-ends (GDI) don't support width, ensure these always report 'medium'.
REPORTER_ASSERT(reporter,
newStyle.width() == width ||
newStyle.width() == 5);
}
DEF_TEST(TypefaceStyle, reporter) {
std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("fonts/Em.ttf"));
if (!stream) {
REPORT_FAILURE(reporter, "fonts/Em.ttf", SkString("Cannot load resource"));
return;
}
sk_sp<SkData> data(SkData::MakeFromStream(stream.get(), stream->getLength()));
using SkFS = SkFontStyle;
for (int weight = SkFS::kInvisible_Weight; weight <= SkFS::kExtraBlack_Weight; ++weight) {
TypefaceStyle_test(reporter, weight, 5, data.get());
}
for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltraExpanded_Width; ++width) {
TypefaceStyle_test(reporter, 400, width, data.get());
}
}
DEF_TEST(TypefacePostScriptName, reporter) {
sk_sp<SkTypeface> typeface(MakeResourceAsTypeface("fonts/Em.ttf"));
if (!typeface) {
// Not all SkFontMgr can MakeFromStream().
return;
}
SkString postScriptName;
bool hasName = typeface->getPostScriptName(&postScriptName);
bool hasName2 = typeface->getPostScriptName(nullptr);
REPORTER_ASSERT(reporter, hasName == hasName2);
if (hasName) {
REPORTER_ASSERT(reporter, postScriptName == SkString("Em"));
}
}
DEF_TEST(TypefaceRoundTrip, reporter) {
sk_sp<SkTypeface> typeface(MakeResourceAsTypeface("fonts/7630.otf"));
if (!typeface) {
// Not all SkFontMgr can MakeFromStream().
return;
}
int fontIndex;
std::unique_ptr<SkStreamAsset> stream = typeface->openStream(&fontIndex);
sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
sk_sp<SkTypeface> typeface2 = fm->makeFromStream(std::move(stream), fontIndex);
REPORTER_ASSERT(reporter, typeface2);
}
DEF_TEST(FontDescriptorNegativeVariationSerialize, reporter) {
SkFontDescriptor desc;
SkFontArguments::VariationPosition::Coordinate* variation = desc.setVariationCoordinates(1);
variation[0] = { 0, -1.0f };
SkDynamicMemoryWStream stream;
desc.serialize(&stream);
SkFontDescriptor descD;
SkFontDescriptor::Deserialize(stream.detachAsStream().get(), &descD);
if (descD.getVariationCoordinateCount() != 1) {
REPORT_FAILURE(reporter, "descD.getVariationCoordinateCount() != 1", SkString());
return;
}
REPORTER_ASSERT(reporter, descD.getVariation()[0].value == -1.0f);
};
DEF_TEST(FontDescriptorDeserializeOldFormat, reporter) {
// From ossfuzz:26254
const uint8_t old_serialized_desc[] = {
0x0, //style
0xff, 0xfb, 0x0, 0x0, 0x0, // kFontAxes
0x0, // coordinateCount
0xff, 0xff, 0x0, 0x0, 0x0, // kSentinel
0x0, // data length
};
SkMemoryStream stream(old_serialized_desc, sizeof(old_serialized_desc), false);
SkFontDescriptor desc;
if (!SkFontDescriptor::Deserialize(&stream, &desc)) {
REPORT_FAILURE(reporter, "!SkFontDescriptor::Deserialize(&stream, &desc)",
SkString("bytes should be recognized unless removing support"));
return;
}
// This call should not crash and should not return a valid SkFontData.
std::unique_ptr<SkFontData> data = desc.maybeAsSkFontData();
REPORTER_ASSERT(reporter, !data);
};
DEF_TEST(TypefaceAxes, reporter) {
using Variation = SkFontArguments::VariationPosition;
// In DWrite in at least up to 1901 18363.1198 IDWriteFontFace5::GetFontAxisValues and
// GetFontAxisValueCount along with IDWriteFontResource::GetFontAxisAttributes and
// GetFontAxisCount (and related) seem to incorrectly collapse multiple axes with the same tag.
// Since this is a limitation of the underlying implementation, for now allow the test to pass
// with the axis tag count (as opposed to the axis count). Eventually all implementations should
// pass this test without 'alsoAcceptedAxisTagCount'.
auto test = [&](SkTypeface* typeface, const Variation& expected, int alsoAcceptedAxisTagCount) {
if (!typeface) {
return; // Not all SkFontMgr can makeFromStream().
}
int actualCount = typeface->getVariationDesignPosition(nullptr, 0);
if (actualCount == -1) {
return; // The number of axes is unknown.
}
REPORTER_ASSERT(reporter, actualCount == expected.coordinateCount ||
actualCount == alsoAcceptedAxisTagCount);
// Variable font conservative bounds don't vary, so ensure they aren't reported.
REPORTER_ASSERT(reporter, typeface->getBounds().isEmpty());
std::unique_ptr<Variation::Coordinate[]> actual(new Variation::Coordinate[actualCount]);
actualCount = typeface->getVariationDesignPosition(actual.get(), actualCount);
if (actualCount == -1) {
return; // The position cannot be determined.
}
REPORTER_ASSERT(reporter, actualCount == expected.coordinateCount ||
actualCount == alsoAcceptedAxisTagCount);
// Every actual must be expected.
std::unique_ptr<bool[]> expectedUsed(new bool[expected.coordinateCount]());
for (int actualIdx = 0; actualIdx < actualCount; ++actualIdx) {
bool actualFound = false;
for (int expectedIdx = 0; expectedIdx < expected.coordinateCount; ++expectedIdx) {
if (expectedUsed[expectedIdx]) {
continue;
}
if (actual[actualIdx].axis != expected.coordinates[expectedIdx].axis) {
continue;
}
// Convert to fixed for "almost equal".
SkFixed fixedRead = SkScalarToFixed(actual[actualIdx].value);
SkFixed fixedOriginal = SkScalarToFixed(expected.coordinates[expectedIdx].value);
if (!(SkTAbs(fixedRead - fixedOriginal) < 2)) {
continue;
}
// This actual matched an unused expected.
actualFound = true;
expectedUsed[expectedIdx] = true;
break;
}
REPORTER_ASSERT(reporter, actualFound,
"Actual axis '%c%c%c%c' with value '%f' not expected",
(actual[actualIdx].axis >> 24) & 0xFF,
(actual[actualIdx].axis >> 16) & 0xFF,
(actual[actualIdx].axis >> 8) & 0xFF,
(actual[actualIdx].axis ) & 0xFF,
SkScalarToDouble(actual[actualIdx].value));
}
};
sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
// Not specifying a position should produce the default.
{
std::unique_ptr<SkStreamAsset> variable(GetResourceAsStream("fonts/Variable.ttf"));
if (!variable) {
REPORT_FAILURE(reporter, "variable", SkString());
return;
}
const Variation::Coordinate defaultPosition[] = {
{ SkSetFourByteTag('w','g','h','t'), 400.0f },
{ SkSetFourByteTag('w','d','t','h'), 100.0f },
};
sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(variable), 0);
test(typeface.get(), Variation{&defaultPosition[0], 2}, -1);
}
// Multiple axes with the same tag (and min, max, default) works.
{
std::unique_ptr<SkStreamAsset> dupTags(GetResourceAsStream("fonts/VaryAlongQuads.ttf"));
if (!dupTags) {
REPORT_FAILURE(reporter, "dupTags", SkString());
return;
}
// The position may be over specified. If there are multiple values for a given axis,
// ensure the last one since that's what css-fonts-4 requires.
const Variation::Coordinate position[] = {
{ SkSetFourByteTag('w','g','h','t'), 700.0f },
{ SkSetFourByteTag('w','g','h','t'), 600.0f },
{ SkSetFourByteTag('w','g','h','t'), 600.0f },
};
SkFontArguments params;
params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(dupTags), params);
test(typeface.get(), Variation{&position[1], 2}, 1);
}
// Overspecifying an axis tag value applies the last one in the list.
{
std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
if (!distortable) {
REPORT_FAILURE(reporter, "distortable", SkString());
return;
}
// The position may be over specified. If there are multiple values for a given axis,
// ensure the last one since that's what css-fonts-4 requires.
const Variation::Coordinate position[] = {
{ SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
{ SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
};
SkFontArguments params;
params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
test(typeface.get(), Variation{&position[1], 1}, -1);
if (typeface) {
// Cloning without specifying any parameters should produce an equivalent variation.
sk_sp<SkTypeface> clone = typeface->makeClone(SkFontArguments());
test(clone.get(), Variation{&position[1], 1}, -1);
}
}
}
DEF_TEST(TypefaceVariationIndex, reporter) {
std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
if (!distortable) {
REPORT_FAILURE(reporter, "distortable", SkString());
return;
}
sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
SkFontArguments params;
// The first named variation position in Distortable is 'Thin'.
params.setCollectionIndex(0x00010000);
sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
if (!typeface) {
// FreeType is the only weird thing that supports this, Skia just needs to make sure if it
// gets one of these things make sense.
return;
}
int count = typeface->getVariationDesignPosition(nullptr, 0);
if (!(count == 1)) {
REPORT_FAILURE(reporter, "count == 1", SkString());
return;
}
SkFontArguments::VariationPosition::Coordinate positionRead[1];
count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
if (count == -1) {
return;
}
if (!(count == 1)) {
REPORT_FAILURE(reporter, "count == 1", SkString());
return;
}
REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t'));
REPORTER_ASSERT(reporter, positionRead[0].value == 0.5);
}
DEF_TEST(Typeface, reporter) {
sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));
sk_sp<SkTypeface> t2(SkTypeface::MakeDefault());
REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get()));
REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t1.get()));
REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t2.get()));
REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), nullptr));
REPORTER_ASSERT(reporter, SkTypeface::Equal(t2.get(), nullptr));
}
DEF_TEST(TypefaceAxesParameters, reporter) {
using Axis = SkFontParameters::Variation::Axis;
// In DWrite in at least up to 1901 18363.1198 IDWriteFontFace5::GetFontAxisValues and
// GetFontAxisValueCount along with IDWriteFontResource::GetFontAxisAttributes and
// GetFontAxisCount (and related) seem to incorrectly collapse multiple axes with the same tag.
// Since this is a limitation of the underlying implementation, for now allow the test to pass
// with the axis tag count (as opposed to the axis count). Eventually all implementations should
// pass this test without 'alsoAcceptedAxisTagCount'.
auto test = [&](SkTypeface* typeface, const Axis* expected, int expectedCount,
int alsoAcceptedAxisTagCount)
{
if (!typeface) {
return; // Not all SkFontMgr can makeFromStream().
}
int actualCount = typeface->getVariationDesignParameters(nullptr, 0);
if (actualCount == -1) {
return; // The number of axes is unknown.
}
REPORTER_ASSERT(reporter, actualCount == expectedCount ||
actualCount == alsoAcceptedAxisTagCount);
std::unique_ptr<Axis[]> actual(new Axis[actualCount]);
actualCount = typeface->getVariationDesignParameters(actual.get(), actualCount);
if (actualCount == -1) {
return; // The position cannot be determined.
}
REPORTER_ASSERT(reporter, actualCount == expectedCount ||
actualCount == alsoAcceptedAxisTagCount);
// Every actual must be expected.
std::unique_ptr<bool[]> expectedUsed(new bool[expectedCount]());
for (int actualIdx = 0; actualIdx < actualCount; ++actualIdx) {
bool actualFound = false;
for (int expectedIdx = 0; expectedIdx < expectedCount; ++expectedIdx) {
if (expectedUsed[expectedIdx]) {
continue;
}
if (actual[actualIdx].tag != expected[expectedIdx].tag) {
continue;
}
// Convert to fixed for "almost equal".
SkFixed fixedActualMin = SkScalarToFixed(actual[actualIdx].min);
SkFixed fixedExpectedMin = SkScalarToFixed(expected[expectedIdx].min);
if (!(SkTAbs(fixedActualMin - fixedExpectedMin) < 2)) {
continue;
}
SkFixed fixedActualMax = SkScalarToFixed(actual[actualIdx].max);
SkFixed fixedExpectedMax = SkScalarToFixed(expected[expectedIdx].max);
if (!(SkTAbs(fixedActualMax - fixedExpectedMax) < 2)) {
continue;
}
SkFixed fixedActualDefault = SkScalarToFixed(actual[actualIdx].def);
SkFixed fixedExpectedDefault = SkScalarToFixed(expected[expectedIdx].def);
if (!(SkTAbs(fixedActualDefault - fixedExpectedDefault) < 2)) {
continue;
}
// This seems silly, but allows MSAN to ensure that isHidden is initialized.
// In GDI or before macOS 10.12, Win10, or FreeType 2.8.1 API for hidden is missing.
if (actual[actualIdx].isHidden() &&
actual[actualIdx].isHidden() != expected[expectedIdx].isHidden())
{
continue;
}
// This actual matched an unused expected.
actualFound = true;
expectedUsed[expectedIdx] = true;
break;
}
REPORTER_ASSERT(reporter, actualFound,
"Actual axis '%c%c%c%c' with min %f max %f default %f hidden %s not expected",
(actual[actualIdx].tag >> 24) & 0xFF,
(actual[actualIdx].tag >> 16) & 0xFF,
(actual[actualIdx].tag >> 8) & 0xFF,
(actual[actualIdx].tag ) & 0xFF,
actual[actualIdx].min,
actual[actualIdx].def,
actual[actualIdx].max,
actual[actualIdx].isHidden() ? "true" : "false");
}
};
sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
// Two axis OpenType variable font.
{
std::unique_ptr<SkStreamAsset> variable(GetResourceAsStream("fonts/Variable.ttf"));
if (!variable) {
REPORT_FAILURE(reporter, "variable", SkString());
return;
}
constexpr Axis expected[] = {
Axis(SkSetFourByteTag('w','g','h','t'), 100.0f, 400.0f, 900.0f, true ),
Axis(SkSetFourByteTag('w','d','t','h'), 50.0f, 100.0f, 200.0f, false),
};
sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(variable), 0);
test(typeface.get(), &expected[0], SK_ARRAY_COUNT(expected), -1);
}
// Multiple axes with the same tag (and min, max, default) works.
{
std::unique_ptr<SkStreamAsset> dupTags(GetResourceAsStream("fonts/VaryAlongQuads.ttf"));
if (!dupTags) {
REPORT_FAILURE(reporter, "dupTags", SkString());
return;
}
// The position may be over specified. If there are multiple values for a given axis,
// ensure the last one since that's what css-fonts-4 requires.
constexpr Axis expected[] = {
Axis(SkSetFourByteTag('w','g','h','t'), 100.0f, 400.0f, 900.0f, false),
Axis(SkSetFourByteTag('w','g','h','t'), 100.0f, 400.0f, 900.0f, false),
};
sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(dupTags), 0);
test(typeface.get(), &expected[0], SK_ARRAY_COUNT(expected), 1);
}
// Simple single axis GX variable font.
{
std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
if (!distortable) {
REPORT_FAILURE(reporter, "distortable", SkString());
return;
}
constexpr Axis expected[] = {
Axis(SkSetFourByteTag('w','g','h','t'), 0.5f, 1.0f, 2.0f, true),
};
sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), 0);
test(typeface.get(), &expected[0], SK_ARRAY_COUNT(expected), -1);
}
}
static bool count_proc(SkTypeface* face, void* ctx) {
int* count = static_cast<int*>(ctx);
*count = *count + 1;
return false;
}
static int count(skiatest::Reporter* reporter, const SkTypefaceCache& cache) {
int count = 0;
sk_sp<SkTypeface> none = cache.findByProcAndRef(count_proc, &count);
REPORTER_ASSERT(reporter, none == nullptr);
return count;
}
DEF_TEST(TypefaceCache, reporter) {
sk_sp<SkTypeface> t1(TestEmptyTypeface::Make());
{
SkTypefaceCache cache;
REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
{
sk_sp<SkTypeface> t0(TestEmptyTypeface::Make());
cache.add(t0);
REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
cache.add(t1);
REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
cache.purgeAll();
REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
}
REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
cache.purgeAll();
REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
}
REPORTER_ASSERT(reporter, t1->unique());
}
static void check_serialize_behaviors(sk_sp<SkTypeface> tf, bool isLocalData,
skiatest::Reporter* reporter) {
if (!tf) {
return;
}
auto data0 = tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
auto data1 = tf->serialize(SkTypeface::SerializeBehavior::kDontIncludeData);
auto data2 = tf->serialize(SkTypeface::SerializeBehavior::kIncludeDataIfLocal);
REPORTER_ASSERT(reporter, data0->size() >= data1->size());
if (isLocalData) {
REPORTER_ASSERT(reporter, data0->equals(data2.get()));
} else {
REPORTER_ASSERT(reporter, data1->equals(data2.get()));
}
}
DEF_TEST(Typeface_serialize, reporter) {
check_serialize_behaviors(SkTypeface::MakeDefault(), false, reporter);
check_serialize_behaviors(SkTypeface::MakeFromStream(
GetResourceAsStream("fonts/Distortable.ttf")),
true, reporter);
}
DEF_TEST(Typeface_glyph_to_char, reporter) {
SkFont font(ToolUtils::emoji_typeface(), 12);
SkASSERT(font.getTypeface());
char const * text = ToolUtils::emoji_sample_text();
size_t const textLen = strlen(text);
size_t const codepointCount = SkUTF::CountUTF8(text, textLen);
char const * const textEnd = text + textLen;
std::unique_ptr<SkUnichar[]> originalCodepoints(new SkUnichar[codepointCount]);
for (size_t i = 0; i < codepointCount; ++i) {
originalCodepoints[i] = SkUTF::NextUTF8(&text, textEnd);
}
std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[codepointCount]);
font.unicharsToGlyphs(originalCodepoints.get(), codepointCount, glyphs.get());
std::unique_ptr<SkUnichar[]> newCodepoints(new SkUnichar[codepointCount]);
SkFontPriv::GlyphsToUnichars(font, glyphs.get(), codepointCount, newCodepoints.get());
SkString familyName;
font.getTypeface()->getFamilyName(&familyName);
for (size_t i = 0; i < codepointCount; ++i) {
#if defined(SK_BUILD_FOR_WIN)
// GDI does not support character to glyph mapping outside BMP.
if (gSkFontMgr_DefaultFactory == &SkFontMgr_New_GDI &&
0xFFFF < originalCodepoints[i] && newCodepoints[i] == 0)
{
continue;
}
#endif
// If two codepoints map to the same glyph then this assert is not valid.
// However, the emoji test font should never have multiple characters map to the same glyph.
REPORTER_ASSERT(reporter, originalCodepoints[i] == newCodepoints[i],
"name:%s i:%zu original:%d new:%d glyph:%d", familyName.c_str(), i,
originalCodepoints[i], newCodepoints[i], glyphs[i]);
}
}
// This test makes sure the legacy typeface creation does not lose its specified
// style. See https://bugs.chromium.org/p/skia/issues/detail?id=8447 for more
// context.
DEF_TEST(LegacyMakeTypeface, reporter) {
sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
sk_sp<SkTypeface> typeface1 = fm->legacyMakeTypeface(nullptr, SkFontStyle::Italic());
sk_sp<SkTypeface> typeface2 = fm->legacyMakeTypeface(nullptr, SkFontStyle::Bold());
sk_sp<SkTypeface> typeface3 = fm->legacyMakeTypeface(nullptr, SkFontStyle::BoldItalic());
REPORTER_ASSERT(reporter, typeface1->isItalic());
REPORTER_ASSERT(reporter, !typeface1->isBold());
REPORTER_ASSERT(reporter, !typeface2->isItalic());
REPORTER_ASSERT(reporter, typeface2->isBold());
REPORTER_ASSERT(reporter, typeface3->isItalic());
REPORTER_ASSERT(reporter, typeface3->isBold());
}