Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d7526a47 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Support variable font family"

parents 8a1ac5ef 0579097d
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -17442,6 +17442,7 @@ package android.graphics.fonts {
    ctor public FontFamily.Builder(@NonNull android.graphics.fonts.Font);
    method @NonNull public android.graphics.fonts.FontFamily.Builder addFont(@NonNull android.graphics.fonts.Font);
    method @NonNull public android.graphics.fonts.FontFamily build();
    method @Nullable public android.graphics.fonts.FontFamily buildVariableFamily();
  }
  public final class FontStyle {
@@ -17622,13 +17623,18 @@ package android.graphics.text {
    method public float getAdvance();
    method public float getAscent();
    method public float getDescent();
    method public boolean getFakeBold(@IntRange(from=0) int);
    method public boolean getFakeItalic(@IntRange(from=0) int);
    method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int);
    method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int);
    method public float getGlyphX(@IntRange(from=0) int);
    method public float getGlyphY(@IntRange(from=0) int);
    method public float getItalicOverride(@IntRange(from=0) int);
    method public float getOffsetX();
    method public float getOffsetY();
    method public float getWeightOverride(@IntRange(from=0) int);
    method @IntRange(from=0) public int glyphCount();
    field public static final float NO_OVERRIDE = 1.4E-45f;
  }
  public class TextRunShaper {
+131 −5
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import dalvik.annotation.optimization.FastNative;
import libcore.util.NativeAllocationRegistry;

import java.util.ArrayList;
import java.util.Set;

/**
 * A font family class can be used for creating Typeface.
@@ -58,6 +59,7 @@ import java.util.ArrayList;
 *
 */
public final class FontFamily {

    private static final String TAG = "FontFamily";

    /**
@@ -73,6 +75,7 @@ public final class FontFamily {
        // initial capacity.
        private final SparseIntArray mStyles = new SparseIntArray(4);


        /**
         * Constructs a builder.
         *
@@ -109,24 +112,64 @@ public final class FontFamily {
            return this;
        }

        /**
         * Build a variable font family that automatically adjust the `wght` and `ital` axes value
         * for the requested weight/italic style values.
         *
         * To build a variable font family, added fonts must meet one of following conditions.
         *
         * If two font files are added, both font files must support `wght` axis and one font must
         * support {@link FontStyle#FONT_SLANT_UPRIGHT} and another font must support
         * {@link FontStyle#FONT_SLANT_ITALIC}. If the requested weight value is lower than minimum
         * value of the supported `wght` axis, the minimum supported `wght` value is used. If the
         * requested weight value is larger than maximum value of the supported `wght` axis, the
         * maximum supported `wght` value is used. The weight values of the fonts are ignored.
         *
         * If one font file is added, that font must support the `wght` axis. If that font support
         * `ital` axis, that `ital` value is set to 1 when the italic style is requested. If that
         * font doesn't support `ital` axis, synthetic italic may be used. If the requested
         * weight value is lower than minimum value of the supported `wght` axis, the minimum
         * supported `wght` value is used. If the requested weight value is larger than maximum
         * value of the supported `wght`axis, the maximum supported `wght` value is used. The weight
         * value of the font is ignored.
         *
         * If none of the above conditions are met, this function return {@code null}.
         *
         * @return A variable font family. null if a variable font cannot be built from the given
         *         fonts.
         */
        public @Nullable FontFamily buildVariableFamily() {
            int variableFamilyType = analyzeAndResolveVariableType(mFonts);
            if (variableFamilyType == VARIABLE_FONT_FAMILY_TYPE_UNKNOWN) {
                return null;
            }
            return build("", FontConfig.FontFamily.VARIANT_DEFAULT,
                    true /* isCustomFallback */,
                    false /* isDefaultFallback */,
                    variableFamilyType);
        }

        /**
         * Build the font family
         * @return a font family
         */
        public @NonNull FontFamily build() {
            return build("", FontConfig.FontFamily.VARIANT_DEFAULT, true /* isCustomFallback */,
                    false /* isDefaultFallback */);
            return build("", FontConfig.FontFamily.VARIANT_DEFAULT,
                    true /* isCustomFallback */,
                    false /* isDefaultFallback */,
                    VARIABLE_FONT_FAMILY_TYPE_NONE);
        }

        /** @hide */
        public @NonNull FontFamily build(@NonNull String langTags, int variant,
                boolean isCustomFallback, boolean isDefaultFallback) {
                boolean isCustomFallback, boolean isDefaultFallback, int variableFamilyType) {

            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,
                    isDefaultFallback);
                    isDefaultFallback, variableFamilyType);
            final FontFamily family = new FontFamily(ptr);
            sFamilyRegistory.registerNativeAllocation(family, ptr);
            return family;
@@ -136,11 +179,94 @@ public final class FontFamily {
            return font.getStyle().getWeight() | (font.getStyle().getSlant()  << 16);
        }

        /**
         * @see #buildVariableFamily()
         * @hide
         */
        public static final int VARIABLE_FONT_FAMILY_TYPE_UNKNOWN = -1;

        /**
         * @see #buildVariableFamily()
         * @hide
         */
        public static final int VARIABLE_FONT_FAMILY_TYPE_NONE = 0;
        /**
         * @see #buildVariableFamily()
         * @hide
         */
        public static final int VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY = 1;
        /**
         * @see #buildVariableFamily()
         * @hide
         */
        public static final int VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL = 2;
        /**
         * @see #buildVariableFamily()
         * @hide
         */
        public static final int VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT = 3;

        /**
         * The registered italic axis used for adjusting requested style.
         * https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxistag_ital
         */
        private static final int TAG_ital = 0x6974616C;  // i(0x69), t(0x74), a(0x61), l(0x6c)

        /**
         * The registered weight axis used for adjusting requested style.
         * https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxistag_wght
         */
        private static final int TAG_wght = 0x77676874;  // w(0x77), g(0x67), h(0x68), t(0x74)

        private static int analyzeAndResolveVariableType(ArrayList<Font> fonts) {
            if (fonts.size() > 2) {
                return VARIABLE_FONT_FAMILY_TYPE_UNKNOWN;
            }

            if (fonts.size() == 1) {
                Font font = fonts.get(0);
                Set<Integer> supportedAxes =
                        FontFileUtil.getSupportedAxes(font.getBuffer(), font.getTtcIndex());
                if (supportedAxes.contains(TAG_wght)) {
                    if (supportedAxes.contains(TAG_ital)) {
                        return VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL;
                    } else {
                        return VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY;
                    }
                } else {
                    return VARIABLE_FONT_FAMILY_TYPE_UNKNOWN;
                }
            } else {
                for (int i = 0; i < fonts.size(); ++i) {
                    Font font = fonts.get(i);
                    Set<Integer> supportedAxes =
                            FontFileUtil.getSupportedAxes(font.getBuffer(), font.getTtcIndex());
                    if (!supportedAxes.contains(TAG_wght)) {
                        return VARIABLE_FONT_FAMILY_TYPE_UNKNOWN;
                    }
                }
                boolean italic1 = fonts.get(0).getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC;
                boolean italic2 = fonts.get(1).getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC;

                if (italic1 == italic2) {
                    return VARIABLE_FONT_FAMILY_TYPE_UNKNOWN;
                } else {
                    if (italic1) {
                        // Swap fonts to make the first font upright, second font italic.
                        Font firstFont = fonts.get(0);
                        fonts.set(0, fonts.get(1));
                        fonts.set(1, firstFont);
                    }
                    return VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT;
                }
            }
        }

        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, boolean isDefaultFallback);
                boolean isCustomFallback, boolean isDefaultFallback, int variableFamilyType);
        @CriticalNative
        private static native long nGetReleaseNativeFamily();
    }
