From 373b15bda93c8b400c5e1271bc93cbef26af503f Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Mon, 1 May 2017 22:17:36 -0700 Subject: [PATCH] Fix double bolding resolveStyle increases minikinStyle's weight value based on Skia's style value. Since we compute Skia's style based on given weight value, we should not add extra bold weight to minikinStyle. This CL also fixes misunderstanding of base weight. The base weight is only used for computing weight relative to the weighted alias. Thus, base weight should not be updated except for createWeightAlias method. To be clear, this CL changes the function names but keeps the same semantics as before. Test: adb shell /data/nativetest/hwui_unit_tests/hwui_unit_tests Test: am instrument -w -e class android.graphics.cts.TypefaceTest\ android.graphics.cts/android.support.test.runner.AndroidJUnitRunner Bug: 37880319 Merged-In: Ied73189b11792fb062da46f45afd2db664e6ecb4 Change-Id: I82350a1bfb99ce198b955f127949e21bccccb1cb --- core/jni/android/graphics/FontFamily.cpp | 3 - core/jni/android/graphics/Typeface.cpp | 17 +- graphics/java/android/graphics/Typeface.java | 8 +- libs/hwui/Android.mk | 1 + libs/hwui/hwui/Typeface.cpp | 75 ++-- libs/hwui/hwui/Typeface.h | 51 ++- libs/hwui/tests/unit/TypefaceTests.cpp | 418 +++++++++++++++++++ 7 files changed, 509 insertions(+), 64 deletions(-) create mode 100644 libs/hwui/tests/unit/TypefaceTests.cpp diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index fc90fb37ff7e6..9ad57b1a7d854 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -41,9 +41,6 @@ namespace android { -// Must be same with Java constant in Typeface.Builder. See Typeface.java -constexpr jint RESOLVE_BY_FONT_TABLE = -1; - struct NativeFamilyBuilder { NativeFamilyBuilder(uint32_t langId, int variant) : langId(langId), variant(variant), allowUnsupportedFont(false) {} diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp index 86c97a1e558c6..eb2ca5dab5162 100644 --- a/core/jni/android/graphics/Typeface.cpp +++ b/core/jni/android/graphics/Typeface.cpp @@ -30,14 +30,14 @@ using namespace android; static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandle, jint style) { Typeface* family = reinterpret_cast(familyHandle); - Typeface* face = Typeface::createFromTypeface(family, (SkTypeface::Style)style); + Typeface* face = Typeface::createRelative(family, (SkTypeface::Style)style); // TODO: the following logic shouldn't be necessary, the above should always succeed. // Try to find the closest matching font, using the standard heuristic if (NULL == face) { - face = Typeface::createFromTypeface(family, (SkTypeface::Style)(style ^ SkTypeface::kItalic)); + face = Typeface::createRelative(family, (SkTypeface::Style)(style ^ SkTypeface::kItalic)); } for (int i = 0; NULL == face && i < 4; i++) { - face = Typeface::createFromTypeface(family, (SkTypeface::Style)i); + face = Typeface::createRelative(family, (SkTypeface::Style)i); } return reinterpret_cast(face); } @@ -45,8 +45,7 @@ static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandl static jlong Typeface_createFromTypefaceWithExactStyle(JNIEnv* env, jobject, jlong nativeInstance, jint weight, jboolean italic) { Typeface* baseTypeface = reinterpret_cast(nativeInstance); - return reinterpret_cast( - Typeface::createFromTypefaceWithStyle(baseTypeface, weight, italic)); + return reinterpret_cast(Typeface::createAbsolute(baseTypeface, weight, italic)); } static jlong Typeface_createFromTypefaceWithVariation(JNIEnv* env, jobject, jlong familyHandle, @@ -68,7 +67,7 @@ static jlong Typeface_createFromTypefaceWithVariation(JNIEnv* env, jobject, jlon static jlong Typeface_createWeightAlias(JNIEnv* env, jobject, jlong familyHandle, jint weight) { Typeface* family = reinterpret_cast(familyHandle); - Typeface* face = Typeface::createWeightAlias(family, weight); + Typeface* face = Typeface::createWithDifferentBaseWeight(family, weight); return reinterpret_cast(face); } @@ -82,9 +81,9 @@ static jint Typeface_getStyle(JNIEnv* env, jobject obj, jlong faceHandle) { return face->fSkiaStyle; } -static jint Typeface_getBaseWeight(JNIEnv* env, jobject obj, jlong faceHandle) { +static jint Typeface_getWeight(JNIEnv* env, jobject obj, jlong faceHandle) { Typeface* face = reinterpret_cast(faceHandle); - return face->fBaseWeight; + return face->fStyle.getWeight() * 100; } static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray, @@ -134,7 +133,7 @@ static const JNINativeMethod gTypefaceMethods[] = { { "nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias }, { "nativeUnref", "(J)V", (void*)Typeface_unref }, { "nativeGetStyle", "(J)I", (void*)Typeface_getStyle }, - { "nativeGetBaseWeight", "(J)I", (void*)Typeface_getBaseWeight }, + { "nativeGetWeight", "(J)I", (void*)Typeface_getWeight }, { "nativeCreateFromArray", "([JII)J", (void*)Typeface_createFromArray }, { "nativeSetDefault", "(J)V", (void*)Typeface_setDefault }, diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 79898bc648a08..5a56f53353733 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -123,7 +123,7 @@ public class Typeface { public static final int BOLD_ITALIC = 3; private int mStyle = 0; - private int mBaseWeight = 0; + private int mWeight = 0; // Value for weight and italic. Indicates the value is resolved by font metadata. // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp @@ -544,7 +544,7 @@ public class Typeface { return base; } - final int weight = (mWeight == RESOLVE_BY_FONT_TABLE) ? base.mBaseWeight : mWeight; + final int weight = (mWeight == RESOLVE_BY_FONT_TABLE) ? base.mWeight : mWeight; final boolean italic = (mItalic == RESOLVE_BY_FONT_TABLE) ? (base.mStyle & ITALIC) != 0 : mItalic == 1; final int key = weight << 1 | (italic ? 1 : 0); @@ -882,7 +882,7 @@ public class Typeface { native_instance = ni; mStyle = nativeGetStyle(ni); - mBaseWeight = nativeGetBaseWeight(ni); + mWeight = nativeGetWeight(ni); } private static FontFamily makeFamilyFromParsed(FontConfig.Family family, @@ -1068,7 +1068,7 @@ public class Typeface { private static native long nativeCreateWeightAlias(long native_instance, int weight); private static native void nativeUnref(long native_instance); private static native int nativeGetStyle(long native_instance); - private static native int nativeGetBaseWeight(long native_instance); + private static native int nativeGetWeight(long native_instance); private static native long nativeCreateFromArray(long[] familyArray, int weight, int italic); private static native void nativeSetDefault(long native_instance); private static native int[] nativeGetSupportedAxes(long native_instance); diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 5ef49dc5fecd4..030e8456f01cd 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -319,6 +319,7 @@ LOCAL_SRC_FILES += \ tests/unit/TestUtilsTests.cpp \ tests/unit/TextDropShadowCacheTests.cpp \ tests/unit/TextureCacheTests.cpp \ + tests/unit/TypefaceTests.cpp \ tests/unit/VectorDrawableTests.cpp \ include $(LOCAL_PATH)/hwui_static_deps.mk diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index 4fb4b53346e02..f66bb045373c2 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -36,28 +36,33 @@ #include #include #include +#include namespace android { -// This indicates that the passed information should be resolved by OS/2 table. -// This value must be the same as the android.graphics.Typeface$Builder.RESOLVE_BY_FONT_TABLE. -constexpr int RESOLVE_BY_FONT_TABLE = -1; +static SkTypeface::Style computeSkiaStyle(int weight, bool italic) { + // This bold detection comes from SkTypeface.h + if (weight >= SkFontStyle::kSemiBold_Weight) { + return italic ? SkTypeface::kBoldItalic : SkTypeface::kBold; + } else { + return italic ? SkTypeface::kItalic : SkTypeface::kNormal; + } +} -// Resolve the 1..10 weight based on base weight and bold flag -static void resolveStyle(Typeface* typeface) { +static minikin::FontStyle computeMinikinStyle(int weight, bool italic) { // TODO: Better to use raw base weight value for font selection instead of dividing by 100. - int weight = (typeface->fBaseWeight + 50) / 100; - if (typeface->fSkiaStyle & SkTypeface::kBold) { - weight += 3; + const int minikinWeight = uirenderer::MathUtils::clamp((weight + 50) / 100, 1, 10); + return minikin::FontStyle(minikinWeight, italic); +} + +// Resolve the relative weight from the baseWeight and target style. +static minikin::FontStyle computeRelativeStyle(int baseWeight, SkTypeface::Style relativeStyle) { + int weight = baseWeight; + if ((relativeStyle & SkTypeface::kBold) != 0) { + weight += 300; } - if (weight > 10) { - weight = 10; - } - if (weight < 1) { - weight = 1; - } - bool italic = (typeface->fSkiaStyle & SkTypeface::kItalic) != 0; - typeface->fStyle = minikin::FontStyle(weight, italic); + bool italic = (relativeStyle & SkTypeface::kItalic) != 0; + return computeMinikinStyle(weight, italic); } Typeface* gDefaultTypeface = NULL; @@ -67,26 +72,26 @@ Typeface* Typeface::resolveDefault(Typeface* src) { return src == nullptr ? gDefaultTypeface : src; } -Typeface* Typeface::createFromTypeface(Typeface* src, SkTypeface::Style style) { +Typeface* Typeface::createRelative(Typeface* src, SkTypeface::Style style) { Typeface* resolvedFace = Typeface::resolveDefault(src); Typeface* result = new Typeface; if (result != nullptr) { result->fFontCollection = resolvedFace->fFontCollection; - result->fSkiaStyle = style; result->fBaseWeight = resolvedFace->fBaseWeight; - resolveStyle(result); + result->fSkiaStyle = style; + result->fStyle = computeRelativeStyle(result->fBaseWeight, style); } return result; } -Typeface* Typeface::createFromTypefaceWithStyle(Typeface* base, int weight, bool italic) { +Typeface* Typeface::createAbsolute(Typeface* base, int weight, bool italic) { Typeface* resolvedFace = Typeface::resolveDefault(base); Typeface* result = new Typeface(); if (result != nullptr) { result->fFontCollection = resolvedFace->fFontCollection; - result->fBaseWeight = weight; - result->fStyle = minikin::FontStyle(weight / 100, italic); - result->fSkiaStyle = resolvedFace->fSkiaStyle; + result->fBaseWeight = resolvedFace->fBaseWeight; + result->fSkiaStyle = computeSkiaStyle(weight, italic); + result->fStyle = computeMinikinStyle(weight, italic); } return result; } @@ -103,21 +108,23 @@ Typeface* Typeface::createFromTypefaceWithVariation(Typeface* src, // So we will reuse the same collection with incrementing reference count. result->fFontCollection = resolvedFace->fFontCollection; } - result->fSkiaStyle = resolvedFace->fSkiaStyle; + // Do not update styles. + // TODO: We may want to update base weight if the 'wght' is specified. result->fBaseWeight = resolvedFace->fBaseWeight; - resolveStyle(result); + result->fSkiaStyle = resolvedFace->fSkiaStyle; + result->fStyle = resolvedFace->fStyle; } return result; } -Typeface* Typeface::createWeightAlias(Typeface* src, int weight) { +Typeface* Typeface::createWithDifferentBaseWeight(Typeface* src, int weight) { Typeface* resolvedFace = Typeface::resolveDefault(src); Typeface* result = new Typeface; if (result != nullptr) { result->fFontCollection = resolvedFace->fFontCollection; - result->fSkiaStyle = resolvedFace->fSkiaStyle; result->fBaseWeight = weight; - resolveStyle(result); + result->fSkiaStyle = resolvedFace->fSkiaStyle; + result->fStyle = computeRelativeStyle(weight, result->fSkiaStyle); } return result; } @@ -160,14 +167,8 @@ Typeface* Typeface::createFromFamilies( } result->fBaseWeight = weight; - // This bold detection comes from SkTypefae.h - const bool isBold = weight >= SkFontStyle::kSemiBold_Weight; - const bool isItalic = italic == 1; - // TODO: remove fSkiaStyle - result->fSkiaStyle = isBold ? - (isItalic ? SkTypeface::kBoldItalic : SkTypeface::kBold) : - (isItalic ? SkTypeface::kItalic : SkTypeface::kNormal); - resolveStyle(result); + result->fSkiaStyle = computeSkiaStyle(weight, italic); + result->fStyle = computeMinikinStyle(weight, italic); return result; } @@ -197,7 +198,7 @@ void Typeface::setRobotoTypefaceForTest() { Typeface* hwTypeface = new Typeface(); hwTypeface->fFontCollection = collection; hwTypeface->fSkiaStyle = SkTypeface::kNormal; - hwTypeface->fBaseWeight = SkFontStyle::kSemiBold_Weight; + hwTypeface->fBaseWeight = SkFontStyle::kNormal_Weight; hwTypeface->fStyle = minikin::FontStyle(4 /* weight */, false /* italic */); Typeface::setDefault(hwTypeface); diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h index e35a7b46c4b22..db0b2cdeba002 100644 --- a/libs/hwui/hwui/Typeface.h +++ b/libs/hwui/hwui/Typeface.h @@ -27,28 +27,53 @@ namespace android { -struct ANDROID_API Typeface { - std::shared_ptr fFontCollection; +// This indicates that the weight or italic information should be resolved by OS/2 table. +// This value must be the same as the android.graphics.Typeface$Builder.RESOLVE_BY_FONT_TABLE. +constexpr int RESOLVE_BY_FONT_TABLE = -1; - // style used for constructing and querying Typeface objects - SkTypeface::Style fSkiaStyle; - // base weight in CSS-style units, 100..900 - int fBaseWeight; +struct ANDROID_API Typeface { + public: + std::shared_ptr fFontCollection; // resolved style actually used for rendering minikin::FontStyle fStyle; + // style used for constructing and querying Typeface objects + SkTypeface::Style fSkiaStyle; + static Typeface* resolveDefault(Typeface* src); - static Typeface* createFromTypeface(Typeface* src, SkTypeface::Style style); - - static Typeface* createFromTypefaceWithStyle(Typeface* base, int weight, bool italic); + // The following three functions create new Typeface from an existing Typeface with a different + // style. There is a base weight concept which is used for calculating relative style from an + // existing Typeface. + // The createRelative method creates a new Typeface with a style relative to the base Typeface. + // For example, if the base Typeface has a base weight of 400 and the desired style is bold, the + // resulting Typeface renders the text with a weight of 700. This function doesn't change the + // base weight, so even if you create a new Typeface from the bold Typeface specifying bold on + // it again, the text is still rendered with a weight of 700. + // You can create another base weight Typeface from an existing Typeface with + // createWithDifferentBaseWeight. The Typeface created with this function renders the text with + // a specified base weight. + // The createAbsolute method creates a new Typeface ignoring the base weight. + // Here is an example: + // Typeface* base = resolveDefault(nullptr); // Usually this has a weight of 400. + // Typeface* bold = createRelative(base, Bold); // Rendered with a weight of 700. + // Typeface* bold2 = createRelative(bold, Bold); // Rendered with a weight of 700. + // + // Typeface* boldBase = createWithDifferentBaseWeight(base, 700); // With a weight of 700. + // Typeface* boldBold = createRelative(boldBase, Bold); // Rendered with a weight of 1000. + // + // Typeface* lightBase = createWithDifferentBaseWeight(base, 300); // With a weight of 300. + // Typeface* lightBold = createRelative(lightBase, Bold); // Rendered with a weight of 600. + // + // Typeface* black = createAbsolute(base, 900, false); // Rendered with a weight of 900. + static Typeface* createWithDifferentBaseWeight(Typeface* src, int baseweight); + static Typeface* createRelative(Typeface* src, SkTypeface::Style desiredStyle); + static Typeface* createAbsolute(Typeface* base, int weight, bool italic); static Typeface* createFromTypefaceWithVariation(Typeface* src, const std::vector& variations); - static Typeface* createWeightAlias(Typeface* src, int baseweight); - static Typeface* createFromFamilies( std::vector>&& families, int weight, int italic); @@ -57,6 +82,10 @@ struct ANDROID_API Typeface { // Sets roboto font as the default typeface for testing purpose. static void setRobotoTypefaceForTest(); + private: + // base weight in CSS-style units, 1..1000 + int fBaseWeight; + }; } diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp new file mode 100644 index 0000000000000..c90b6f0e9cddf --- /dev/null +++ b/libs/hwui/tests/unit/TypefaceTests.cpp @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include "SkFontMgr.h" +#include "SkStream.h" + +#include "hwui/MinikinSkia.h" +#include "hwui/Typeface.h" + +using namespace android; + +namespace { + +constexpr char kRobotoRegular[] = "/system/fonts/Roboto-Regular.ttf"; +constexpr char kRobotoBold[] = "/system/fonts/Roboto-Bold.ttf"; +constexpr char kRobotoItalic[] = "/system/fonts/Roboto-Italic.ttf"; +constexpr char kRobotoBoldItalic[] = "/system/fonts/Roboto-BoldItalic.ttf"; + +void unmap(const void* ptr, void* context) { + void* p = const_cast(ptr); + size_t len = reinterpret_cast(context); + munmap(p, len); +} + +std::shared_ptr buildFamily(const char* fileName) { + int fd = open(fileName, O_RDONLY); + LOG_ALWAYS_FATAL_IF(fd == -1, "Failed to open file %s", fileName); + struct stat st = {}; + LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", fileName); + void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + sk_sp skData = + SkData::MakeWithProc(data, st.st_size, unmap, reinterpret_cast(st.st_size)); + std::unique_ptr fontData(new SkMemoryStream(skData)); + sk_sp fm(SkFontMgr::RefDefault()); + sk_sp typeface(fm->createFromStream(fontData.release())); + LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName); + std::shared_ptr font = std::make_shared( + std::move(typeface), data, st.st_size, 0, std::vector()); + return std::make_shared( + std::vector({ minikin::Font(std::move(font), minikin::FontStyle()) })); +} + +std::vector> makeSingleFamlyVector(const char* fileName) { + return std::vector>({ buildFamily(fileName) }); +} + +TEST(TypefaceTest, resolveDefault_and_setDefaultTest) { + std::unique_ptr regular( + Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + EXPECT_EQ(regular.get(), Typeface::resolveDefault(regular.get())); + + Typeface* old = Typeface::resolveDefault(nullptr); // Keep the original to restore it later. + ASSERT_NE(nullptr, old); + + Typeface::setDefault(regular.get()); + EXPECT_EQ(regular.get(), Typeface::resolveDefault(nullptr)); + + Typeface::setDefault(old); // Restore to the original. +} + +TEST(TypefaceTest, createWithDifferentBaseWeight) { + std::unique_ptr bold(Typeface::createWithDifferentBaseWeight(nullptr, 700)); + EXPECT_EQ(7, bold->fStyle.getWeight()); + EXPECT_FALSE(bold->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kNormal, bold->fSkiaStyle); + + std::unique_ptr light(Typeface::createWithDifferentBaseWeight(nullptr, 300)); + EXPECT_EQ(3, light->fStyle.getWeight()); + EXPECT_FALSE(light->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kNormal, light->fSkiaStyle); +} + +TEST(TypefaceTest, createRelativeTest_fromRegular) { + // In Java, Typeface.create(Typeface.DEFAULT, Typeface.NORMAL); + std::unique_ptr normal(Typeface::createRelative(nullptr, SkTypeface::kNormal)); + EXPECT_EQ(4, normal->fStyle.getWeight()); + EXPECT_FALSE(normal->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle); + + // In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD); + std::unique_ptr bold(Typeface::createRelative(nullptr, SkTypeface::kBold)); + EXPECT_EQ(7, bold->fStyle.getWeight()); + EXPECT_FALSE(bold->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + + // In Java, Typeface.create(Typeface.DEFAULT, Typeface.ITALIC); + std::unique_ptr italic(Typeface::createRelative(nullptr, SkTypeface::kItalic)); + EXPECT_EQ(4, italic->fStyle.getWeight()); + EXPECT_TRUE(italic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + + // In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC); + std::unique_ptr boldItalic( + Typeface::createRelative(nullptr, SkTypeface::kBoldItalic)); + EXPECT_EQ(7, boldItalic->fStyle.getWeight()); + EXPECT_TRUE(boldItalic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); +} + +TEST(TypefaceTest, createRelativeTest_BoldBase) { + std::unique_ptr base(Typeface::createWithDifferentBaseWeight(nullptr, 700)); + + // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.NORMAL); + std::unique_ptr normal(Typeface::createRelative(base.get(), SkTypeface::kNormal)); + EXPECT_EQ(7, normal->fStyle.getWeight()); + EXPECT_FALSE(normal->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle); + + // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.BOLD); + std::unique_ptr bold(Typeface::createRelative(base.get(), SkTypeface::kBold)); + EXPECT_EQ(10, bold->fStyle.getWeight()); + EXPECT_FALSE(bold->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + + // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.ITALIC); + std::unique_ptr italic(Typeface::createRelative(base.get(), SkTypeface::kItalic)); + EXPECT_EQ(7, italic->fStyle.getWeight()); + EXPECT_TRUE(italic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + + // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.BOLD_ITALIC); + std::unique_ptr + boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic)); + EXPECT_EQ(10, boldItalic->fStyle.getWeight()); + EXPECT_TRUE(boldItalic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); +} + +TEST(TypefaceTest, createRelativeTest_LightBase) { + std::unique_ptr base(Typeface::createWithDifferentBaseWeight(nullptr, 300)); + + // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.NORMAL); + std::unique_ptr normal(Typeface::createRelative(base.get(), SkTypeface::kNormal)); + EXPECT_EQ(3, normal->fStyle.getWeight()); + EXPECT_FALSE(normal->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle); + + // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.BOLD); + std::unique_ptr bold(Typeface::createRelative(base.get(), SkTypeface::kBold)); + EXPECT_EQ(6, bold->fStyle.getWeight()); + EXPECT_FALSE(bold->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + + // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.ITLIC); + std::unique_ptr italic(Typeface::createRelative(base.get(), SkTypeface::kItalic)); + EXPECT_EQ(3, italic->fStyle.getWeight()); + EXPECT_TRUE(italic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + + // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.BOLD_ITALIC); + std::unique_ptr + boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic)); + EXPECT_EQ(6, boldItalic->fStyle.getWeight()); + EXPECT_TRUE(boldItalic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); +} + +TEST(TypefaceTest, createRelativeTest_fromBoldStyled) { + std::unique_ptr base(Typeface::createRelative(nullptr, SkTypeface::kBold)); + + // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.NORMAL); + std::unique_ptr normal(Typeface::createRelative(base.get(), SkTypeface::kNormal)); + EXPECT_EQ(4, normal->fStyle.getWeight()); + EXPECT_FALSE(normal->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle); + + // In Java Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.BOLD); + std::unique_ptr bold(Typeface::createRelative(base.get(), SkTypeface::kBold)); + EXPECT_EQ(7, bold->fStyle.getWeight()); + EXPECT_FALSE(bold->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + + // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.ITALIC); + std::unique_ptr italic(Typeface::createRelative(base.get(), SkTypeface::kItalic)); + EXPECT_EQ(4, normal->fStyle.getWeight()); + EXPECT_TRUE(italic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + + // In Java, + // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.BOLD_ITALIC); + std::unique_ptr + boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic)); + EXPECT_EQ(7, boldItalic->fStyle.getWeight()); + EXPECT_TRUE(boldItalic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); +} + +TEST(TypefaceTest, createRelativeTest_fromItalicStyled) { + std::unique_ptr base(Typeface::createRelative(nullptr, SkTypeface::kItalic)); + + // In Java, + // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.NORMAL); + std::unique_ptr normal(Typeface::createRelative(base.get(), SkTypeface::kNormal)); + EXPECT_EQ(4, normal->fStyle.getWeight()); + EXPECT_FALSE(normal->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle); + + // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.BOLD); + std::unique_ptr bold(Typeface::createRelative(base.get(), SkTypeface::kBold)); + EXPECT_EQ(7, bold->fStyle.getWeight()); + EXPECT_FALSE(bold->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + + // In Java, + // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.ITALIC); + std::unique_ptr italic(Typeface::createRelative(base.get(), SkTypeface::kItalic)); + EXPECT_EQ(4, italic->fStyle.getWeight()); + EXPECT_TRUE(italic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + + // In Java, + // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.BOLD_ITALIC); + std::unique_ptr + boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic)); + EXPECT_EQ(7, boldItalic->fStyle.getWeight()); + EXPECT_TRUE(boldItalic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); +} + +TEST(TypefaceTest, createRelativeTest_fromSpecifiedStyled) { + std::unique_ptr base(Typeface::createAbsolute(nullptr, 400, false)); + + // In Java, + // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif") + // .setWeight(700).setItalic(false).build(); + // Typeface.create(typeface, Typeface.NORMAL); + std::unique_ptr normal(Typeface::createRelative(base.get(), SkTypeface::kNormal)); + EXPECT_EQ(4, normal->fStyle.getWeight()); + EXPECT_FALSE(normal->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle); + + // In Java, + // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif") + // .setWeight(700).setItalic(false).build(); + // Typeface.create(typeface, Typeface.BOLD); + std::unique_ptr bold(Typeface::createRelative(base.get(), SkTypeface::kBold)); + EXPECT_EQ(7, bold->fStyle.getWeight()); + EXPECT_FALSE(bold->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + + // In Java, + // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif") + // .setWeight(700).setItalic(false).build(); + // Typeface.create(typeface, Typeface.ITALIC); + std::unique_ptr italic(Typeface::createRelative(base.get(), SkTypeface::kItalic)); + EXPECT_EQ(4, italic->fStyle.getWeight()); + EXPECT_TRUE(italic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + + // In Java, + // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif") + // .setWeight(700).setItalic(false).build(); + // Typeface.create(typeface, Typeface.BOLD_ITALIC); + std::unique_ptr + boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic)); + EXPECT_EQ(7, boldItalic->fStyle.getWeight()); + EXPECT_TRUE(boldItalic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); +} + +TEST(TypefaceTest, createAbsolute) { + // In Java, + // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(false) + // .build(); + std::unique_ptr regular(Typeface::createAbsolute(nullptr, 400, false)); + EXPECT_EQ(4, regular->fStyle.getWeight()); + EXPECT_FALSE(regular->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle); + + // In Java, + // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(false) + // .build(); + std::unique_ptr bold(Typeface::createAbsolute(nullptr, 700, false)); + EXPECT_EQ(7, bold->fStyle.getWeight()); + EXPECT_FALSE(bold->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + + // In Java, + // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(true) + // .build(); + std::unique_ptr italic(Typeface::createAbsolute(nullptr, 400, true)); + EXPECT_EQ(4, italic->fStyle.getWeight()); + EXPECT_TRUE(italic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + + // In Java, + // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(true) + // .build(); + std::unique_ptr boldItalic(Typeface::createAbsolute(nullptr, 700, true)); + EXPECT_EQ(7, boldItalic->fStyle.getWeight()); + EXPECT_TRUE(boldItalic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); + + // In Java, + // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(1100).setItalic(true) + // .build(); + std::unique_ptr over1000(Typeface::createAbsolute(nullptr, 1100, false)); + EXPECT_EQ(10, over1000->fStyle.getWeight()); + EXPECT_FALSE(over1000->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBold, over1000->fSkiaStyle); +} + +TEST(TypefaceTest, createFromFamilies_Single) { + // In Java, new Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(false).build(); + std::unique_ptr regular( + Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), 400, false)); + EXPECT_EQ(4, regular->fStyle.getWeight()); + EXPECT_FALSE(regular->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle); + + // In Java, new Typeface.Builder("Roboto-Bold.ttf").setWeight(700).setItalic(false).build(); + std::unique_ptr bold( + Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 700, false)); + EXPECT_EQ(7, bold->fStyle.getWeight()); + EXPECT_FALSE(bold->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + + // In Java, new Typeface.Builder("Roboto-Italic.ttf").setWeight(400).setItalic(true).build(); + std::unique_ptr italic( + Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoItalic), 400, true)); + EXPECT_EQ(4, italic->fStyle.getWeight()); + EXPECT_TRUE(italic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + + // In Java, + // new Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(700).setItalic(true).build(); + std::unique_ptr boldItalic( + Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic), 700, true)); + EXPECT_EQ(7, boldItalic->fStyle.getWeight()); + EXPECT_TRUE(boldItalic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + + // In Java, + // new Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(1100).setItalic(false).build(); + std::unique_ptr over1000( + Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 1100, false)); + EXPECT_EQ(10, over1000->fStyle.getWeight()); + EXPECT_FALSE(over1000->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBold, over1000->fSkiaStyle); +} + +TEST(TypefaceTest, createFromFamilies_Single_resolveByTable) { + // In Java, new Typeface.Builder("Roboto-Regular.ttf").build(); + std::unique_ptr regular( + Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + EXPECT_EQ(4, regular->fStyle.getWeight()); + EXPECT_FALSE(regular->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle); + + // In Java, new Typeface.Builder("Roboto-Bold.ttf").build(); + std::unique_ptr bold( + Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + EXPECT_EQ(7, bold->fStyle.getWeight()); + EXPECT_FALSE(bold->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + + // In Java, new Typeface.Builder("Roboto-Italic.ttf").build(); + std::unique_ptr italic( + Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoItalic), + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + EXPECT_EQ(4, italic->fStyle.getWeight()); + EXPECT_TRUE(italic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + + // In Java, new Typeface.Builder("Roboto-BoldItalic.ttf").build(); + std::unique_ptr boldItalic( + Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic), + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + EXPECT_EQ(7, boldItalic->fStyle.getWeight()); + EXPECT_TRUE(boldItalic->fStyle.getItalic()); + EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); +} + +TEST(TypefaceTest, createFromFamilies_Family) { + std::vector> families = { + buildFamily(kRobotoRegular), buildFamily(kRobotoBold), buildFamily(kRobotoItalic), + buildFamily(kRobotoBoldItalic) + }; + std::unique_ptr typeface(Typeface::createFromFamilies(std::move(families), + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + EXPECT_EQ(4, typeface->fStyle.getWeight()); + EXPECT_FALSE(typeface->fStyle.getItalic()); +} + +TEST(TypefaceTest, createFromFamilies_Family_withoutRegular) { + std::vector> families = { + buildFamily(kRobotoBold), buildFamily(kRobotoItalic), buildFamily(kRobotoBoldItalic) + }; + std::unique_ptr typeface(Typeface::createFromFamilies(std::move(families), + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + EXPECT_EQ(7, typeface->fStyle.getWeight()); + EXPECT_FALSE(typeface->fStyle.getItalic()); +} + +} // namespace