Loading core/java/android/text/FontConfig.java +12 −2 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.FontListParser; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; Loading Loading @@ -106,8 +107,17 @@ public final class FontConfig implements Parcelable { private final float mStyleValue; public Axis(int tag, float styleValue) { this.mTag = tag; this.mStyleValue = styleValue; mTag = tag; mStyleValue = styleValue; } /** @hide */ public Axis(@NonNull String tagString, float styleValue) { if (!FontListParser.isValidTag(tagString)) { throw new IllegalArgumentException("Invalid tag pattern: " + tagString); } mTag = FontListParser.makeTag(tagString); mStyleValue = styleValue; } /** Loading core/jni/android/graphics/FontFamily.cpp +65 −70 Original line number Diff line number Diff line Loading @@ -34,12 +34,16 @@ #include <hwui/MinikinSkia.h> #include <hwui/Typeface.h> #include <utils/FatVector.h> #include <minikin/FontFamily.h> #include <memory> namespace android { // Must be same with Java constant in Typeface.Builder. See Typeface.java constexpr jint RESOLVE_BY_FONT_TABLE = -1; struct NativeFamilyBuilder { uint32_t langId; int variant; Loading Loading @@ -81,24 +85,53 @@ static void FontFamily_unref(jlong familyPtr) { delete family; } static void addSkTypeface(jlong builderPtr, sk_sp<SkTypeface> face, const void* fontData, size_t fontSize, int ttcIndex, jint givenWeight, jboolean givenItalic) { static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex, jint givenWeight, jint givenItalic) { uirenderer::FatVector<SkFontMgr::FontParameters::Axis, 2> skiaAxes; for (const auto& axis : builder->axes) { skiaAxes.emplace_back(SkFontMgr::FontParameters::Axis{axis.axisTag, axis.value}); } const size_t fontSize = data->size(); const void* fontPtr = data->data(); std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data))); SkFontMgr::FontParameters params; params.setCollectionIndex(ttcIndex); params.setAxes(skiaAxes.data(), skiaAxes.size()); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), params)); if (face == NULL) { ALOGE("addFont failed to create font, invalid request"); builder->axes.clear(); return false; } std::shared_ptr<minikin::MinikinFont> minikinFont = std::make_shared<MinikinFontSkia>(std::move(face), fontData, fontSize, ttcIndex, std::vector<minikin::FontVariation>()); NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex, builder->axes); int weight = givenWeight / 100; bool italic = givenItalic; if (weight == 0) { if (!minikin::FontFamily::analyzeStyle(minikinFont, &weight, &italic)) { bool italic = givenItalic == 1; if (givenWeight == RESOLVE_BY_FONT_TABLE || givenItalic == RESOLVE_BY_FONT_TABLE) { int os2Weight; bool os2Italic; if (!minikin::FontFamily::analyzeStyle(minikinFont, &os2Weight, &os2Italic)) { ALOGE("analyzeStyle failed. Using default style"); weight = 4; italic = false; os2Weight = 4; os2Italic = false; } if (givenWeight == RESOLVE_BY_FONT_TABLE) { weight = os2Weight; } if (givenItalic == RESOLVE_BY_FONT_TABLE) { italic = os2Italic; } } builder->fonts.push_back(minikin::Font( std::move(minikinFont), minikin::FontStyle(weight, italic))); builder->fonts.push_back(minikin::Font(minikinFont, minikin::FontStyle(weight, italic))); builder->axes.clear(); return true; } static void release_global_ref(const void* /*data*/, void* context) { Loading @@ -125,80 +158,47 @@ static void release_global_ref(const void* /*data*/, void* context) { } static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong builderPtr, jobject bytebuf, jint ttcIndex) { jint ttcIndex, jint weight, jint isItalic) { NPE_CHECK_RETURN_ZERO(env, bytebuf); NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); const void* fontPtr = env->GetDirectBufferAddress(bytebuf); if (fontPtr == NULL) { ALOGE("addFont failed to create font, buffer invalid"); builder->axes.clear(); return false; } jlong fontSize = env->GetDirectBufferCapacity(bytebuf); if (fontSize < 0) { ALOGE("addFont failed to create font, buffer size invalid"); builder->axes.clear(); return false; } jobject fontRef = MakeGlobalRefOrDie(env, bytebuf); sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize, release_global_ref, reinterpret_cast<void*>(fontRef))); std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data))); SkFontMgr::FontParameters params; params.setCollectionIndex(ttcIndex); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), params)); if (face == NULL) { ALOGE("addFont failed to create font"); return false; } addSkTypeface(builderPtr, std::move(face), fontPtr, (size_t)fontSize, ttcIndex, 0, false); return true; return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong builderPtr, jobject font, jint ttcIndex, jint weight, jboolean isItalic) { jobject font, jint ttcIndex, jint weight, jint isItalic) { NPE_CHECK_RETURN_ZERO(env, font); NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); // Declare axis native type. std::vector<SkFontMgr::FontParameters::Axis> skiaAxes; skiaAxes.reserve(builder->axes.size()); for (const minikin::FontVariation& minikinAxis : builder->axes) { skiaAxes.push_back({minikinAxis.axisTag, minikinAxis.value}); } const void* fontPtr = env->GetDirectBufferAddress(font); if (fontPtr == NULL) { ALOGE("addFont failed to create font, buffer invalid"); builder->axes.clear(); return false; } jlong fontSize = env->GetDirectBufferCapacity(font); if (fontSize < 0) { ALOGE("addFont failed to create font, buffer size invalid"); builder->axes.clear(); return false; } jobject fontRef = MakeGlobalRefOrDie(env, font); sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize, release_global_ref, reinterpret_cast<void*>(fontRef))); std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data))); SkFontMgr::FontParameters params; params.setCollectionIndex(ttcIndex); params.setAxes(skiaAxes.data(), skiaAxes.size()); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), params)); if (face == NULL) { ALOGE("addFont failed to create font, invalid request"); return false; } std::shared_ptr<minikin::MinikinFont> minikinFont = std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex, std::vector<minikin::FontVariation>()); builder->fonts.push_back(minikin::Font(std::move(minikinFont), minikin::FontStyle(weight / 100, isItalic))); return true; return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } static void releaseAsset(const void* ptr, void* context) { Loading @@ -206,18 +206,21 @@ static void releaseAsset(const void* ptr, void* context) { } static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong builderPtr, jobject jassetMgr, jstring jpath, jint cookie, jboolean isAsset, jint weight, jboolean isItalic) { jobject jassetMgr, jstring jpath, jint cookie, jboolean isAsset, jint ttcIndex, jint weight, jint isItalic) { NPE_CHECK_RETURN_ZERO(env, jassetMgr); NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; } ScopedUtfChars str(env, jpath); if (str.c_str() == nullptr) { builder->axes.clear(); return false; } Loading @@ -230,27 +233,19 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b } if (NULL == asset) { builder->axes.clear(); return false; } const void* buf = asset->getBuffer(false); if (NULL == buf) { delete asset; builder->axes.clear(); return false; } size_t bufSize = asset->getLength(); sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data))); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), SkFontMgr::FontParameters())); if (face == NULL) { ALOGE("addFontFromAsset failed to create font %s", str.c_str()); return false; } addSkTypeface(builderPtr, std::move(face), buf, bufSize, 0 /* ttc index */, weight, isItalic); addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); return true; } Loading @@ -266,10 +261,10 @@ static const JNINativeMethod gFontFamilyMethods[] = { { "nCreateFamily", "(J)J", (void*)FontFamily_create }, { "nAbort", "(J)V", (void*)FontFamily_abort }, { "nUnrefFamily", "(J)V", (void*)FontFamily_unref }, { "nAddFont", "(JLjava/nio/ByteBuffer;I)Z", (void*)FontFamily_addFont }, { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;IIZ)Z", { "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont }, { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFontWeightStyle }, { "nAddFontFromAssetManager", "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZIZ)Z", { "nAddFontFromAssetManager", "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZIII)Z", (void*)FontFamily_addFontFromAssetManager }, { "nAddAxisValue", "(JIF)V", (void*)FontFamily_addAxisValue }, }; Loading core/tests/coretests/src/android/graphics/VariationParserTest.java +8 −8 Original line number Diff line number Diff line Loading @@ -28,7 +28,7 @@ public class VariationParserTest extends TestCase { @SmallTest public void testParseFontVariationSetting() { int tag = FontListParser.makeTag('w', 'd', 't', 'h'); int tag = FontListParser.makeTag("wdth"); List<FontConfig.Axis> axes = FontListParser.parseFontVariationSettings("'wdth' 1"); assertEquals(tag, axes.get(0).getTag()); assertEquals(1.0f, axes.get(0).getStyleValue()); Loading @@ -45,7 +45,7 @@ public class VariationParserTest extends TestCase { assertEquals(tag, axes.get(0).getTag()); assertEquals(0.5f, axes.get(0).getStyleValue()); tag = FontListParser.makeTag('A', 'X', ' ', ' '); tag = FontListParser.makeTag("AX "); axes = FontListParser.parseFontVariationSettings("'AX ' 1"); assertEquals(tag, axes.get(0).getTag()); assertEquals(1.0f, axes.get(0).getStyleValue()); Loading Loading @@ -93,8 +93,8 @@ public class VariationParserTest extends TestCase { public void testParseFontVariationStyleSettings() { List<FontConfig.Axis> axes = FontListParser.parseFontVariationSettings("'wdth' 10,'AX '\r1"); int tag1 = FontListParser.makeTag('w', 'd', 't', 'h'); int tag2 = FontListParser.makeTag('A', 'X', ' ', ' '); int tag1 = FontListParser.makeTag("wdth"); int tag2 = FontListParser.makeTag("AX "); assertEquals(tag1, axes.get(0).getTag()); assertEquals(10.0f, axes.get(0).getStyleValue()); assertEquals(tag2, axes.get(1).getTag()); Loading @@ -102,7 +102,7 @@ public class VariationParserTest extends TestCase { // Test only spacers are allowed before tag axes = FontListParser.parseFontVariationSettings(" 'wdth' 10,ab'wdth' 1"); tag1 = FontListParser.makeTag('w', 'd', 't', 'h'); tag1 = FontListParser.makeTag("wdth"); assertEquals(tag1, axes.get(0).getTag()); assertEquals(10.0f, axes.get(0).getStyleValue()); assertEquals(1, axes.size()); Loading @@ -119,8 +119,8 @@ public class VariationParserTest extends TestCase { @SmallTest public void testMakeTag() { assertEquals(0x77647468, FontListParser.makeTag('w', 'd', 't', 'h')); assertEquals(0x41582020, FontListParser.makeTag('A', 'X', ' ', ' ')); assertEquals(0x20202020, FontListParser.makeTag(' ', ' ', ' ', ' ')); assertEquals(0x77647468, FontListParser.makeTag("wdth")); assertEquals(0x41582020, FontListParser.makeTag("AX ")); assertEquals(0x20202020, FontListParser.makeTag(" ")); } } graphics/java/android/graphics/FontFamily.java +30 −12 Original line number Diff line number Diff line Loading @@ -81,7 +81,8 @@ public class FontFamily { } } public boolean addFont(String path, int ttcIndex) { public boolean addFont(String path, int ttcIndex, FontConfig.Axis[] axes, int weight, int italic) { if (mBuilderPtr == 0) { throw new IllegalStateException("Unable to call addFont after freezing."); } Loading @@ -89,22 +90,29 @@ public class FontFamily { FileChannel fileChannel = file.getChannel(); long fontSize = fileChannel.size(); ByteBuffer fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize); return nAddFont(mBuilderPtr, fontBuffer, ttcIndex); if (axes != null) { for (FontConfig.Axis axis : axes) { nAddAxisValue(mBuilderPtr, axis.getTag(), axis.getStyleValue()); } } return nAddFont(mBuilderPtr, fontBuffer, ttcIndex, weight, italic); } catch (IOException e) { Log.e(TAG, "Error mapping font file " + path); return false; } } public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, FontConfig.Axis[] axes, int weight, boolean style) { public boolean addFontFromBuffer(ByteBuffer font, int ttcIndex, FontConfig.Axis[] axes, int weight, int italic) { if (mBuilderPtr == 0) { throw new IllegalStateException("Unable to call addFontWeightStyle after freezing."); } if (axes != null) { for (FontConfig.Axis axis : axes) { nAddAxisValue(mBuilderPtr, axis.getTag(), axis.getStyleValue()); } return nAddFontWeightStyle(mBuilderPtr, font, ttcIndex, weight, style); } return nAddFontWeightStyle(mBuilderPtr, font, ttcIndex, weight, italic); } /** Loading @@ -120,11 +128,18 @@ public class FontFamily { * @return */ public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie, boolean isAsset, int weight, boolean isItalic) { boolean isAsset, int ttcIndex, int weight, int isItalic, FontConfig.Axis[] axes) { if (mBuilderPtr == 0) { throw new IllegalStateException("Unable to call addFontFromAsset after freezing."); } return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, weight, isItalic); if (axes != null) { for (FontConfig.Axis axis : axes) { nAddAxisValue(mBuilderPtr, axis.getTag(), axis.getStyleValue()); } } return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, ttcIndex, weight, isItalic); } private static native long nInitBuilder(String lang, int variant); Loading @@ -137,11 +152,14 @@ public class FontFamily { @CriticalNative private static native void nUnrefFamily(long nativePtr); private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex); // By passing -1 to weigth argument, the weight value is resolved by OS/2 table in the font. // By passing -1 to italic argument, the italic value is resolved by OS/2 table in the font. private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex, int weight, int isItalic); private static native boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font, int ttcIndex, int weight, boolean isItalic); int ttcIndex, int weight, int isItalic); private static native boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr, String path, int cookie, boolean isAsset, int weight, boolean isItalic); String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic); // The added axis values are only valid for the next nAddFont* method call. @CriticalNative Loading graphics/java/android/graphics/FontListParser.java +15 −6 Original line number Diff line number Diff line Loading @@ -98,15 +98,17 @@ public class FontListParser { } catch (NumberFormatException e) { continue; // ignoreing invalid number format } int tag = makeTag(tagString.charAt(0), tagString.charAt(1), tagString.charAt(2), tagString.charAt(3)); int tag = makeTag(tagString); axisList.add(new FontConfig.Axis(tag, styleValue)); } return axisList; } @VisibleForTesting public static int makeTag(char c1, char c2, char c3, char c4) { public static int makeTag(String tagString) { char c1 = tagString.charAt(0); char c2 = tagString.charAt(1); char c3 = tagString.charAt(2); char c4 = tagString.charAt(3); return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; } Loading Loading @@ -198,6 +200,13 @@ public class FontListParser { */ private static final Pattern TAG_PATTERN = Pattern.compile("[\\x20-\\x7E]{4}"); public static boolean isValidTag(String tagString) { if (tagString == null || tagString.length() != 4) { return false; } return TAG_PATTERN.matcher(tagString).matches(); } /** The 'styleValue' attribute has an optional leading '-', followed by '<digits>', * '<digits>.<digits>', or '.<digits>' where '<digits>' is one or more of [0-9]. */ Loading @@ -208,8 +217,8 @@ public class FontListParser { throws XmlPullParserException, IOException { int tag = 0; String tagStr = parser.getAttributeValue(null, "tag"); if (tagStr != null && TAG_PATTERN.matcher(tagStr).matches()) { tag = makeTag(tagStr.charAt(0), tagStr.charAt(1), tagStr.charAt(2), tagStr.charAt(3)); if (isValidTag(tagStr)) { tag = makeTag(tagStr); } else { throw new XmlPullParserException("Invalid tag attribute value.", parser, null); } Loading Loading
core/java/android/text/FontConfig.java +12 −2 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.FontListParser; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; Loading Loading @@ -106,8 +107,17 @@ public final class FontConfig implements Parcelable { private final float mStyleValue; public Axis(int tag, float styleValue) { this.mTag = tag; this.mStyleValue = styleValue; mTag = tag; mStyleValue = styleValue; } /** @hide */ public Axis(@NonNull String tagString, float styleValue) { if (!FontListParser.isValidTag(tagString)) { throw new IllegalArgumentException("Invalid tag pattern: " + tagString); } mTag = FontListParser.makeTag(tagString); mStyleValue = styleValue; } /** Loading
core/jni/android/graphics/FontFamily.cpp +65 −70 Original line number Diff line number Diff line Loading @@ -34,12 +34,16 @@ #include <hwui/MinikinSkia.h> #include <hwui/Typeface.h> #include <utils/FatVector.h> #include <minikin/FontFamily.h> #include <memory> namespace android { // Must be same with Java constant in Typeface.Builder. See Typeface.java constexpr jint RESOLVE_BY_FONT_TABLE = -1; struct NativeFamilyBuilder { uint32_t langId; int variant; Loading Loading @@ -81,24 +85,53 @@ static void FontFamily_unref(jlong familyPtr) { delete family; } static void addSkTypeface(jlong builderPtr, sk_sp<SkTypeface> face, const void* fontData, size_t fontSize, int ttcIndex, jint givenWeight, jboolean givenItalic) { static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex, jint givenWeight, jint givenItalic) { uirenderer::FatVector<SkFontMgr::FontParameters::Axis, 2> skiaAxes; for (const auto& axis : builder->axes) { skiaAxes.emplace_back(SkFontMgr::FontParameters::Axis{axis.axisTag, axis.value}); } const size_t fontSize = data->size(); const void* fontPtr = data->data(); std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data))); SkFontMgr::FontParameters params; params.setCollectionIndex(ttcIndex); params.setAxes(skiaAxes.data(), skiaAxes.size()); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), params)); if (face == NULL) { ALOGE("addFont failed to create font, invalid request"); builder->axes.clear(); return false; } std::shared_ptr<minikin::MinikinFont> minikinFont = std::make_shared<MinikinFontSkia>(std::move(face), fontData, fontSize, ttcIndex, std::vector<minikin::FontVariation>()); NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex, builder->axes); int weight = givenWeight / 100; bool italic = givenItalic; if (weight == 0) { if (!minikin::FontFamily::analyzeStyle(minikinFont, &weight, &italic)) { bool italic = givenItalic == 1; if (givenWeight == RESOLVE_BY_FONT_TABLE || givenItalic == RESOLVE_BY_FONT_TABLE) { int os2Weight; bool os2Italic; if (!minikin::FontFamily::analyzeStyle(minikinFont, &os2Weight, &os2Italic)) { ALOGE("analyzeStyle failed. Using default style"); weight = 4; italic = false; os2Weight = 4; os2Italic = false; } if (givenWeight == RESOLVE_BY_FONT_TABLE) { weight = os2Weight; } if (givenItalic == RESOLVE_BY_FONT_TABLE) { italic = os2Italic; } } builder->fonts.push_back(minikin::Font( std::move(minikinFont), minikin::FontStyle(weight, italic))); builder->fonts.push_back(minikin::Font(minikinFont, minikin::FontStyle(weight, italic))); builder->axes.clear(); return true; } static void release_global_ref(const void* /*data*/, void* context) { Loading @@ -125,80 +158,47 @@ static void release_global_ref(const void* /*data*/, void* context) { } static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong builderPtr, jobject bytebuf, jint ttcIndex) { jint ttcIndex, jint weight, jint isItalic) { NPE_CHECK_RETURN_ZERO(env, bytebuf); NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); const void* fontPtr = env->GetDirectBufferAddress(bytebuf); if (fontPtr == NULL) { ALOGE("addFont failed to create font, buffer invalid"); builder->axes.clear(); return false; } jlong fontSize = env->GetDirectBufferCapacity(bytebuf); if (fontSize < 0) { ALOGE("addFont failed to create font, buffer size invalid"); builder->axes.clear(); return false; } jobject fontRef = MakeGlobalRefOrDie(env, bytebuf); sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize, release_global_ref, reinterpret_cast<void*>(fontRef))); std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data))); SkFontMgr::FontParameters params; params.setCollectionIndex(ttcIndex); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), params)); if (face == NULL) { ALOGE("addFont failed to create font"); return false; } addSkTypeface(builderPtr, std::move(face), fontPtr, (size_t)fontSize, ttcIndex, 0, false); return true; return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong builderPtr, jobject font, jint ttcIndex, jint weight, jboolean isItalic) { jobject font, jint ttcIndex, jint weight, jint isItalic) { NPE_CHECK_RETURN_ZERO(env, font); NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); // Declare axis native type. std::vector<SkFontMgr::FontParameters::Axis> skiaAxes; skiaAxes.reserve(builder->axes.size()); for (const minikin::FontVariation& minikinAxis : builder->axes) { skiaAxes.push_back({minikinAxis.axisTag, minikinAxis.value}); } const void* fontPtr = env->GetDirectBufferAddress(font); if (fontPtr == NULL) { ALOGE("addFont failed to create font, buffer invalid"); builder->axes.clear(); return false; } jlong fontSize = env->GetDirectBufferCapacity(font); if (fontSize < 0) { ALOGE("addFont failed to create font, buffer size invalid"); builder->axes.clear(); return false; } jobject fontRef = MakeGlobalRefOrDie(env, font); sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize, release_global_ref, reinterpret_cast<void*>(fontRef))); std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data))); SkFontMgr::FontParameters params; params.setCollectionIndex(ttcIndex); params.setAxes(skiaAxes.data(), skiaAxes.size()); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), params)); if (face == NULL) { ALOGE("addFont failed to create font, invalid request"); return false; } std::shared_ptr<minikin::MinikinFont> minikinFont = std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex, std::vector<minikin::FontVariation>()); builder->fonts.push_back(minikin::Font(std::move(minikinFont), minikin::FontStyle(weight / 100, isItalic))); return true; return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } static void releaseAsset(const void* ptr, void* context) { Loading @@ -206,18 +206,21 @@ static void releaseAsset(const void* ptr, void* context) { } static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong builderPtr, jobject jassetMgr, jstring jpath, jint cookie, jboolean isAsset, jint weight, jboolean isItalic) { jobject jassetMgr, jstring jpath, jint cookie, jboolean isAsset, jint ttcIndex, jint weight, jint isItalic) { NPE_CHECK_RETURN_ZERO(env, jassetMgr); NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; } ScopedUtfChars str(env, jpath); if (str.c_str() == nullptr) { builder->axes.clear(); return false; } Loading @@ -230,27 +233,19 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b } if (NULL == asset) { builder->axes.clear(); return false; } const void* buf = asset->getBuffer(false); if (NULL == buf) { delete asset; builder->axes.clear(); return false; } size_t bufSize = asset->getLength(); sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data))); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), SkFontMgr::FontParameters())); if (face == NULL) { ALOGE("addFontFromAsset failed to create font %s", str.c_str()); return false; } addSkTypeface(builderPtr, std::move(face), buf, bufSize, 0 /* ttc index */, weight, isItalic); addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); return true; } Loading @@ -266,10 +261,10 @@ static const JNINativeMethod gFontFamilyMethods[] = { { "nCreateFamily", "(J)J", (void*)FontFamily_create }, { "nAbort", "(J)V", (void*)FontFamily_abort }, { "nUnrefFamily", "(J)V", (void*)FontFamily_unref }, { "nAddFont", "(JLjava/nio/ByteBuffer;I)Z", (void*)FontFamily_addFont }, { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;IIZ)Z", { "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont }, { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFontWeightStyle }, { "nAddFontFromAssetManager", "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZIZ)Z", { "nAddFontFromAssetManager", "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZIII)Z", (void*)FontFamily_addFontFromAssetManager }, { "nAddAxisValue", "(JIF)V", (void*)FontFamily_addAxisValue }, }; Loading
core/tests/coretests/src/android/graphics/VariationParserTest.java +8 −8 Original line number Diff line number Diff line Loading @@ -28,7 +28,7 @@ public class VariationParserTest extends TestCase { @SmallTest public void testParseFontVariationSetting() { int tag = FontListParser.makeTag('w', 'd', 't', 'h'); int tag = FontListParser.makeTag("wdth"); List<FontConfig.Axis> axes = FontListParser.parseFontVariationSettings("'wdth' 1"); assertEquals(tag, axes.get(0).getTag()); assertEquals(1.0f, axes.get(0).getStyleValue()); Loading @@ -45,7 +45,7 @@ public class VariationParserTest extends TestCase { assertEquals(tag, axes.get(0).getTag()); assertEquals(0.5f, axes.get(0).getStyleValue()); tag = FontListParser.makeTag('A', 'X', ' ', ' '); tag = FontListParser.makeTag("AX "); axes = FontListParser.parseFontVariationSettings("'AX ' 1"); assertEquals(tag, axes.get(0).getTag()); assertEquals(1.0f, axes.get(0).getStyleValue()); Loading Loading @@ -93,8 +93,8 @@ public class VariationParserTest extends TestCase { public void testParseFontVariationStyleSettings() { List<FontConfig.Axis> axes = FontListParser.parseFontVariationSettings("'wdth' 10,'AX '\r1"); int tag1 = FontListParser.makeTag('w', 'd', 't', 'h'); int tag2 = FontListParser.makeTag('A', 'X', ' ', ' '); int tag1 = FontListParser.makeTag("wdth"); int tag2 = FontListParser.makeTag("AX "); assertEquals(tag1, axes.get(0).getTag()); assertEquals(10.0f, axes.get(0).getStyleValue()); assertEquals(tag2, axes.get(1).getTag()); Loading @@ -102,7 +102,7 @@ public class VariationParserTest extends TestCase { // Test only spacers are allowed before tag axes = FontListParser.parseFontVariationSettings(" 'wdth' 10,ab'wdth' 1"); tag1 = FontListParser.makeTag('w', 'd', 't', 'h'); tag1 = FontListParser.makeTag("wdth"); assertEquals(tag1, axes.get(0).getTag()); assertEquals(10.0f, axes.get(0).getStyleValue()); assertEquals(1, axes.size()); Loading @@ -119,8 +119,8 @@ public class VariationParserTest extends TestCase { @SmallTest public void testMakeTag() { assertEquals(0x77647468, FontListParser.makeTag('w', 'd', 't', 'h')); assertEquals(0x41582020, FontListParser.makeTag('A', 'X', ' ', ' ')); assertEquals(0x20202020, FontListParser.makeTag(' ', ' ', ' ', ' ')); assertEquals(0x77647468, FontListParser.makeTag("wdth")); assertEquals(0x41582020, FontListParser.makeTag("AX ")); assertEquals(0x20202020, FontListParser.makeTag(" ")); } }
graphics/java/android/graphics/FontFamily.java +30 −12 Original line number Diff line number Diff line Loading @@ -81,7 +81,8 @@ public class FontFamily { } } public boolean addFont(String path, int ttcIndex) { public boolean addFont(String path, int ttcIndex, FontConfig.Axis[] axes, int weight, int italic) { if (mBuilderPtr == 0) { throw new IllegalStateException("Unable to call addFont after freezing."); } Loading @@ -89,22 +90,29 @@ public class FontFamily { FileChannel fileChannel = file.getChannel(); long fontSize = fileChannel.size(); ByteBuffer fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize); return nAddFont(mBuilderPtr, fontBuffer, ttcIndex); if (axes != null) { for (FontConfig.Axis axis : axes) { nAddAxisValue(mBuilderPtr, axis.getTag(), axis.getStyleValue()); } } return nAddFont(mBuilderPtr, fontBuffer, ttcIndex, weight, italic); } catch (IOException e) { Log.e(TAG, "Error mapping font file " + path); return false; } } public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, FontConfig.Axis[] axes, int weight, boolean style) { public boolean addFontFromBuffer(ByteBuffer font, int ttcIndex, FontConfig.Axis[] axes, int weight, int italic) { if (mBuilderPtr == 0) { throw new IllegalStateException("Unable to call addFontWeightStyle after freezing."); } if (axes != null) { for (FontConfig.Axis axis : axes) { nAddAxisValue(mBuilderPtr, axis.getTag(), axis.getStyleValue()); } return nAddFontWeightStyle(mBuilderPtr, font, ttcIndex, weight, style); } return nAddFontWeightStyle(mBuilderPtr, font, ttcIndex, weight, italic); } /** Loading @@ -120,11 +128,18 @@ public class FontFamily { * @return */ public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie, boolean isAsset, int weight, boolean isItalic) { boolean isAsset, int ttcIndex, int weight, int isItalic, FontConfig.Axis[] axes) { if (mBuilderPtr == 0) { throw new IllegalStateException("Unable to call addFontFromAsset after freezing."); } return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, weight, isItalic); if (axes != null) { for (FontConfig.Axis axis : axes) { nAddAxisValue(mBuilderPtr, axis.getTag(), axis.getStyleValue()); } } return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, ttcIndex, weight, isItalic); } private static native long nInitBuilder(String lang, int variant); Loading @@ -137,11 +152,14 @@ public class FontFamily { @CriticalNative private static native void nUnrefFamily(long nativePtr); private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex); // By passing -1 to weigth argument, the weight value is resolved by OS/2 table in the font. // By passing -1 to italic argument, the italic value is resolved by OS/2 table in the font. private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex, int weight, int isItalic); private static native boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font, int ttcIndex, int weight, boolean isItalic); int ttcIndex, int weight, int isItalic); private static native boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr, String path, int cookie, boolean isAsset, int weight, boolean isItalic); String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic); // The added axis values are only valid for the next nAddFont* method call. @CriticalNative Loading
graphics/java/android/graphics/FontListParser.java +15 −6 Original line number Diff line number Diff line Loading @@ -98,15 +98,17 @@ public class FontListParser { } catch (NumberFormatException e) { continue; // ignoreing invalid number format } int tag = makeTag(tagString.charAt(0), tagString.charAt(1), tagString.charAt(2), tagString.charAt(3)); int tag = makeTag(tagString); axisList.add(new FontConfig.Axis(tag, styleValue)); } return axisList; } @VisibleForTesting public static int makeTag(char c1, char c2, char c3, char c4) { public static int makeTag(String tagString) { char c1 = tagString.charAt(0); char c2 = tagString.charAt(1); char c3 = tagString.charAt(2); char c4 = tagString.charAt(3); return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; } Loading Loading @@ -198,6 +200,13 @@ public class FontListParser { */ private static final Pattern TAG_PATTERN = Pattern.compile("[\\x20-\\x7E]{4}"); public static boolean isValidTag(String tagString) { if (tagString == null || tagString.length() != 4) { return false; } return TAG_PATTERN.matcher(tagString).matches(); } /** The 'styleValue' attribute has an optional leading '-', followed by '<digits>', * '<digits>.<digits>', or '.<digits>' where '<digits>' is one or more of [0-9]. */ Loading @@ -208,8 +217,8 @@ public class FontListParser { throws XmlPullParserException, IOException { int tag = 0; String tagStr = parser.getAttributeValue(null, "tag"); if (tagStr != null && TAG_PATTERN.matcher(tagStr).matches()) { tag = makeTag(tagStr.charAt(0), tagStr.charAt(1), tagStr.charAt(2), tagStr.charAt(3)); if (isValidTag(tagStr)) { tag = makeTag(tagStr); } else { throw new XmlPullParserException("Invalid tag attribute value.", parser, null); } Loading