Merge "Introduce Font and its builder class"
diff --git a/api/current.txt b/api/current.txt
index d971f18..498ab95 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15178,6 +15178,37 @@
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 3d80f3d..6856e29 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -154,6 +154,7 @@
"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 0c625a7..1820888 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -140,6 +140,7 @@
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 @@
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 9eaaa49..9f6462e 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 @@
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 0000000..2d1d7a0
--- /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 0000000..9da61db9
--- /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 d15f581..f8b456b 100644
--- a/graphics/java/android/graphics/fonts/FontFileUtil.java
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -46,6 +45,13 @@
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 @@
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 @@
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 @@
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 */);