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

Commit 3a282056 authored by Seigo Nonaka's avatar Seigo Nonaka
Browse files

Create Font sets from native fonts

Bug: 173752727
Test: atest SystemFontsTest (with enabling LAZY flag)
Change-Id: Ic4e405a68e355919ee6493e51528fd44d79cd48e
parent 398ef538
Loading
Loading
Loading
Loading
+20 −32
Original line number Diff line number Diff line
@@ -123,6 +123,19 @@ public final class Font {
            mLocaleList = localeList;
        }

        /**
         * Construct a builder with a byte buffer and file path.
         *
         * This method is intended to be called only from SystemFonts.
         * @param path font file path
         * @param localeList comma concatenated BCP47 compliant language tag.
         * @hide
         */
        public Builder(@NonNull File path, @NonNull String localeList) {
            this(path);
            mLocaleList = localeList;
        }

        /**
         * Constructs a builder with a file path.
         *
@@ -809,29 +822,13 @@ public final class Font {

            // If not found, create Font object from native object for Java API users.
            ByteBuffer buffer = NativeFontBufferHelper.refByteBuffer(ptr);
            long packed = nGetFontInfo(ptr);
            int weight = (int) (packed & 0x0000_0000_0000_FFFFL);
            boolean italic = (packed & 0x0000_0000_0001_0000L) != 0;
            int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32);
            int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48);
            FontVariationAxis[] axes = new FontVariationAxis[axisCount];
            char[] charBuffer = new char[4];
            for (int i = 0; i < axisCount; ++i) {
                long packedAxis = nGetAxisInfo(ptr, i);
                float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
                charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56);
                charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48);
                charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40);
                charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32);
                axes[i] = new FontVariationAxis(new String(charBuffer), value);
            }
            String path = nGetFontPath(ptr);
            File file = (path == null) ? null : new File(path);
            Font.Builder builder = new Font.Builder(buffer, file, "")
                    .setWeight(weight)
                    .setSlant(italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT)
                    .setTtcIndex(ttcIndex)
                    .setFontVariationSettings(axes);
            NativeFont.Font font = NativeFont.readNativeFont(ptr);

            Font.Builder builder = new Font.Builder(buffer, font.getFile(), "")
                    .setWeight(font.getStyle().getWeight())
                    .setSlant(font.getStyle().getSlant())
                    .setTtcIndex(font.getIndex())
                    .setFontVariationSettings(font.getAxes());

            Font newFont = null;
            try {
@@ -845,15 +842,6 @@ public final class Font {
        }
    }

    @CriticalNative
    private static native long nGetFontInfo(long ptr);

    @CriticalNative
    private static native long nGetAxisInfo(long ptr, int i);

    @FastNative
    private static native String nGetFontPath(long ptr);

    @FastNative
    private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);

+205 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.graphics.Typeface;

import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * Read native font objects.
 *
 * @hide
 */
public class NativeFont {

    /**
     * Represents native font object.
     */
    public static final class Font {
        private final File mFile;
        private final int mIndex;
        private final FontVariationAxis[] mAxes;
        private final FontStyle mStyle;

        public Font(File file, int index, FontVariationAxis[] axes, FontStyle style) {
            mFile = file;
            mIndex = index;
            mAxes = axes;
            mStyle = style;
        }

        public File getFile() {
            return mFile;
        }

        public FontVariationAxis[] getAxes() {
            return mAxes;
        }

        public FontStyle getStyle() {
            return mStyle;
        }

        public int getIndex() {
            return mIndex;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Font font = (Font) o;
            return mIndex == font.mIndex && mFile.equals(font.mFile)
                    && Arrays.equals(mAxes, font.mAxes) && mStyle.equals(font.mStyle);
        }

        @Override
        public int hashCode() {
            int result = Objects.hash(mFile, mIndex, mStyle);
            result = 31 * result + Arrays.hashCode(mAxes);
            return result;
        }
    }

    /**
     * Represents native font family object.
     */
    public static final class Family {
        private final List<Font> mFonts;
        private final String mLocale;

        public Family(List<Font> fonts, String locale) {
            mFonts = fonts;
            mLocale = locale;
        }

        public List<Font> getFonts() {
            return mFonts;
        }

        public String getLocale() {
            return mLocale;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Family family = (Family) o;
            return mFonts.equals(family.mFonts) && mLocale.equals(family.mLocale);
        }

        @Override
        public int hashCode() {
            return Objects.hash(mFonts, mLocale);
        }
    }

    /**
     * Get underlying font families from Typeface
     *
     * @param typeface a typeface
     * @return list of family
     */
    public static List<Family> readTypeface(Typeface typeface) {
        int familyCount = nGetFamilyCount(typeface.native_instance);
        List<Family> result = new ArrayList<>(familyCount);
        for (int i = 0; i < familyCount; ++i) {
            result.add(readNativeFamily(nGetFamily(typeface.native_instance, i)));
        }
        return result;
    }

