blob: 75ea1206292990f3069e09f55575cd2b59b5875a [file] [log] [blame]
/*
* 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.text.FontConfig;
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 final class FontFamily {
private static final String TAG = "FontFamily";
/**
* A builder class for creating new FontFamily.
*/
public static final class Builder {
private static final NativeAllocationRegistry sFamilyRegistory =
NativeAllocationRegistry.createMalloced(FontFamily.class.getClassLoader(),
nGetReleaseNativeFamily());
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() {
return build("", FontConfig.Family.VARIANT_DEFAULT, true /* isCustomFallback */);
}
/** @hide */
public @NonNull FontFamily build(@NonNull String langTags, int variant,
boolean isCustomFallback) {
final long builderPtr = nInitBuilder();
for (int i = 0; i < mFonts.size(); ++i) {
nAddFont(builderPtr, mFonts.get(i).getNativePtr());
}
final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback);
final FontFamily family = new FontFamily(mFonts, ptr);
sFamilyRegistory.registerNativeAllocation(family, ptr);
return family;
}
private static int makeStyleIdentifier(@NonNull Font font) {
return font.getStyle().getWeight() | (font.getStyle().getSlant() << 16);
}
private static native long nInitBuilder();
@CriticalNative
private static native void nAddFont(long builderPtr, long fontPtr);
private static native long nBuild(long builderPtr, String langTags, int variant,
boolean isCustomFallback);
@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 @NonNull 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 @IntRange(from = 1) int getSize() {
return mFonts.size();
}
/** @hide */
public long getNativePtr() {
return mNativePtr;
}
}