Merge "Introduce FontFamily and its builder"
diff --git a/api/current.txt b/api/current.txt
index 7f4fae5..d70d1e7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15211,6 +15211,17 @@
method public android.graphics.fonts.Font.Builder setWeight(int);
}
+ public class FontFamily {
+ method public android.graphics.fonts.Font getFont(int);
+ method public int getFontCount();
+ }
+
+ public static class FontFamily.Builder {
+ ctor public FontFamily.Builder(android.graphics.fonts.Font);
+ method public android.graphics.fonts.FontFamily.Builder addFont(android.graphics.fonts.Font);
+ method public android.graphics.fonts.FontFamily build();
+ }
+
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 6856e29..6316da5 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -155,6 +155,7 @@
"android/graphics/Utils.cpp",
"android/graphics/YuvToJpegEncoder.cpp",
"android/graphics/fonts/Font.cpp",
+ "android/graphics/fonts/FontFamily.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 1820888..7fe095b 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -141,6 +141,7 @@
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_fonts_FontFamily(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);
@@ -1408,6 +1409,7 @@
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_fonts_FontFamily),
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/fonts/FontFamily.cpp b/core/jni/android/graphics/fonts/FontFamily.cpp
new file mode 100644
index 0000000..4597386
--- /dev/null
+++ b/core/jni/android/graphics/fonts/FontFamily.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 "FontUtils.h"
+
+#include <minikin/FontFamily.h>
+
+#include <memory>
+
+namespace android {
+
+struct NativeFamilyBuilder {
+ std::vector<minikin::Font> fonts;
+};
+
+static inline NativeFamilyBuilder* toBuilder(jlong ptr) {
+ return reinterpret_cast<NativeFamilyBuilder*>(ptr);
+}
+
+static inline FontWrapper* toFontWrapper(jlong ptr) {
+ return reinterpret_cast<FontWrapper*>(ptr);
+}
+
+static void releaseFontFamily(jlong family) {
+ delete reinterpret_cast<FontFamilyWrapper*>(family);
+}
+
+// Regular JNI
+static jlong FontFamily_Builder_initBuilder(JNIEnv*, jobject) {
+ return reinterpret_cast<jlong>(new NativeFamilyBuilder());
+}
+
+// Critical Native
+static void FontFamily_Builder_addFont(jlong builderPtr, jlong fontPtr) {
+ toBuilder(builderPtr)->fonts.push_back(toFontWrapper(fontPtr)->font);
+}
+
+// Regular JNI
+static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr) {
+ std::unique_ptr<NativeFamilyBuilder> builder(toBuilder(builderPtr));
+ std::shared_ptr<minikin::FontFamily> family =
+ std::make_shared<minikin::FontFamily>(std::move(builder->fonts));
+ if (family->getCoverage().length() == 0) {
+ // No coverage means minikin rejected given font for some reasons.
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "Failed to create internal object. maybe invalid font data");
+ return 0;
+ }
+ return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
+}
+
+// CriticalNative
+static jlong FontFamily_Builder_GetReleaseFunc() {
+ return reinterpret_cast<jlong>(releaseFontFamily);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gFontFamilyBuilderMethods[] = {
+ { "nInitBuilder", "()J", (void*) FontFamily_Builder_initBuilder },
+ { "nAddFont", "(JJ)V", (void*) FontFamily_Builder_addFont },
+ { "nBuild", "(J)J", (void*) FontFamily_Builder_build },
+
+ { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
+};
+
+int register_android_graphics_fonts_FontFamily(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily$Builder",
+ gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods));
+}
+
+}
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 9da61db9..9d94a64 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -469,4 +469,9 @@
public long getNativePtr() {
return mNativePtr;
}
+
+ @Override
+ public String toString() {
+ return "Font {weight=" + mWeight + ", italic=" + mItalic + "}";
+ }
}
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
new file mode 100644
index 0000000..74b58ea
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -0,0 +1,164 @@
+/*
+ * 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 com.android.internal.util.Preconditions;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * A font family class can be used for creating Typeface.
+ *
+ * <p>
+ * A font family is a bundle of fonts for drawing text in various styles.
+ * For example, you can bundle regular style font and bold style font into a single font family,
+ * then system will select the correct style font from family for drawing.
+ *
+ * <pre>
+ * FontFamily family = new FontFamily.Builder(new Font.Builder("regular.ttf").build())
+ * .addFont(new Font.Builder("bold.ttf").build()).build();
+ * Typeface typeface = new Typeface.Builder2(family).build();
+ *
+ * SpannableStringBuilder ssb = new SpannableStringBuilder("Hello, World.");
+ * ssb.setSpan(new StyleSpan(Typeface.Bold), 6, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ *
+ * textView.setTypeface(typeface);
+ * textView.setText(ssb);
+ * </pre>
+ *
+ * In this example, "Hello, " is drawn with "regular.ttf", and "World." is drawn with "bold.ttf".
+ *
+ * If there is no font exactly matches with the text style, the system will select the closest font.
+ * </p>
+ *
+ */
+public class FontFamily {
+ private static final String TAG = "FontFamily";
+
+ /**
+ * A builder class for creating new FontFamily.
+ */
+ public static class Builder {
+ private static final NativeAllocationRegistry sFamilyRegistory =
+ new NativeAllocationRegistry(FontFamily.class.getClassLoader(),
+ nGetReleaseNativeFamily(), 64);
+
+ private final ArrayList<Font> mFonts = new ArrayList<>();
+ private final HashSet<Integer> mStyleHashSet = new HashSet<>();
+
+ /**
+ * Constructs a builder.
+ *
+ * @param font a font
+ */
+ public Builder(@NonNull Font font) {
+ Preconditions.checkNotNull(font, "font can not be null");
+ mStyleHashSet.add(makeStyleIdentifier(font));
+ mFonts.add(font);
+ }
+
+ /**
+ * Adds different style font to the builder.
+ *
+ * System will select the font if the text style is closest to the font.
+ * If the same style font is already added to the builder, this method will fail with
+ * {@link IllegalArgumentException}.
+ *
+ * Note that system assumes all fonts bundled in FontFamily have the same coverage for the
+ * code points. For example, regular style font and bold style font must have the same code
+ * point coverage, otherwise some character may be shown as tofu.
+ *
+ * @param font a font
+ * @return this builder
+ */
+ public @NonNull Builder addFont(@NonNull Font font) {
+ Preconditions.checkNotNull(font, "font can not be null");
+ if (!mStyleHashSet.add(makeStyleIdentifier(font))) {
+ throw new IllegalArgumentException(font + " has already been added");
+ }
+ mFonts.add(font);
+ return this;
+ }
+
+ /**
+ * Build the font family
+ * @return a font family
+ */
+ public @NonNull FontFamily build() {
+ final long builderPtr = nInitBuilder();
+ for (int i = 0; i < mFonts.size(); ++i) {
+ nAddFont(builderPtr, mFonts.get(i).getNativePtr());
+ }
+ final long ptr = nBuild(builderPtr);
+ final FontFamily family = new FontFamily(mFonts, ptr);
+ sFamilyRegistory.registerNativeAllocation(family, ptr);
+ return family;
+ }
+
+ private static int makeStyleIdentifier(@NonNull Font font) {
+ return font.getWeight() | (font.isItalic() ? (1 << 16) : 0);
+ }
+
+ private static native long nInitBuilder();
+ @CriticalNative
+ private static native void nAddFont(long builderPtr, long fontPtr);
+ private static native long nBuild(long builderPtr);
+ @CriticalNative
+ private static native long nGetReleaseNativeFamily();
+ }
+
+ private final ArrayList<Font> mFonts;
+ private final long mNativePtr;
+
+ // Use Builder instead.
+ private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
+ mFonts = fonts;
+ mNativePtr = ptr;
+ }
+
+ /**
+ * Returns a font
+ *
+ * @param index an index of the font
+ * @return a registered font
+ */
+ public Font getFont(@IntRange(from = 0) int index) {
+ return mFonts.get(index);
+ }
+
+ /**
+ * Returns the number of fonts in this FontFamily.
+ *
+ * @return the number of fonts registered in this family.
+ */
+ public int getFontCount() {
+ return mFonts.size();
+ }
+
+ /** @hide */
+ public long getNativePtr() {
+ return mNativePtr;
+ }
+}