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

Commit 3002ab67 authored by yirui's avatar yirui
Browse files

Introduce font variation settings parser

Font variation settings enables developer to specify font axes for
configuring glyph looking. This CL parse Font Variation settings to
make a array of Axis. More work is needed for using parsed axes.

Bug: 33062398
Test: Done by unittests.
Change-Id: I529b98b28df1c738237bee0729d9f479bd656dd3
parent 2dd5018a
Loading
Loading
Loading
Loading
+64 −4
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import com.android.internal.annotations.VisibleForTesting;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
@@ -99,6 +101,67 @@ public class FontListParser {
        }
    }

    // Note that a well-formed variation contains a four-character tag and a float as styleValue,
    // with spacers in between. The tag is enclosd either by double quotes or single quotes.
    @VisibleForTesting
    public static Axis[] parseFontVariationSettings(String settings) {
        String[] settingList = settings.split(",");
        ArrayList<Axis> axisList = new ArrayList<>();
        settingLoop:
        for (String setting : settingList) {
            int pos = 0;
            while (pos < setting.length()) {
                char c = setting.charAt(pos);
                if (c == '\'' || c == '"') {
                    break;
                } else if (!isSpacer(c)) {
                    continue settingLoop;  // Only spacers are allowed before tag appeared.
                }
                pos++;
            }
            if (pos + 7 > setting.length()) {
                continue;  // 7 is the minimum length of tag-style value pair text.
            }
            if (setting.charAt(pos) != setting.charAt(pos + 5)) {
                continue;  // Tag should be wrapped with double or single quote.
            }
            String tagString = setting.substring(pos + 1, pos + 5);
            if (!TAG_PATTERN.matcher(tagString).matches()) {
                continue;  // Skip incorrect format tag.
            }
            pos += 6;
            while (pos < setting.length()) {
                if (!isSpacer(setting.charAt(pos++))) {
                    break;  // Skip spacers between the tag and the styleValue.
                }
            }
            // Skip invalid styleValue
            float styleValue;
            String valueString = setting.substring(pos - 1);
            if (!STYLE_VALUE_PATTERN.matcher(valueString).matches()) {
                continue;  // Skip incorrect format styleValue.
            }
            try {
                styleValue = Float.parseFloat(valueString);
            } catch (NumberFormatException e) {
                continue;  // ignoreing invalid number format
            }
            int tag = makeTag(tagString.charAt(0), tagString.charAt(1), tagString.charAt(2),
                    tagString.charAt(3));
            axisList.add(new Axis(tag, styleValue));
        }
        return axisList.toArray(new Axis[axisList.size()]);
    }

    @VisibleForTesting
    public static int makeTag(char c1, char c2, char c3, char c4) {
        return (c1 << 24) + (c2 << 16) + (c3 << 8) + c4;
    }

    private static boolean isSpacer(char c) {
        return c == ' ' || c == '\r' || c == '\t' || c == '\n';
    }

    private static Config readFamilies(XmlPullParser parser)
            throws XmlPullParserException, IOException {
        Config config = new Config();
@@ -179,10 +242,7 @@ public class FontListParser {
        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)      );
            tag = makeTag(tagStr.charAt(0), tagStr.charAt(1), tagStr.charAt(2), tagStr.charAt(3));
        } else {
            throw new XmlPullParserException("Invalid tag attribute value.", parser, null);
        }
+113 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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;

import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;


public class VariationParserTest extends TestCase {

    @SmallTest
    public void testParseFontVariationSetting() {
        int tag = FontListParser.makeTag('w', 'd', 't', 'h');
        FontListParser.Axis[] axis = FontListParser.parseFontVariationSettings("'wdth' 1");
        assertEquals(tag, axis[0].tag);
        assertEquals(1.0f, axis[0].styleValue);

        axis = FontListParser.parseFontVariationSettings("\"wdth\" 100");
        assertEquals(tag, axis[0].tag);
        assertEquals(100.0f, axis[0].styleValue);

        axis = FontListParser.parseFontVariationSettings("   'wdth' 100");
        assertEquals(tag, axis[0].tag);
        assertEquals(100.0f, axis[0].styleValue);

        axis = FontListParser.parseFontVariationSettings("\t'wdth' 0.5");
        assertEquals(tag, axis[0].tag);
        assertEquals(0.5f, axis[0].styleValue);

        tag = FontListParser.makeTag('A', 'X', ' ', ' ');
        axis = FontListParser.parseFontVariationSettings("'AX  ' 1");
        assertEquals(tag, axis[0].tag);
        assertEquals(1.0f, axis[0].styleValue);

        axis = FontListParser.parseFontVariationSettings("'AX  '\t1");
        assertEquals(tag, axis[0].tag);
        assertEquals(1.0f, axis[0].styleValue);

        axis = FontListParser.parseFontVariationSettings("'AX  '\n1");
        assertEquals(tag, axis[0].tag);
        assertEquals(1.0f, axis[0].styleValue);

        axis = FontListParser.parseFontVariationSettings("'AX  '\r1");
        assertEquals(tag, axis[0].tag);
        assertEquals(1.0f, axis[0].styleValue);

        axis = FontListParser.parseFontVariationSettings("'AX  '\r\t\n 1");
        assertEquals(tag, axis[0].tag);
        assertEquals(1.0f, axis[0].styleValue);

        // Test for invalid input
        axis = FontListParser.parseFontVariationSettings("");
        assertEquals(0, axis.length);
        axis = FontListParser.parseFontVariationSettings("invalid_form");
        assertEquals(0, axis.length);

        // Test with invalid tag
        axis = FontListParser.parseFontVariationSettings("'' 1");
        assertEquals(0, axis.length);
        axis = FontListParser.parseFontVariationSettings("'invalid' 1");
        assertEquals(0, axis.length);

        // Test with invalid styleValue
        axis = FontListParser.parseFontVariationSettings("'wdth' ");
        assertEquals(0, axis.length);
        axis = FontListParser.parseFontVariationSettings("'wdth' x");
        assertEquals(0, axis.length);
        axis = FontListParser.parseFontVariationSettings("'wdth' \t");
        assertEquals(0, axis.length);
        axis = FontListParser.parseFontVariationSettings("'wdth' \n\r");
        assertEquals(0, axis.length);
    }

    @SmallTest
    public void testParseFontVariationStyleSettings() {
        FontListParser.Axis[] axis =
                FontListParser.parseFontVariationSettings("'wdth' 10,'AX  '\r1");
        int tag1 = FontListParser.makeTag('w', 'd', 't', 'h');
        int tag2 = FontListParser.makeTag('A', 'X', ' ', ' ');
        assertEquals(tag1, axis[0].tag);
        assertEquals(10.0f, axis[0].styleValue);
        assertEquals(tag2, axis[1].tag);
        assertEquals(1.0f, axis[1].styleValue);

        // Test only spacers are allowed before tag
        axis = FontListParser.parseFontVariationSettings("     'wdth' 10,ab'wdth' 1");
        tag1 = FontListParser.makeTag('w', 'd', 't', 'h');
        assertEquals(tag1, axis[0].tag);
        assertEquals(10.0f, axis[0].styleValue);
        assertEquals(1, axis.length);
    }

    @SmallTest
    public void testMakeTag() {
      assertEquals(0x77647468, FontListParser.makeTag('w', 'd', 't', 'h'));
      assertEquals(0x41582020, FontListParser.makeTag('A', 'X', ' ', ' '));
      assertEquals(0x20202020, FontListParser.makeTag(' ', ' ', ' ', ' '));
    }
}
 No newline at end of file