+71 −0
Original line number Diff line number Diff line
@@ -19,11 +19,14 @@ package android.graphics.fonts;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArraySet;

import dalvik.annotation.optimization.FastNative;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Collections;
import java.util.Set;

/**
 * Provides a utility for font file operations.
@@ -62,6 +65,7 @@ public class FontFileUtil {
    private static final int SFNT_VERSION_OTTO = 0x4F54544F;
    private static final int TTC_TAG = 0x74746366;
    private static final int OS2_TABLE_TAG = 0x4F532F32;
    private static final int FVAR_TABLE_TAG = 0x66766172;

    private static final int ANALYZE_ERROR = 0xFFFFFFFF;

@@ -200,6 +204,73 @@ public class FontFileUtil {
        }
    }

    private static int getUInt16(ByteBuffer buffer, int offset) {
        return ((int) buffer.getShort(offset)) & 0xFFFF;
    }

    /**
     * Returns supported axes of font
     *
     * @param buffer A buffer of the entire font file.
     * @param index A font index in case of font collection. Must be 0 otherwise.
     * @return set of supported axes tag. Returns empty set on error.
     */
    public static Set<Integer> getSupportedAxes(@NonNull ByteBuffer buffer, int index) {
        ByteOrder originalOrder = buffer.order();
        buffer.order(ByteOrder.BIG_ENDIAN);
        try {
            int fontFileOffset = 0;
            int magicNumber = buffer.getInt(0);
            if (magicNumber == TTC_TAG) {
                // TTC file.
                if (index >= buffer.getInt(8 /* offset to number of fonts in TTC */)) {
                    return Collections.EMPTY_SET;
                }
                fontFileOffset = buffer.getInt(
                        12 /* offset to array of offsets of font files */ + 4 * index);
            }
            int sfntVersion = buffer.getInt(fontFileOffset);

            if (sfntVersion != SFNT_VERSION_1 && sfntVersion != SFNT_VERSION_OTTO) {
                return Collections.EMPTY_SET;
            }

            int numTables = buffer.getShort(fontFileOffset + 4 /* offset to number of tables */);
            int fvarTableOffset = -1;
            for (int i = 0; i < numTables; ++i) {
                int tableOffset = fontFileOffset + 12 /* size of offset table */
                        + i * 16 /* size of table record */;
                if (buffer.getInt(tableOffset) == FVAR_TABLE_TAG) {
                    fvarTableOffset = buffer.getInt(tableOffset + 8 /* offset to the table */);
                    break;
                }
            }

            if (fvarTableOffset == -1) {
                // Couldn't find OS/2 table. use regular style
                return Collections.EMPTY_SET;
            }

            if (buffer.getShort(fvarTableOffset) != 1
                    || buffer.getShort(fvarTableOffset + 2) != 0) {
                return Collections.EMPTY_SET;
            }

            int axesArrayOffset = getUInt16(buffer, fvarTableOffset + 4);
            int axisCount = getUInt16(buffer, fvarTableOffset + 8);
            int axisSize = getUInt16(buffer, fvarTableOffset + 10);

            ArraySet<Integer> axes = new ArraySet<>();
            for (int i = 0; i < axisCount; ++i) {
                axes.add(buffer.getInt(fvarTableOffset + axesArrayOffset + axisSize * i));
            }

            return axes;
        } finally {
            buffer.order(originalOrder);
        }
    }

    @FastNative
    private static native long nGetFontRevision(@NonNull ByteBuffer buffer,
            @IntRange(from = 0) int index);
