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

Commit 47467647 authored by Ben Wagner's avatar Ben Wagner Committed by Android (Google) Code Review
Browse files

Merge changes I5ca69f96,I15623fe8

* changes:
  Deduplicate font file mappings.
  Add support for gx font variation axes.
parents 5da5250a fffcf0a3
Loading
Loading
Loading
Loading
+100 −9
Original line number Diff line number Diff line
@@ -20,11 +20,13 @@
#include <core_jni_helpers.h>

#include "SkData.h"
#include "SkFontMgr.h"
#include "SkRefCnt.h"
#include "SkTypeface.h"
#include "GraphicsJNI.h"
#include <ScopedPrimitiveArray.h>
#include <ScopedUtfChars.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_util_AssetManager.h>
#include <androidfw/AssetManager.h>
#include "Utils.h"
@@ -33,6 +35,8 @@
#include <minikin/FontFamily.h>
#include "MinikinSkia.h"

#include <memory>

namespace android {

static jlong FontFamily_create(JNIEnv* env, jobject clazz, jstring lang, jint variant) {
@@ -69,13 +73,89 @@ static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr,
    return addSkTypeface(fontFamily, face);
}

static struct {
    jmethodID mGet;
    jmethodID mSize;
} gListClassInfo;

static struct {
    jfieldID mTag;
    jfieldID mStyleValue;
} gAxisClassInfo;

static void release_global_ref(const void* /*data*/, void* context) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    bool needToAttach = (env == NULL);
    if (needToAttach) {
        JavaVMAttachArgs args;
        args.version = JNI_VERSION_1_4;
        args.name = "release_font_data";
        args.group = NULL;
        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);

    if (needToAttach) {
       AndroidRuntime::getJavaVM()->DetachCurrentThread();
    }
}

static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr,
        jstring path, jint ttcIndex, jint weight, jboolean isItalic) {
    NPE_CHECK_RETURN_ZERO(env, path);
    ScopedUtfChars str(env, path);
    SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex);
        jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) {
    NPE_CHECK_RETURN_ZERO(env, font);

    // Declare axis native type.
    std::unique_ptr<SkFontMgr::FontParameters::Axis[]> skiaAxes;
    int skiaAxesLength = 0;
    if (listOfAxis) {
        jint listSize = env->CallIntMethod(listOfAxis, gListClassInfo.mSize);

        skiaAxes.reset(new SkFontMgr::FontParameters::Axis[listSize]);
        skiaAxesLength = listSize;
        for (jint i = 0; i < listSize; ++i) {
            jobject axisObject = env->CallObjectMethod(listOfAxis, gListClassInfo.mGet, i);
            if (!axisObject) {
                skiaAxes[i].fTag = 0;
                skiaAxes[i].fStyleValue = 0;
                continue;
            }

            jint tag = env->GetIntField(axisObject, gAxisClassInfo.mTag);
            jfloat stylevalue = env->GetFloatField(axisObject, gAxisClassInfo.mStyleValue);
            skiaAxes[i].fTag = tag;
            skiaAxes[i].fStyleValue = SkFloatToScalar(stylevalue);
        }
    }

    void* fontPtr = env->GetDirectBufferAddress(font);
    if (fontPtr == NULL) {
        ALOGE("addFont failed to create font, buffer invalid");
        return false;
    }
    jlong fontSize = env->GetDirectBufferCapacity(font);
    if (fontSize < 0) {
        ALOGE("addFont failed to create font, buffer size invalid");
        return false;
    }
    jobject fontRef = MakeGlobalRefOrDie(env, font);
    SkAutoTUnref<SkData> data(SkData::NewWithProc(fontPtr, fontSize,
            release_global_ref, reinterpret_cast<void*>(fontRef)));
    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data));

    SkFontMgr::FontParameters params;
    params.setCollectionIndex(ttcIndex);
    params.setAxes(skiaAxes.get(), skiaAxesLength);

    SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
    SkTypeface* face = fm->createFromStream(fontData.release(), params);
    if (face == NULL) {
        ALOGE("addFont failed to create font %s", str.c_str());
        ALOGE("addFont failed to create font, invalid request");
        return false;
    }
    FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
