Merge "Introduce font variation settings parser"

This commit is contained in:
Yirui Huang
2016-11-30 01:13:00 +00:00
committed by Android (Google) Code Review
2 changed files with 177 additions and 4 deletions

View File

@@ -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);
}

View File

@@ -0,0 +1,113 @@
/*
* 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(' ', ' ', ' ', ' '));
}
}