    /**
     * Read family object from native pointer
     *
     * @param familyPtr a font family pointer
     * @return a family
     */
    public static Family readNativeFamily(long familyPtr) {
        int fontCount = nGetFontCount(familyPtr);
        List<Font> result = new ArrayList<>(fontCount);
        for (int i = 0; i < fontCount; ++i) {
            result.add(readNativeFont(nGetFont(familyPtr, i)));
        }
        String localeList = nGetLocaleList(familyPtr);
        return new Family(result, localeList);
    }

    /**
     * Read font object from native pointer.
     *
     * @param ptr a font pointer
     * @return a font
     */
    public static Font readNativeFont(long ptr) {
        long packed = nGetFontInfo(ptr);
        int weight = (int) (packed & 0x0000_0000_0000_FFFFL);
        boolean italic = (packed & 0x0000_0000_0001_0000L) != 0;
        int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32);
        int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48);
        FontVariationAxis[] axes = new FontVariationAxis[axisCount];
        char[] charBuffer = new char[4];
        for (int i = 0; i < axisCount; ++i) {
            long packedAxis = nGetAxisInfo(ptr, i);
            float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
            charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56);
            charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48);
            charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40);
            charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32);
            axes[i] = new FontVariationAxis(new String(charBuffer), value);
        }
        String path = nGetFontPath(ptr);
        File file = (path == null) ? null : new File(path);
        FontStyle style = new FontStyle(weight,
                italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT);

        return new Font(file, ttcIndex, axes, style);
    }

    @CriticalNative
    private static native int nGetFamilyCount(long ptr);

    @CriticalNative
    private static native long nGetFamily(long ptr, int index);

    @FastNative
    private static native String nGetLocaleList(long familyPtr);

    @CriticalNative
    private static native long nGetFont(long familyPtr, int fontIndex);

    @CriticalNative
    private static native int nGetFontCount(long familyPtr);

    @CriticalNative
    private static native long nGetFontInfo(long fontPtr);

    @CriticalNative
    private static native long nGetAxisInfo(long fontPtr, int i);

    @FastNative
    private static native String nGetFontPath(long fontPtr);
}
+40 −8
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.graphics.fonts;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.FontListParser;
import android.graphics.Typeface;
import android.text.FontConfig;
import android.util.ArrayMap;
import android.util.Log;
@@ -68,8 +69,10 @@ public final class SystemFonts {
                return sAvailableFonts;
            }

            if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
                sAvailableFonts = collectAllFonts();
            } else {
                Set<Font> set = new HashSet<>();

                for (FontFamily[] items : sFamilyMap.values()) {
                    for (FontFamily family : items) {
                        for (int i = 0; i < family.getSize(); ++i) {
@@ -79,10 +82,39 @@ public final class SystemFonts {
                }

                sAvailableFonts = Collections.unmodifiableSet(set);
            }
            return sAvailableFonts;
        }
    }

    private static @NonNull Set<Font> collectAllFonts() {
        Map<String, Typeface> map = Typeface.getSystemFontMap();
        HashSet<NativeFont.Font> seenFonts = new HashSet<>();
        HashSet<Font> result = new HashSet<>();
        for (Typeface typeface : map.values()) {
            List<NativeFont.Family> families = NativeFont.readTypeface(typeface);
            for (NativeFont.Family family : families) {
                for (NativeFont.Font font : family.getFonts()) {
                    if (seenFonts.contains(font)) {
                        continue;
                    }
                    seenFonts.add(font);
                    try {
                        result.add(new Font.Builder(font.getFile(), family.getLocale())
                                .setFontVariationSettings(font.getAxes())
                                .setTtcIndex(font.getIndex())
                                .setWeight(font.getStyle().getWeight())
                                .setSlant(font.getStyle().getSlant())
                                .build());
                    } catch (IOException e) {
                        Log.w(TAG, "Failed to load " + font.getFile(), e);
                    }
                }
            }
        }
        return result;
    }

    private static @Nullable ByteBuffer mmap(@NonNull String fullPath) {
        try (FileInputStream file = new FileInputStream(fullPath)) {
            final FileChannel fileChannel = file.getChannel();
+1 −0
Original line number Diff line number Diff line
@@ -333,6 +333,7 @@ cc_defaults {
        "jni/YuvToJpegEncoder.cpp",
        "jni/fonts/Font.cpp",
        "jni/fonts/FontFamily.cpp",
        "jni/fonts/NativeFont.cpp",
        "jni/text/LineBreaker.cpp",
        "jni/text/MeasuredText.cpp",
        "jni/text/TextShaper.cpp",
+2 −0
Original line number Diff line number Diff line
@@ -69,6 +69,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_fonts_NativeFont(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);
@@ -135,6 +136,7 @@ static const RegJNIRec gRegJNI[] = {
    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_fonts_NativeFont),
    REG_JNI(register_android_graphics_pdf_PdfDocument),
    REG_JNI(register_android_graphics_pdf_PdfEditor),
    REG_JNI(register_android_graphics_pdf_PdfRenderer),
Loading