@@ -129,15 +209,26 @@ static const JNINativeMethod gFontFamilyMethods[] = {
    { "nCreateFamily",         "(Ljava/lang/String;I)J", (void*)FontFamily_create },
    { "nUnrefFamily",          "(J)V", (void*)FontFamily_unref },
    { "nAddFont",              "(JLjava/lang/String;I)Z", (void*)FontFamily_addFont },
    { "nAddFontWeightStyle",   "(JLjava/lang/String;IIZ)Z", (void*)FontFamily_addFontWeightStyle },
    { "nAddFontWeightStyle",   "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z",
            (void*)FontFamily_addFontWeightStyle },
    { "nAddFontFromAsset",     "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
            (void*)FontFamily_addFontFromAsset },
};

int register_android_graphics_FontFamily(JNIEnv* env)
{
    return RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods,
    int err = RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods,
            NELEM(gFontFamilyMethods));

    jclass listClass = FindClassOrDie(env, "java/util/List");
    gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;");
    gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I");

    jclass axisClass = FindClassOrDie(env, "android/graphics/FontListParser$Axis");
    gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "tag", "I");
    gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "styleValue", "F");

    return err;
}

}
+9 −4
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@ package android.graphics;

import android.content.res.AssetManager;

import java.nio.ByteBuffer;
import java.util.List;

