/* * Copyright (C) 2018 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. */ #undef LOG_TAG #define LOG_TAG "Minikin" #include "Font.h" #include "SkData.h" #include "SkFont.h" #include "SkFontMetrics.h" #include "SkFontMgr.h" #include "SkRefCnt.h" #include "SkTypeface.h" #include "GraphicsJNI.h" #include #include "Utils.h" #include "FontUtils.h" #include #include #include #include #include #include #include #include #include namespace android { struct NativeFontBuilder { std::vector axes; }; static inline NativeFontBuilder* toBuilder(jlong ptr) { return reinterpret_cast(ptr); } static void releaseFont(jlong font) { delete reinterpret_cast(font); } static void release_global_ref(const void* /*data*/, void* context) { JNIEnv* env = GraphicsJNI::getJNIEnv(); bool needToAttach = (env == nullptr); if (needToAttach) { env = GraphicsJNI::attachJNIEnv("release_font_data"); if (env == nullptr) { ALOGE("failed to attach to thread to release global ref."); return; } } jobject obj = reinterpret_cast(context); env->DeleteGlobalRef(obj); } // Regular JNI static jlong Font_Builder_initBuilder(JNIEnv*, jobject) { return reinterpret_cast(new NativeFontBuilder()); } // Critical Native static void Font_Builder_addAxis(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jint tag, jfloat value) { toBuilder(builderPtr)->axes.emplace_back(static_cast(tag), value); } // Regular JNI static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jobject buffer, jstring filePath, jstring langTags, jint weight, jboolean italic, jint ttcIndex) { NPE_CHECK_RETURN_ZERO(env, buffer); std::unique_ptr builder(toBuilder(builderPtr)); const void* fontPtr = env->GetDirectBufferAddress(buffer); if (fontPtr == nullptr) { jniThrowException(env, "java/lang/IllegalArgumentException", "Not a direct buffer"); return 0; } jlong fontSize = env->GetDirectBufferCapacity(buffer); if (fontSize <= 0) { jniThrowException(env, "java/lang/IllegalArgumentException", "buffer size must not be zero or negative"); return 0; } ScopedUtfChars fontPath(env, filePath); ScopedUtfChars langTagStr(env, langTags); jobject fontRef = MakeGlobalRefOrDie(env, buffer); sk_sp data(SkData::MakeWithProc(fontPtr, fontSize, release_global_ref, reinterpret_cast(fontRef))); std::shared_ptr minikinFont = fonts::createMinikinFontSkia( std::move(data), std::string_view(fontPath.c_str(), fontPath.size()), fontPtr, fontSize, ttcIndex, builder->axes); if (minikinFont == nullptr) { jniThrowException(env, "java/lang/IllegalArgumentException", "Failed to create internal object. maybe invalid font data."); return 0; } uint32_t localeListId = minikin::registerLocaleList(langTagStr.c_str()); std::shared_ptr font = minikin::Font::Builder(minikinFont) .setWeight(weight) .setSlant(static_cast(italic)) .setLocaleListId(localeListId) .build(); return reinterpret_cast(new FontWrapper(std::move(font))); } // Fast Native static jlong Font_Builder_clone(JNIEnv* env, jobject clazz, jlong fontPtr, jlong builderPtr, jint weight, jboolean italic, jint ttcIndex) { FontWrapper* font = reinterpret_cast(fontPtr); MinikinFontSkia* minikinSkia = static_cast(font->font->typeface().get()); std::unique_ptr builder(toBuilder(builderPtr)); // Reconstruct SkTypeface with different arguments from existing SkTypeface. FatVector skVariation; for (const auto& axis : builder->axes) { skVariation.push_back({axis.axisTag, axis.value}); } SkFontArguments args; args.setCollectionIndex(ttcIndex); args.setVariationDesignPosition({skVariation.data(), static_cast(skVariation.size())}); sk_sp newTypeface = minikinSkia->GetSkTypeface()->makeClone(args); std::shared_ptr newMinikinFont = std::make_shared( std::move(newTypeface), minikinSkia->GetSourceId(), minikinSkia->GetFontData(), minikinSkia->GetFontSize(), minikinSkia->getFilePath(), minikinSkia->GetFontIndex(), builder->axes); std::shared_ptr newFont = minikin::Font::Builder(newMinikinFont) .setWeight(weight) .setSlant(static_cast(italic)) .build(); return reinterpret_cast(new FontWrapper(std::move(newFont))); } /////////////////////////////////////////////////////////////////////////////// // Font JNI functions // Fast Native static jfloat Font_getGlyphBounds(JNIEnv* env, jobject, jlong fontHandle, jint glyphId, jlong paintHandle, jobject rect) { FontWrapper* font = reinterpret_cast(fontHandle); MinikinFontSkia* minikinSkia = static_cast(font->font->typeface().get()); Paint* paint = reinterpret_cast(paintHandle); SkFont* skFont = &paint->getSkFont(); // We don't use populateSkFont since it is designed to be used for layout result with addressing // auto fake-bolding. skFont->setTypeface(minikinSkia->RefSkTypeface()); uint16_t glyph16 = glyphId; SkRect skBounds; SkScalar skWidth; skFont->getWidthsBounds(&glyph16, 1, &skWidth, &skBounds, nullptr); GraphicsJNI::rect_to_jrectf(skBounds, env, rect); return SkScalarToFloat(skWidth); } // Fast Native static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong paintHandle, jobject metricsObj) { FontWrapper* font = reinterpret_cast(fontHandle); MinikinFontSkia* minikinSkia = static_cast(font->font->typeface().get()); Paint* paint = reinterpret_cast(paintHandle); SkFont* skFont = &paint->getSkFont(); // We don't use populateSkFont since it is designed to be used for layout result with addressing // auto fake-bolding. skFont->setTypeface(minikinSkia->RefSkTypeface()); SkFontMetrics metrics; SkScalar spacing = skFont->getMetrics(&metrics); GraphicsJNI::set_metrics(env, metricsObj, metrics); return spacing; } // Critical Native static jlong Font_getMinikinFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { FontWrapper* font = reinterpret_cast(fontPtr); return reinterpret_cast(font->font.get()); } // Critical Native static jlong Font_cloneFont(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { FontWrapper* font = reinterpret_cast(fontPtr); std::shared_ptr ref = font->font; return reinterpret_cast(new FontWrapper(std::move(ref))); } // Fast Native static jobject Font_newByteBuffer(JNIEnv* env, jobject, jlong fontPtr) { FontWrapper* font = reinterpret_cast(fontPtr); const std::shared_ptr& minikinFont = font->font->typeface(); return env->NewDirectByteBuffer(const_cast(minikinFont->GetFontData()), minikinFont->GetFontSize()); } // Critical Native static jlong Font_getBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { FontWrapper* font = reinterpret_cast(fontPtr); return reinterpret_cast(font->font->typeface()->GetFontData()); } // Critical Native static jlong Font_getReleaseNativeFontFunc(CRITICAL_JNI_PARAMS) { return reinterpret_cast(releaseFont); } // Fast Native static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontPtr) { FontWrapper* font = reinterpret_cast(fontPtr); minikin::BufferReader reader = font->font->typefaceMetadataReader(); if (reader.data() != nullptr) { std::string path = std::string(reader.readString()); if (path.empty()) { return nullptr; } return env->NewStringUTF(path.c_str()); } else { const std::shared_ptr& minikinFont = font->font->typeface(); const std::string& path = minikinFont->GetFontPath(); if (path.empty()) { return nullptr; } return env->NewStringUTF(path.c_str()); } } // Fast Native static jstring Font_getLocaleList(JNIEnv* env, jobject, jlong fontPtr) { FontWrapper* font = reinterpret_cast(fontPtr); uint32_t localeListId = font->font->getLocaleListId(); if (localeListId == 0) { return nullptr; } std::string langTags = minikin::getLocaleString(localeListId); if (langTags.empty()) { return nullptr; } return env->NewStringUTF(langTags.c_str()); } // Critical Native static jint Font_getPackedStyle(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { FontWrapper* font = reinterpret_cast(fontPtr); uint32_t weight = font->font->style().weight(); uint32_t isItalic = font->font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 1 : 0; return (isItalic << 16) | weight; } // Critical Native static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { FontWrapper* font = reinterpret_cast(fontPtr); minikin::BufferReader reader = font->font->typefaceMetadataReader(); if (reader.data() != nullptr) { reader.skipString(); // fontPath return reader.read(); } else { const std::shared_ptr& minikinFont = font->font->typeface(); return minikinFont->GetFontIndex(); } } // Critical Native static jint Font_getAxisCount(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { FontWrapper* font = reinterpret_cast(fontPtr); minikin::BufferReader reader = font->font->typefaceMetadataReader(); if (reader.data() != nullptr) { reader.skipString(); // fontPath reader.skip(); // fontIndex return reader.readArray().second; } else { const std::shared_ptr& minikinFont = font->font->typeface(); return minikinFont->GetAxes().size(); } } // Critical Native static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr, jint index) { FontWrapper* font = reinterpret_cast(fontPtr); minikin::BufferReader reader = font->font->typefaceMetadataReader(); minikin::FontVariation var; if (reader.data() != nullptr) { reader.skipString(); // fontPath reader.skip(); // fontIndex var = reader.readArray().first[index]; } else { const std::shared_ptr& minikinFont = font->font->typeface(); var = minikinFont->GetAxes().at(index); } uint32_t floatBinary = *reinterpret_cast(&var.value); return (static_cast(var.axisTag) << 32) | static_cast(floatBinary); } // Critical Native static jint Font_getSourceId(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { FontWrapper* font = reinterpret_cast(fontPtr); return font->font->typeface()->GetSourceId(); } static jlongArray Font_getAvailableFontSet(JNIEnv* env, jobject) { std::vector refArray; minikin::SystemFonts::getFontSet( [&refArray](const std::vector>& fontSet) { refArray.reserve(fontSet.size()); for (const auto& font : fontSet) { std::shared_ptr fontRef = font; refArray.push_back( reinterpret_cast(new FontWrapper(std::move(fontRef)))); } }); jlongArray r = env->NewLongArray(refArray.size()); env->SetLongArrayRegion(r, 0, refArray.size(), refArray.data()); return r; } // Fast Native static jlong FontFileUtil_getFontRevision(JNIEnv* env, jobject, jobject buffer, jint index) { NPE_CHECK_RETURN_ZERO(env, buffer); const void* fontPtr = env->GetDirectBufferAddress(buffer); if (fontPtr == nullptr) { jniThrowException(env, "java/lang/IllegalArgumentException", "Not a direct buffer"); return 0; } jlong fontSize = env->GetDirectBufferCapacity(buffer); if (fontSize <= 0) { jniThrowException(env, "java/lang/IllegalArgumentException", "buffer size must not be zero or negative"); return 0; } minikin::FontFileParser parser(fontPtr, fontSize, index); std::optional revision = parser.getFontRevision(); if (!revision.has_value()) { return -1L; } return revision.value(); } static jstring FontFileUtil_getFontPostScriptName(JNIEnv* env, jobject, jobject buffer, jint index) { NPE_CHECK_RETURN_ZERO(env, buffer); const void* fontPtr = env->GetDirectBufferAddress(buffer); if (fontPtr == nullptr) { jniThrowException(env, "java/lang/IllegalArgumentException", "Not a direct buffer"); return nullptr; } jlong fontSize = env->GetDirectBufferCapacity(buffer); if (fontSize <= 0) { jniThrowException(env, "java/lang/IllegalArgumentException", "buffer size must not be zero or negative"); return nullptr; } minikin::FontFileParser parser(fontPtr, fontSize, index); std::optional psName = parser.getPostScriptName(); if (!psName.has_value()) { return nullptr; // null } return env->NewStringUTF(psName->c_str()); } static jint FontFileUtil_isPostScriptType1Font(JNIEnv* env, jobject, jobject buffer, jint index) { NPE_CHECK_RETURN_ZERO(env, buffer); const void* fontPtr = env->GetDirectBufferAddress(buffer); if (fontPtr == nullptr) { jniThrowException(env, "java/lang/IllegalArgumentException", "Not a direct buffer"); return -1; } jlong fontSize = env->GetDirectBufferCapacity(buffer); if (fontSize <= 0) { jniThrowException(env, "java/lang/IllegalArgumentException", "buffer size must not be zero or negative"); return -1; } minikin::FontFileParser parser(fontPtr, fontSize, index); std::optional isType1 = parser.isPostScriptType1Font(); if (!isType1.has_value()) { return -1; // not an OpenType font. HarfBuzz failed to parse it. } return isType1.value(); } /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gFontBuilderMethods[] = { {"nInitBuilder", "()J", (void*)Font_Builder_initBuilder}, {"nAddAxis", "(JIF)V", (void*)Font_Builder_addAxis}, {"nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;Ljava/lang/String;IZI)J", (void*)Font_Builder_build}, {"nClone", "(JJIZI)J", (void*)Font_Builder_clone}, }; static const JNINativeMethod gFontMethods[] = { {"nGetMinikinFontPtr", "(J)J", (void*)Font_getMinikinFontPtr}, {"nCloneFont", "(J)J", (void*)Font_cloneFont}, {"nNewByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*)Font_newByteBuffer}, {"nGetBufferAddress", "(J)J", (void*)Font_getBufferAddress}, {"nGetReleaseNativeFont", "()J", (void*)Font_getReleaseNativeFontFunc}, {"nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*)Font_getGlyphBounds}, {"nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*)Font_getFontMetrics}, {"nGetFontPath", "(J)Ljava/lang/String;", (void*)Font_getFontPath}, {"nGetLocaleList", "(J)Ljava/lang/String;", (void*)Font_getLocaleList}, {"nGetPackedStyle", "(J)I", (void*)Font_getPackedStyle}, {"nGetIndex", "(J)I", (void*)Font_getIndex}, {"nGetAxisCount", "(J)I", (void*)Font_getAxisCount}, {"nGetAxisInfo", "(JI)J", (void*)Font_getAxisInfo}, {"nGetSourceId", "(J)I", (void*)Font_getSourceId}, // System font accessors {"nGetAvailableFontSet", "()[J", (void*)Font_getAvailableFontSet}, }; static const JNINativeMethod gFontFileUtilMethods[] = { { "nGetFontRevision", "(Ljava/nio/ByteBuffer;I)J", (void*) FontFileUtil_getFontRevision }, { "nGetFontPostScriptName", "(Ljava/nio/ByteBuffer;I)Ljava/lang/String;", (void*) FontFileUtil_getFontPostScriptName }, { "nIsPostScriptType1Font", "(Ljava/nio/ByteBuffer;I)I", (void*) FontFileUtil_isPostScriptType1Font }, }; int register_android_graphics_fonts_Font(JNIEnv* env) { return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods, NELEM(gFontBuilderMethods)) + RegisterMethodsOrDie(env, "android/graphics/fonts/Font", gFontMethods, NELEM(gFontMethods)) + RegisterMethodsOrDie(env, "android/graphics/fonts/FontFileUtil", gFontFileUtilMethods, NELEM(gFontFileUtilMethods)); } namespace fonts { std::shared_ptr createMinikinFontSkia( sk_sp&& data, std::string_view fontPath, const void *fontPtr, size_t fontSize, int ttcIndex, const std::vector& axes) { FatVector skVariation; for (const auto& axis : axes) { skVariation.push_back({axis.axisTag, axis.value}); } std::unique_ptr fontData(new SkMemoryStream(std::move(data))); SkFontArguments args; args.setCollectionIndex(ttcIndex); args.setVariationDesignPosition({skVariation.data(), static_cast(skVariation.size())}); sk_sp fm(SkFontMgr::RefDefault()); sk_sp face(fm->makeFromStream(std::move(fontData), args)); if (face == nullptr) { return nullptr; } return std::make_shared(std::move(face), getNewSourceId(), fontPtr, fontSize, fontPath, ttcIndex, axes); } int getNewSourceId() { static std::atomic sSourceId = {0}; return sSourceId++; } } // namespace fonts } // namespace android