2 * Copyright (C) 2017 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package android.graphics.fonts;
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.text.TextUtils;
23 import java.util.ArrayList;
24 import java.util.regex.Pattern;
27 * Class that holds information about single font variation axis.
29 public final class FontVariationAxis {
30 private final int mTag;
31 private final String mTagString;
32 private final float mStyleValue;
35 * Construct FontVariationAxis.
37 * The axis tag must contain four ASCII characters. Tag string that are longer or shorter than
38 * four characters, or contains characters outside of U+0020..U+007E are invalid.
40 * @throws IllegalArgumentException If given tag string is invalid.
42 public FontVariationAxis(@NonNull String tagString, float styleValue) {
43 if (!isValidTag(tagString)) {
44 throw new IllegalArgumentException("Illegal tag pattern: " + tagString);
46 mTag = makeTag(tagString);
47 mTagString = tagString;
48 mStyleValue = styleValue;
52 * Returns the OpenType style tag value.
55 public int getOpenTypeTagValue() {
60 * Returns the variable font axis tag associated to this axis.
62 public String getTag() {
67 * Returns the style value associated to the given axis for this font.
69 public float getStyleValue() {
74 * Returns a valid font variation setting string for this object.
77 public @NonNull String toString() {
78 return "'" + mTagString + "' " + Float.toString(mStyleValue);
82 * The 'tag' attribute value is read as four character values between U+0020 and U+007E
85 private static final Pattern TAG_PATTERN = Pattern.compile("[\u0020-\u007E]{4}");
88 * Returns true if 'tagString' is valid for font variation axis tag.
90 private static boolean isValidTag(String tagString) {
91 return tagString != null && TAG_PATTERN.matcher(tagString).matches();
95 * The 'styleValue' attribute has an optional leading '-', followed by '<digits>',
96 * '<digits>.<digits>', or '.<digits>' where '<digits>' is one or more of [0-9].
98 private static final Pattern STYLE_VALUE_PATTERN =
99 Pattern.compile("-?(([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))");
101 private static boolean isValidValueFormat(String valueString) {
102 return valueString != null && STYLE_VALUE_PATTERN.matcher(valueString).matches();
106 public static int makeTag(String tagString) {
107 final char c1 = tagString.charAt(0);
108 final char c2 = tagString.charAt(1);
109 final char c3 = tagString.charAt(2);
110 final char c4 = tagString.charAt(3);
111 return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4;
115 * Construct FontVariationAxis array from font variation settings.
117 * The settings string is constructed from multiple pairs of axis tag and style values. The axis
118 * tag must contain four ASCII characters and must be wrapped with single quotes (U+0027) or
119 * double quotes (U+0022). Axis strings that are longer or shorter than four characters, or
120 * contain characters outside of U+0020..U+007E are invalid. If a specified axis name is not
121 * defined in the font, the settings will be ignored.
124 * FontVariationAxis.fromFontVariationSettings("'wdth' 1.0");
125 * FontVariationAxis.fromFontVariationSettings("'AX ' 1.0, 'FB ' 2.0");
128 * @param settings font variation settings.
129 * @return FontVariationAxis[] the array of parsed font variation axis. {@code null} if settings
130 * has no font variation settings.
131 * @throws IllegalArgumentException If given string is not a valid font variation settings
134 public static @Nullable FontVariationAxis[] fromFontVariationSettings(
135 @Nullable String settings) {
136 if (settings == null || settings.isEmpty()) {
139 final ArrayList<FontVariationAxis> axisList = new ArrayList<>();
140 final int length = settings.length();
141 for (int i = 0; i < length; i++) {
142 final char c = settings.charAt(i);
143 if (Character.isWhitespace(c)) {
146 if (!(c == '\'' || c == '"') || length < i + 6 || settings.charAt(i + 5) != c) {
147 throw new IllegalArgumentException(
148 "Tag should be wrapped with double or single quote: " + settings);
150 final String tagString = settings.substring(i + 1, i + 5);
152 i += 6; // Move to end of tag.
153 int endOfValueString = settings.indexOf(',', i);
154 if (endOfValueString == -1) {
155 endOfValueString = length;
159 // Float.parseFloat ignores leading/trailing whitespaces.
160 value = Float.parseFloat(settings.substring(i, endOfValueString));
161 } catch (NumberFormatException e) {
162 throw new IllegalArgumentException(
163 "Failed to parse float string: " + e.getMessage());
165 axisList.add(new FontVariationAxis(tagString, value));
166 i = endOfValueString;
168 if (axisList.isEmpty()) {
171 return axisList.toArray(new FontVariationAxis[0]);
175 * Stringify the array of FontVariationAxis.
177 * @param axes an array of FontVariationAxis.
178 * @return String a valid font variation settings string.
180 public static @NonNull String toFontVariationSettings(@Nullable FontVariationAxis[] axes) {
181 if (axes == null || axes.length == 0) {
184 return TextUtils.join(",", axes);