+1 −1
Original line number Diff line number Diff line
@@ -194,7 +194,7 @@ public final class SystemFonts {
            }
        }
        return b == null ? null : b.build(languageTags, variant, false /* isCustomFallback */,
                isDefaultFallback);
                isDefaultFallback, FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE);
    }

    private static void appendNamedFamilyList(@NonNull FontConfig.NamedFamilyList namedFamilyList,
+70 −0
Original line number Diff line number Diff line
@@ -164,6 +164,68 @@ public final class PositionedGlyphs {
        return nGetY(mLayoutPtr, index) + mYOffset;
    }

    /**
     * Returns true if the fake bold option used for drawing, otherwise false.
     *
     * @param index the glyph index
     * @return true if the fake bold option is on, otherwise off.
     */
    public boolean getFakeBold(@IntRange(from = 0) int index) {
        Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
        return nGetFakeBold(mLayoutPtr, index);
    }

    /**
     * Returns true if the fake italic option used for drawing, otherwise false.
     *
     * @param index the glyph index
     * @return true if the fake italic option is on, otherwise off.
     */
    public boolean getFakeItalic(@IntRange(from = 0) int index) {
        Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
        return nGetFakeItalic(mLayoutPtr, index);
    }

    /**
     * A special value returned by {@link #getWeightOverride(int)} and
     * {@link #getItalicOverride(int)} that indicates no font variation setting is overridden.
     */
    public static final float NO_OVERRIDE = Float.MIN_VALUE;

    /**
     * Returns overridden weight value if the font is variable font and `wght` value is overridden
     * for drawing. Otherwise returns {@link #NO_OVERRIDE}.
     *
     * @param index the glyph index
     * @return overridden weight value or {@link #NO_OVERRIDE}.
     */
    public float getWeightOverride(@IntRange(from = 0) int index) {
        Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
        float value = nGetWeightOverride(mLayoutPtr, index);
        if (value == -1) {
            return NO_OVERRIDE;
        } else {
            return value;
        }
    }

    /**
     * Returns overridden italic value if the font is variable font and `ital` value is overridden
     * for drawing. Otherwise returns {@link #NO_OVERRIDE}.
     *
     * @param index the glyph index
     * @return overridden weight value or {@link #NO_OVERRIDE}.
     */
    public float getItalicOverride(@IntRange(from = 0) int index) {
        Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
        float value = nGetItalicOverride(mLayoutPtr, index);
        if (value == -1) {
            return NO_OVERRIDE;
        } else {
            return value;
        }
    }

    /**
     * Create single style layout from native result.
     *
@@ -210,6 +272,14 @@ public final class PositionedGlyphs {
    private static native long nGetFont(long minikinLayout, int i);
    @CriticalNative
    private static native long nReleaseFunc();
    @CriticalNative
    private static native boolean nGetFakeBold(long minikinLayout, int i);
    @CriticalNative
    private static native boolean nGetFakeItalic(long minikinLayout, int i);
    @CriticalNative
    private static native float nGetWeightOverride(long minikinLayout, int i);
    @CriticalNative
    private static native float nGetItalicOverride(long minikinLayout, int i);

    @Override
    public boolean equals(Object o) {
Loading