/**
 * A family of typefaces with different styles.
 *
@@ -62,8 +65,9 @@ public class FontFamily {
        return nAddFont(mNativePtr, path, ttcIndex);
    }

    public boolean addFontWeightStyle(String path, int ttcIndex, int weight, boolean style) {
        return nAddFontWeightStyle(mNativePtr, path, ttcIndex, weight, style);
    public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, List<FontListParser.Axis> axes,
            int weight, boolean style) {
        return nAddFontWeightStyle(mNativePtr, font, ttcIndex, axes, weight, style);
    }

    public boolean addFontFromAsset(AssetManager mgr, String path) {
@@ -73,8 +77,9 @@ public class FontFamily {
    private static native long nCreateFamily(String lang, int variant);
    private static native void nUnrefFamily(long nativePtr);
    private static native boolean nAddFont(long nativeFamily, String path, int ttcIndex);
    private static native boolean nAddFontWeightStyle(long nativeFamily, String path,
            int ttcIndex, int weight, boolean isItalic);
    private static native boolean nAddFontWeightStyle(long nativeFamily, ByteBuffer font,
            int ttcIndex, List<FontListParser.Axis> listOfAxis,
            int weight, boolean isItalic);
    private static native boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr,
            String path);
}
+81 −11
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * Parser for font config files.
@@ -42,15 +43,26 @@ public class FontListParser {
        public List<Alias> aliases;
    }

    public static class Axis {
        Axis(int tag, float styleValue) {
            this.tag = tag;
            this.styleValue = styleValue;
        }
        public final int tag;
        public final float styleValue;
    }

    public static class Font {
        Font(String fontName, int ttcIndex, int weight, boolean isItalic) {
        Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic) {
            this.fontName = fontName;
            this.ttcIndex = ttcIndex;
            this.axes = axes;
            this.weight = weight;
            this.isItalic = isItalic;
        }
        public String fontName;
        public int ttcIndex;
        public final List<Axis> axes;
        public int weight;
        public boolean isItalic;
    }
@@ -93,9 +105,10 @@ public class FontListParser {
        parser.require(XmlPullParser.START_TAG, null, "familyset");
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) continue;
            if (parser.getName().equals("family")) {
            String tag = parser.getName();
            if (tag.equals("family")) {
                config.families.add(readFamily(parser));
            } else if (parser.getName().equals("alias")) {
            } else if (tag.equals("alias")) {
                config.aliases.add(readAlias(parser));
            } else {
                skip(parser);
@@ -114,19 +127,76 @@ public class FontListParser {
            if (parser.getEventType() != XmlPullParser.START_TAG) continue;
            String tag = parser.getName();
            if (tag.equals("font")) {
                String ttcIndexStr = parser.getAttributeValue(null, "index");
                int ttcIndex = ttcIndexStr == null ? 0 : Integer.parseInt(ttcIndexStr);
                fonts.add(readFont(parser));
            } else {
                skip(parser);
            }
        }
        return new Family(name, fonts, lang, variant);
    }

    /** Matches leading and trailing XML whitespace. */
    private static final Pattern FILENAME_WHITESPACE_PATTERN =
            Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");

    private static Font readFont(XmlPullParser parser)
            throws XmlPullParserException, IOException {
        String indexStr = parser.getAttributeValue(null, "index");
        int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
        List<Axis> axes = new ArrayList<Axis>();
        String weightStr = parser.getAttributeValue(null, "weight");
        int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
        boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
                String filename = parser.nextText();
                String fullFilename = "/system/fonts/" + filename;
                fonts.add(new Font(fullFilename, ttcIndex, weight, isItalic));
        StringBuilder filename = new StringBuilder();
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() == XmlPullParser.TEXT) {
                filename.append(parser.getText());
            }
            if (parser.getEventType() != XmlPullParser.START_TAG) continue;
            String tag = parser.getName();
            if (tag.equals("axis")) {
                axes.add(readAxis(parser));
            } else {
                skip(parser);
            }
        }
        return new Family(name, fonts, lang, variant);
        String fullFilename = "/system/fonts/" +
                FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
        return new Font(fullFilename, index, axes, weight, isItalic);
    }

    /** The 'tag' attribute value is read as four character values between 0 and 255 inclusive. */
    private static final Pattern TAG_PATTERN = Pattern.compile("[\\x00-\\xFF]{4}");

    /** The 'styleValue' attribute has an optional leading '-', followed by '<digits>',
     *  '<digits>.<digits>', or '.<digits>' where '<digits>' is one or more of [0-9].
     */
    private static final Pattern STYLE_VALUE_PATTERN =
            Pattern.compile("-?(([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))");

    private static Axis readAxis(XmlPullParser parser)
            throws XmlPullParserException, IOException {
        int tag = 0;
        String tagStr = parser.getAttributeValue(null, "tag");
        if (tagStr != null && TAG_PATTERN.matcher(tagStr).matches()) {
            tag = tagStr.charAt(0) << 24 +
                  tagStr.charAt(1) << 16 +
                  tagStr.charAt(2) <<  8 +
                  tagStr.charAt(3);
        } else {
            throw new XmlPullParserException("Invalid tag attribute value.", parser, null);
        }

        float styleValue = 0;
        String styleValueStr = parser.getAttributeValue(null, "stylevalue");
        if (styleValueStr != null && STYLE_VALUE_PATTERN.matcher(styleValueStr).matches()) {
            styleValue = Float.parseFloat(styleValueStr);
        } else {
            throw new XmlPullParserException("Invalid styleValue attribute value.", parser, null);
        }

        skip(parser);  // axis tag is empty, ignore any contents and consume end tag
        return new Axis(tag, styleValue);
    }

    private static Alias readAlias(XmlPullParser parser)
+29 −10
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package android.graphics;

