diff options
| -rw-r--r-- | api/current.txt | 31 | ||||
| -rw-r--r-- | core/jni/Android.bp | 1 | ||||
| -rw-r--r-- | core/jni/AndroidRuntime.cpp | 2 | ||||
| -rw-r--r-- | core/jni/android/graphics/FontUtils.h | 7 | ||||
| -rw-r--r-- | core/jni/android/graphics/fonts/Font.cpp | 205 | ||||
| -rw-r--r-- | graphics/java/android/graphics/fonts/Font.java | 472 | ||||
| -rw-r--r-- | graphics/java/android/graphics/fonts/FontFileUtil.java | 17 |
7 files changed, 730 insertions, 5 deletions
diff --git a/api/current.txt b/api/current.txt index d971f1813731..498ab9558f2b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15178,6 +15178,37 @@ package android.graphics.drawable.shapes { package android.graphics.fonts { + public class Font { + method public android.graphics.fonts.FontVariationAxis[] getAxes(); + method public int getTtcIndex(); + method public int getWeight(); + method public boolean isItalic(); + field public static final int FONT_WEIGHT_BLACK = 900; // 0x384 + field public static final int FONT_WEIGHT_BOLD = 700; // 0x2bc + field public static final int FONT_WEIGHT_EXTRA_BOLD = 800; // 0x320 + field public static final int FONT_WEIGHT_EXTRA_LIGHT = 200; // 0xc8 + field public static final int FONT_WEIGHT_LIGHT = 300; // 0x12c + field public static final int FONT_WEIGHT_MEDIUM = 500; // 0x1f4 + field public static final int FONT_WEIGHT_NORMAL = 400; // 0x190 + field public static final int FONT_WEIGHT_SEMI_BOLD = 600; // 0x258 + field public static final int FONT_WEIGHT_THIN = 100; // 0x64 + } + + public static class Font.Builder { + ctor public Font.Builder(java.nio.ByteBuffer); + ctor public Font.Builder(java.io.File) throws java.io.IOException; + ctor public Font.Builder(java.io.FileDescriptor) throws java.io.IOException; + ctor public Font.Builder(java.io.FileDescriptor, long, long) throws java.io.IOException; + ctor public Font.Builder(android.content.res.AssetManager, java.lang.String) throws java.io.IOException; + ctor public Font.Builder(android.content.res.Resources, int) throws java.io.IOException; + method public android.graphics.fonts.Font build(); + method public android.graphics.fonts.Font.Builder setFontVariationSettings(java.lang.String); + method public android.graphics.fonts.Font.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]); + method public android.graphics.fonts.Font.Builder setItalic(boolean); + method public android.graphics.fonts.Font.Builder setTtcIndex(int); + method public android.graphics.fonts.Font.Builder setWeight(int); + } + public final class FontVariationAxis { ctor public FontVariationAxis(java.lang.String, float); method public static android.graphics.fonts.FontVariationAxis[] fromFontVariationSettings(java.lang.String); diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 3d80f3d91f59..6856e2942f21 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -154,6 +154,7 @@ cc_library_shared { "android/graphics/Typeface.cpp", "android/graphics/Utils.cpp", "android/graphics/YuvToJpegEncoder.cpp", + "android/graphics/fonts/Font.cpp", "android/graphics/pdf/PdfDocument.cpp", "android/graphics/pdf/PdfEditor.cpp", "android/graphics/pdf/PdfRenderer.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 0c625a773df3..1820888f9c0a 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -140,6 +140,7 @@ extern int register_android_graphics_Region(JNIEnv* env); extern int register_android_graphics_SurfaceTexture(JNIEnv* env); extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env); extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env); +extern int register_android_graphics_fonts_Font(JNIEnv* env); extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env); extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env); extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env); @@ -1406,6 +1407,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_YuvImage), REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable), REG_JNI(register_android_graphics_drawable_VectorDrawable), + REG_JNI(register_android_graphics_fonts_Font), REG_JNI(register_android_graphics_pdf_PdfDocument), REG_JNI(register_android_graphics_pdf_PdfEditor), REG_JNI(register_android_graphics_pdf_PdfRenderer), diff --git a/core/jni/android/graphics/FontUtils.h b/core/jni/android/graphics/FontUtils.h index 9eaaa4964b66..9f6462e67050 100644 --- a/core/jni/android/graphics/FontUtils.h +++ b/core/jni/android/graphics/FontUtils.h @@ -20,6 +20,8 @@ #include <jni.h> #include <memory> +#include <minikin/Font.h> + namespace minikin { class FontFamily; } // namespace minikin @@ -31,6 +33,11 @@ struct FontFamilyWrapper { std::shared_ptr<minikin::FontFamily> family; }; +struct FontWrapper { + FontWrapper(minikin::Font&& font) : font(std::move(font)) {} + minikin::Font font; +}; + // Utility wrapper for java.util.List class ListHelper { public: diff --git a/core/jni/android/graphics/fonts/Font.cpp b/core/jni/android/graphics/fonts/Font.cpp new file mode 100644 index 000000000000..2d1d7a0c4aea --- /dev/null +++ b/core/jni/android/graphics/fonts/Font.cpp @@ -0,0 +1,205 @@ +/* + * 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. + */ + +#define LOG_TAG "Minikin" + +#include <nativehelper/JNIHelp.h> +#include <core_jni_helpers.h> + +#include "SkData.h" +#include "SkFontMgr.h" +#include "SkRefCnt.h" +#include "SkTypeface.h" +#include "GraphicsJNI.h" +#include <nativehelper/ScopedUtfChars.h> +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/android_util_AssetManager.h> +#include <androidfw/AssetManager2.h> +#include "Utils.h" +#include "FontUtils.h" + +#include <hwui/MinikinSkia.h> +#include <hwui/Typeface.h> +#include <utils/FatVector.h> +#include <minikin/FontFamily.h> + +#include <memory> + +namespace android { + +struct NativeFontBuilder { + std::vector<minikin::FontVariation> axes; +}; + +static inline NativeFontBuilder* toBuilder(jlong ptr) { + return reinterpret_cast<NativeFontBuilder*>(ptr); +} + +static inline Asset* toAsset(jlong ptr) { + return reinterpret_cast<Asset*>(ptr); +} + +static void releaseAsset(jlong asset) { + delete toAsset(asset); +} + +static void releaseFont(jlong font) { + delete reinterpret_cast<FontWrapper*>(font); +} + +static void release_global_ref(const void* /*data*/, void* context) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (env == nullptr) { + JavaVMAttachArgs args; + args.version = JNI_VERSION_1_4; + args.name = "release_font_data"; + args.group = nullptr; + jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args); + if (result != JNI_OK) { + ALOGE("failed to attach to thread to release global ref."); + return; + } + } + + jobject obj = reinterpret_cast<jobject>(context); + env->DeleteGlobalRef(obj); +} + +// Regular JNI +static jlong Font_Builder_getNativeAsset( + JNIEnv* env, jobject clazz, jobject assetMgr, jstring path, jboolean isAsset, jint cookie) { + NPE_CHECK_RETURN_ZERO(env, assetMgr); + NPE_CHECK_RETURN_ZERO(env, path); + + Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, assetMgr); + if (mgr == nullptr) { + return 0; + } + + ScopedUtfChars str(env, path); + if (str.c_str() == nullptr) { + return 0; + } + + std::unique_ptr<Asset> asset; + { + ScopedLock<AssetManager2> locked_mgr(*mgr); + if (isAsset) { + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + } else if (cookie > 0) { + // Valid java cookies are 1-based, but AssetManager cookies are 0-based. + asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1), + Asset::ACCESS_BUFFER); + } else { + asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + } + } + + return reinterpret_cast<jlong>(asset.release()); +} + +// Regular JNI +static jobject Font_Builder_getAssetBuffer(JNIEnv* env, jobject clazz, jlong nativeAsset) { + Asset* asset = toAsset(nativeAsset); + return env->NewDirectByteBuffer(const_cast<void*>(asset->getBuffer(false)), asset->getLength()); +} + +// CriticalNative +static jlong Font_Builder_getReleaseNativeAssetFunc() { + return reinterpret_cast<jlong>(&releaseAsset); +} + +// Regular JNI +static jlong Font_Builder_initBuilder(JNIEnv*, jobject) { + return reinterpret_cast<jlong>(new NativeFontBuilder()); +} + +// Critical Native +static void Font_Builder_addAxis(jlong builderPtr, jint tag, jfloat value) { + toBuilder(builderPtr)->axes.emplace_back(static_cast<minikin::AxisTag>(tag), value); +} + +// Regular JNI +static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jobject buffer, + jint weight, jboolean italic, jint ttcIndex) { + NPE_CHECK_RETURN_ZERO(env, buffer); + std::unique_ptr<NativeFontBuilder> 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; + } + jobject fontRef = MakeGlobalRefOrDie(env, buffer); + sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize, + release_global_ref, reinterpret_cast<void*>(fontRef))); + + uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes; + for (const auto& axis : builder->axes) { + skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value}); + } + + std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data))); + + SkFontArguments params; + params.setCollectionIndex(ttcIndex); + params.setAxes(skiaAxes.data(), skiaAxes.size()); + + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); + sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params)); + if (face == nullptr) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Failed to create internal object. maybe invalid font data."); + return 0; + } + std::shared_ptr<minikin::MinikinFont> minikinFont = + std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex, + builder->axes); + minikin::Font font = minikin::Font::Builder(minikinFont).setWeight(weight) + .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build(); + return reinterpret_cast<jlong>(new FontWrapper(std::move(font))); +} + +// Critical Native +static jlong Font_Builder_getReleaseNativeFont() { + return reinterpret_cast<jlong>(releaseFont); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gFontBuilderMethods[] = { + { "nInitBuilder", "()J", (void*) Font_Builder_initBuilder }, + { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis }, + { "nBuild", "(JLjava/nio/ByteBuffer;IZI)J", (void*) Font_Builder_build }, + { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont }, + + { "nGetNativeAsset", "(Landroid/content/res/AssetManager;Ljava/lang/String;ZI)J", + (void*) Font_Builder_getNativeAsset }, + { "nGetAssetBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) Font_Builder_getAssetBuffer }, + { "nGetReleaseNativeAssetFunc", "()J", (void*) Font_Builder_getReleaseNativeAssetFunc }, +}; + +int register_android_graphics_fonts_Font(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods, + NELEM(gFontBuilderMethods)); +} + +} diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java new file mode 100644 index 000000000000..9da61db94780 --- /dev/null +++ b/graphics/java/android/graphics/fonts/Font.java @@ -0,0 +1,472 @@ +/* + * 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. + */ + +package android.graphics.fonts; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.AssetManager; +import android.content.res.Resources; +import android.util.TypedValue; + +import com.android.internal.util.Preconditions; + +import dalvik.annotation.optimization.CriticalNative; + +import libcore.util.NativeAllocationRegistry; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +/** + * A font class can be used for creating FontFamily. + */ +public class Font { + private static final String TAG = "Font"; + + private static final int NOT_SPECIFIED = -1; + private static final int STYLE_ITALIC = 1; + private static final int STYLE_NORMAL = 0; + + /** + * A font weight value for the thin weight + */ + public static final int FONT_WEIGHT_THIN = 100; + + /** + * A font weight value for the extra-light weight + */ + public static final int FONT_WEIGHT_EXTRA_LIGHT = 200; + + /** + * A font weight value for the light weight + */ + public static final int FONT_WEIGHT_LIGHT = 300; + + /** + * A font weight value for the normal weight + */ + public static final int FONT_WEIGHT_NORMAL = 400; + + /** + * A font weight value for the medium weight + */ + public static final int FONT_WEIGHT_MEDIUM = 500; + + /** + * A font weight value for the semi-bold weight + */ + public static final int FONT_WEIGHT_SEMI_BOLD = 600; + + /** + * A font weight value for the bold weight. + */ + public static final int FONT_WEIGHT_BOLD = 700; + + /** + * A font weight value for the extra-bold weight + */ + public static final int FONT_WEIGHT_EXTRA_BOLD = 800; + + /** + * A font weight value for the black weight + */ + public static final int FONT_WEIGHT_BLACK = 900; + + /** + * A builder class for creating new Font. + */ + public static class Builder { + private static final NativeAllocationRegistry sAssetByteBufferRegistroy = + new NativeAllocationRegistry(ByteBuffer.class.getClassLoader(), + nGetReleaseNativeAssetFunc(), 64); + + private static final NativeAllocationRegistry sFontRegistory = + new NativeAllocationRegistry(Font.class.getClassLoader(), + nGetReleaseNativeFont(), 64); + + private @Nullable ByteBuffer mBuffer; + private @IntRange(from = -1, to = 1000) int mWeight = NOT_SPECIFIED; + private @IntRange(from = -1, to = 1) int mItalic = NOT_SPECIFIED; + private @IntRange(from = 0) int mTtcIndex = 0; + private @Nullable FontVariationAxis[] mAxes = null; + + /** + * Constructs a builder with a byte buffer. + * + * Note that only direct buffer can be used as the source of font data. + * + * @see ByteBuffer#allocateDirect(int) + * @param buffer a byte buffer of a font data + */ + public Builder(@NonNull ByteBuffer buffer) { + Preconditions.checkNotNull(buffer, "buffer can not be null"); + if (!buffer.isDirect()) { + throw new IllegalArgumentException( + "Only direct buffer can be used as the source of font data."); + } + mBuffer = buffer; + } + + /** + * Constructs a builder with a file path. + * + * @param path a file path to the font file + */ + public Builder(@NonNull File path) throws IOException { + Preconditions.checkNotNull(path, "path can not be null"); + try (FileInputStream fis = new FileInputStream(path)) { + final FileChannel fc = fis.getChannel(); + mBuffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); + } + } + + /** + * Constructs a builder with a file descriptor. + * + * @param fd a file descriptor + */ + public Builder(@NonNull FileDescriptor fd) throws IOException { + this(fd, 0, -1); + } + + /** + * Constructs a builder with a file descriptor. + * + * @param fd a file descriptor + * @param offset an offset to of the font data in the file + * @param size a size of the font data. If -1 is passed, use until end of the file. + */ + public Builder(@NonNull FileDescriptor fd, @IntRange(from = 0) long offset, + @IntRange(from = -1) long size) throws IOException { + try (FileInputStream fis = new FileInputStream(fd)) { + final FileChannel fc = fis.getChannel(); + size = (size == -1) ? fc.size() - offset : size; + mBuffer = fc.map(FileChannel.MapMode.READ_ONLY, offset, size); + } + } + + /** + * Constructs a builder from an asset manager and a file path in an asset directory. + * + * @param am the application's asset manager + * @param path the file name of the font data in the asset directory + */ + public Builder(@NonNull AssetManager am, @NonNull String path) throws IOException { + final long nativeAsset = nGetNativeAsset(am, path, true /* is asset */, 0 /* cookie */); + if (nativeAsset == 0) { + throw new FileNotFoundException("Unable to open " + path); + } + final ByteBuffer b = nGetAssetBuffer(nativeAsset); + sAssetByteBufferRegistroy.registerNativeAllocation(b, nativeAsset); + if (b == null) { + throw new FileNotFoundException(path + " not found"); + } + mBuffer = b; + } + + /** + * Constructs a builder from resources. + * + * Resource ID must points the font file. XML font can not be used here. + * + * @param res the resource of this application. + * @param resId the resource ID of font file. + */ + public Builder(@NonNull Resources res, int resId) throws IOException { + final TypedValue value = new TypedValue(); + res.getValue(resId, value, true); + if (value.string == null) { + throw new FileNotFoundException(resId + " not found"); + } + final String str = value.string.toString(); + if (str.toLowerCase().endsWith(".xml")) { + throw new FileNotFoundException(resId + " must be font file."); + } + final long nativeAsset = nGetNativeAsset(res.getAssets(), str, false /* is asset */, + value.assetCookie); + if (nativeAsset == 0) { + throw new FileNotFoundException("Unable to open " + str); + } + final ByteBuffer b = nGetAssetBuffer(nativeAsset); + sAssetByteBufferRegistroy.registerNativeAllocation(b, nativeAsset); + if (b == null) { + throw new FileNotFoundException(str + " not found"); + } + mBuffer = b; + } + + /** + * Sets weight of the font. + * + * Tells the system the weight of the given font. If this function is not called, the system + * will resolve the weight value by reading font tables. + * + * Here are pairs of the common names and their values. + * <p> + * <table> + * <thead> + * <tr> + * <th align="center">Value</th> + * <th align="center">Name</th> + * <th align="center">Android Definition</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td align="center">100</td> + * <td align="center">Thin</td> + * <td align="center">{@link Font#FONT_WEIGHT_THIN}</td> + * </tr> + * <tr> + * <td align="center">200</td> + * <td align="center">Extra Light (Ultra Light)</td> + * <td align="center">{@link Font#FONT_WEIGHT_EXTRA_LIGHT}</td> + * </tr> + * <tr> + * <td align="center">300</td> + * <td align="center">Light</td> + * <td align="center">{@link Font#FONT_WEIGHT_LIGHT}</td> + * </tr> + * <tr> + * <td align="center">400</td> + * <td align="center">Normal (Regular)</td> + * <td align="center">{@link Font#FONT_WEIGHT_NORMAL}</td> + * </tr> + * <tr> + * <td align="center">500</td> + * <td align="center">Medium</td> + * <td align="center">{@link Font#FONT_WEIGHT_MEDIUM}</td> + * </tr> + * <tr> + * <td align="center">600</td> + * <td align="center">Semi Bold (Demi Bold)</td> + * <td align="center">{@link Font#FONT_WEIGHT_SEMI_BOLD}</td> + * </tr> + * <tr> + * <td align="center">700</td> + * <td align="center">Bold</td> + * <td align="center">{@link Font#FONT_WEIGHT_BOLD}</td> + * </tr> + * <tr> + * <td align="center">800</td> + * <td align="center">Extra Bold (Ultra Bold)</td> + * <td align="center">{@link Font#FONT_WEIGHT_EXTRA_BOLD}</td> + * </tr> + * <tr> + * <td align="center">900</td> + * <td align="center">Black (Heavy)</td> + * <td align="center">{@link Font#FONT_WEIGHT_BLACK}</td> + * </tr> + * </tbody> + * </p> + * + * @see Font#FONT_WEIGHT_THIN + * @see Font#FONT_WEIGHT_EXTRA_LIGHT + * @see Font#FONT_WEIGHT_LIGHT + * @see Font#FONT_WEIGHT_NORMAL + * @see Font#FONT_WEIGHT_MEDIUM + * @see Font#FONT_WEIGHT_SEMI_BOLD + * @see Font#FONT_WEIGHT_BOLD + * @see Font#FONT_WEIGHT_EXTRA_BOLD + * @see Font#FONT_WEIGHT_BLACK + * @param weight a weight value + * @return this builder + */ + public @NonNull Builder setWeight(@IntRange(from = 1, to = 1000) int weight) { + Preconditions.checkArgument(1 <= weight && weight <= 1000); + mWeight = weight; + return this; + } + + /** + * Sets italic information of the font. + * + * Tells the system the style of the given font. If this function is not called, the system + * will resolve the style by reading font tables. + * + * For example, if you want to use italic font as upright font, call {@code + * setItalic(false)} explicitly. + * + * @param italic {@code true} if the font is italic. Otherwise {@code false}. + * @return this builder + */ + public @NonNull Builder setItalic(boolean italic) { + mItalic = italic ? STYLE_ITALIC : STYLE_NORMAL; + return this; + } + + /** + * Sets an index of the font collection. See {@link android.R.attr#ttcIndex}. + * + * @param ttcIndex An index of the font collection. If the font source is not font + * collection, do not call this method or specify 0. + * @return this builder + */ + public @NonNull Builder setTtcIndex(@IntRange(from = 0) int ttcIndex) { + mTtcIndex = ttcIndex; + return this; + } + + /** + * Sets the font variation settings. + * + * @param variationSettings see {@link FontVariationAxis#fromFontVariationSettings(String)} + * @return this builder + * @throws IllegalArgumentException If given string is not a valid font variation settings + * format. + */ + public @NonNull Builder setFontVariationSettings(@Nullable String variationSettings) { + mAxes = FontVariationAxis.fromFontVariationSettings(variationSettings); + return this; + } + + /** + * Sets the font variation settings. + * + * @param axes an array of font variation axis tag-value pairs + * @return this builder + */ + public @NonNull Builder setFontVariationSettings(@Nullable FontVariationAxis[] axes) { + mAxes = axes; + return this; + } + + /** + * Creates the font based on the configured values. + * @return the Font object + */ + public @Nullable Font build() { + if (mWeight == NOT_SPECIFIED || mItalic == NOT_SPECIFIED) { + final int packed = FontFileUtil.analyzeStyle(mBuffer, mTtcIndex, mAxes); + if (FontFileUtil.isSuccess(packed)) { + if (mWeight == NOT_SPECIFIED) { + mWeight = FontFileUtil.unpackWeight(packed); + } + if (mItalic == NOT_SPECIFIED) { + mItalic = FontFileUtil.unpackItalic(packed) ? STYLE_ITALIC : STYLE_NORMAL; + } + } else { + mWeight = 400; + mItalic = STYLE_NORMAL; + } + } + final boolean italic = (mItalic == STYLE_ITALIC); + final long builderPtr = nInitBuilder(); + if (mAxes != null) { + for (FontVariationAxis axis : mAxes) { + nAddAxis(builderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue()); + } + } + final long ptr = nBuild(builderPtr, mBuffer, mWeight, italic, mTtcIndex); + final Font font = new Font(ptr, mWeight, italic, mTtcIndex, mAxes); + sFontRegistory.registerNativeAllocation(font, ptr); + return font; + } + + /** + * Native methods for accessing underlying buffer in Asset + */ + private static native long nGetNativeAsset( + @NonNull AssetManager am, @NonNull String path, boolean isAsset, int cookie); + private static native ByteBuffer nGetAssetBuffer(long nativeAsset); + @CriticalNative + private static native long nGetReleaseNativeAssetFunc(); + + /** + * Native methods for creating Font + */ + private static native long nInitBuilder(); + @CriticalNative + private static native void nAddAxis(long builderPtr, int tag, float value); + private static native long nBuild( + long builderPtr, ByteBuffer buffer, int weight, boolean italic, int ttcIndex); + @CriticalNative + private static native long nGetReleaseNativeFont(); + } + + private final long mNativePtr; // address of the shared ptr of minikin::Font + private final @IntRange(from = 0, to = 1000) int mWeight; + private final boolean mItalic; + private final @IntRange(from = 0) int mTtcIndex; + private final @Nullable FontVariationAxis[] mAxes; + + /** + * Use Builder instead + */ + private Font(long nativePtr, @IntRange(from = 0, to = 1000) int weight, boolean italic, + @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] axes) { + mWeight = weight; + mItalic = italic; + mNativePtr = nativePtr; + mTtcIndex = ttcIndex; + mAxes = axes; + } + + /** + * Get a weight value associated with this font. + * + * @see Builder#setWeight(int) + * @return a weight value + */ + public @IntRange(from = 0, to = 1000)int getWeight() { + return mWeight; + } + + /** + * Returns true if this font is marked as italic, otherwise returns false. + * + * @see Builder#setItalic(boolean) + * @return true if italic, otherwise false + */ + public boolean isItalic() { + return mItalic; + } + + /** + * Get a TTC index value associated with this font. + * + * If TTF/OTF file is provided, this value is always 0. + * + * @see Builder#setTtcIndex(int) + * @return a TTC index value + */ + public @IntRange(from = 0) int getTtcIndex() { + return mTtcIndex; + } + + /** + * Get a font variation settings associated with this font + * + * @see Builder#setFontVariationSettings(String) + * @see Builder#setFontVariationSettings(FontVariationAxis[]) + * @return font variation settings + */ + public @Nullable FontVariationAxis[] getAxes() { + return mAxes; + } + + /** @hide */ + public long getNativePtr() { + return mNativePtr; + } +} diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java index d15f581f918c..f8b456b982c5 100644 --- a/graphics/java/android/graphics/fonts/FontFileUtil.java +++ b/graphics/java/android/graphics/fonts/FontFileUtil.java @@ -20,7 +20,6 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; -import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -46,6 +45,13 @@ public class FontFileUtil { return (packed & 0x10000) != 0; } + /** + * Returns true if the analyzeStyle succeeded + */ + public static boolean isSuccess(int packed) { + return packed != ANALYZE_ERROR; + } + private static int pack(@IntRange(from = 0, to = 1000) int weight, boolean italic) { return weight | (italic ? 0x10000 : 0); } @@ -55,12 +61,13 @@ public class FontFileUtil { private static final int TTC_TAG = 0x74746366; private static final int OS2_TABLE_TAG = 0x4F532F32; + private static final int ANALYZE_ERROR = 0xFFFFFFFF; + /** * Analyze the font file returns packed style info */ public static final int analyzeStyle(@NonNull ByteBuffer buffer, - @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] varSettings) - throws IOException { + @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] varSettings) { int weight = -1; int italic = -1; if (varSettings != null) { @@ -88,7 +95,7 @@ public class FontFileUtil { if (magicNumber == TTC_TAG) { // TTC file. if (ttcIndex >= buffer.getInt(8 /* offset to number of fonts in TTC */)) { - throw new IOException("Font index out of bounds"); + return ANALYZE_ERROR; } fontFileOffset = buffer.getInt( 12 /* offset to array of offsets of font files */ + 4 * ttcIndex); @@ -96,7 +103,7 @@ public class FontFileUtil { int sfntVersion = buffer.getInt(fontFileOffset); if (sfntVersion != SFNT_VERSION_1 && sfntVersion != SFNT_VERSION_OTTO) { - throw new IOException("Unknown font file format"); + return ANALYZE_ERROR; } int numTables = buffer.getShort(fontFileOffset + 4 /* offset to number of tables */); |