import android.content.res.AssetManager;
import android.graphics.FontListParser.Family;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
@@ -28,6 +27,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -259,10 +260,26 @@ public class Typeface {
        mStyle = nativeGetStyle(ni);
    }

    private static FontFamily makeFamilyFromParsed(FontListParser.Family family) {
    private static FontFamily makeFamilyFromParsed(FontListParser.Family family,
            Map<String, ByteBuffer> bufferForPath) {
        FontFamily fontFamily = new FontFamily(family.lang, family.variant);
        for (FontListParser.Font font : family.fonts) {
            fontFamily.addFontWeightStyle(font.fontName, font.ttcIndex, font.weight, font.isItalic);
            ByteBuffer fontBuffer = bufferForPath.get(font.fontName);
            if (fontBuffer == null) {
                try (FileInputStream file = new FileInputStream(font.fontName)) {
                    FileChannel fileChannel = file.getChannel();
                    long fontSize = fileChannel.size();
                    fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
                    bufferForPath.put(font.fontName, fontBuffer);
                } catch (IOException e) {
                    Log.e(TAG, "Error mapping font file " + font.fontName);
                    continue;
                }
            }
            if (!fontFamily.addFontWeightStyle(fontBuffer, font.ttcIndex, font.axes,
                    font.weight, font.isItalic)) {
                Log.e(TAG, "Error creating font " + font.fontName + "#" + font.ttcIndex);
            }
        }
        return fontFamily;
    }
@@ -280,13 +297,15 @@ public class Typeface {
            FileInputStream fontsIn = new FileInputStream(configFilename);
            FontListParser.Config fontConfig = FontListParser.parse(fontsIn);

            Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>();

            List<FontFamily> familyList = new ArrayList<FontFamily>();
            // Note that the default typeface is always present in the fallback list;
            // this is an enhancement from pre-Minikin behavior.
            for (int i = 0; i < fontConfig.families.size(); i++) {
                Family f = fontConfig.families.get(i);
                FontListParser.Family f = fontConfig.families.get(i);
                if (i == 0 || f.name == null) {
                    familyList.add(makeFamilyFromParsed(f));
                    familyList.add(makeFamilyFromParsed(f, bufferForPath));
                }
            }
            sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
@@ -295,14 +314,14 @@ public class Typeface {
            Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
            for (int i = 0; i < fontConfig.families.size(); i++) {
                Typeface typeface;
                Family f = fontConfig.families.get(i);
                FontListParser.Family f = fontConfig.families.get(i);
                if (f.name != null) {
                    if (i == 0) {
                        // The first entry is the default typeface; no sense in
                        // duplicating the corresponding FontFamily.
                        typeface = sDefaultTypeface;
                    } else {
                        FontFamily fontFamily = makeFamilyFromParsed(f);
                        FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath);
                        FontFamily[] families = { fontFamily };
                        typeface = Typeface.createFromFamiliesWithDefault(families);
                    }
@@ -324,11 +343,11 @@ public class Typeface {
            Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);
            // TODO: normal in non-Minikin case, remove or make error when Minikin-only
        } catch (FileNotFoundException e) {
            Log.e(TAG, "Error opening " + configFilename);
            Log.e(TAG, "Error opening " + configFilename, e);
        } catch (IOException e) {
            Log.e(TAG, "Error reading " + configFilename);
            Log.e(TAG, "Error reading " + configFilename, e);
        } catch (XmlPullParserException e) {
            Log.e(TAG, "XML parse exception for " + configFilename);
            Log.e(TAG, "XML parse exception for " + configFilename, e);
        }
    }

+3 −1
Original line number Diff line number Diff line
@@ -288,8 +288,10 @@ public class FontFamily_Delegate {
    }

    @LayoutlibDelegate
    /*package*/ static boolean nAddFontWeightStyle(long nativeFamily, final String path,
    /*package*/ static boolean nAddFontWeightStyle(long nativeFamily,
            final String path, final int index, final List<FontListParser.Axis> axes,
            final int weight, final boolean isItalic) {
        // 'index' and 'axes' are not supported by java.awt.Font
        final FontFamily_Delegate delegate = getDelegate(nativeFamily);
        if (delegate != null) {
            if (sFontLocation == null) {