• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 package android.graphics.fonts;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.os.Build;
23 import android.text.TextUtils;
24 
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Objects;
29 import java.util.regex.Pattern;
30 
31 /**
32  * Class that holds information about single font variation axis.
33  */
34 @android.ravenwood.annotation.RavenwoodKeepWholeClass
35 public final class FontVariationAxis {
36     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
37     private final int mTag;
38     private final String mTagString;
39     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
40     private final float mStyleValue;
41 
42     /**
43      * Construct FontVariationAxis.
44      *
45      * The axis tag must contain four ASCII characters. Tag string that are longer or shorter than
46      * four characters, or contains characters outside of U+0020..U+007E are invalid.
47      *
48      * @throws IllegalArgumentException If given tag string is invalid.
49      */
FontVariationAxis(@onNull String tagString, float styleValue)50     public FontVariationAxis(@NonNull String tagString, float styleValue) {
51         if (!isValidTag(tagString)) {
52             throw new IllegalArgumentException("Illegal tag pattern: " + tagString);
53         }
54         mTag = makeTag(tagString);
55         mTagString = tagString;
56         mStyleValue = styleValue;
57     }
58 
59     /**
60      * Returns the OpenType style tag value.
61      * @hide
62      */
getOpenTypeTagValue()63     public int getOpenTypeTagValue() {
64         return mTag;
65     }
66 
67     /**
68      * Returns the variable font axis tag associated to this axis.
69      */
getTag()70     public String getTag() {
71         return mTagString;
72     }
73 
74     /**
75      * Returns the style value associated to the given axis for this font.
76      */
getStyleValue()77     public float getStyleValue() {
78         return mStyleValue;
79     }
80 
81     /**
82      * Returns a valid font variation setting string for this object.
83      */
84     @Override
toString()85     public @NonNull String toString() {
86         return "'" + mTagString + "' " + Float.toString(mStyleValue);
87     }
88 
89     /**
90      * The 'tag' attribute value is read as four character values between U+0020 and U+007E
91      * inclusive.
92      */
93     private static final Pattern TAG_PATTERN = Pattern.compile("[\u0020-\u007E]{4}");
94 
95     /**
96      * Returns true if 'tagString' is valid for font variation axis tag.
97      */
isValidTag(String tagString)98     private static boolean isValidTag(String tagString) {
99         return tagString != null && TAG_PATTERN.matcher(tagString).matches();
100     }
101 
102     /**
103      * The 'styleValue' attribute has an optional leading '-', followed by '<digits>',
104      * '<digits>.<digits>', or '.<digits>' where '<digits>' is one or more of [0-9].
105      */
106     private static final Pattern STYLE_VALUE_PATTERN =
107             Pattern.compile("-?(([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))");
108 
isValidValueFormat(String valueString)109     private static boolean isValidValueFormat(String valueString) {
110         return valueString != null && STYLE_VALUE_PATTERN.matcher(valueString).matches();
111     }
112 
113     /** @hide */
makeTag(String tagString)114     public static int makeTag(String tagString) {
115         final char c1 = tagString.charAt(0);
116         final char c2 = tagString.charAt(1);
117         final char c3 = tagString.charAt(2);
118         final char c4 = tagString.charAt(3);
119         return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4;
120     }
121 
122     /**
123      * Construct FontVariationAxis array from font variation settings.
124      *
125      * The settings string is constructed from multiple pairs of axis tag and style values. The axis
126      * tag must contain four ASCII characters and must be wrapped with single quotes (U+0027) or
127      * double quotes (U+0022). Axis strings that are longer or shorter than four characters, or
128      * contain characters outside of U+0020..U+007E are invalid. If a specified axis name is not
129      * defined in the font, the settings will be ignored.
130      *
131      * <pre>
132      *   FontVariationAxis.fromFontVariationSettings("'wdth' 1.0");
133      *   FontVariationAxis.fromFontVariationSettings("'AX  ' 1.0, 'FB  ' 2.0");
134      * </pre>
135      *
136      * @param settings font variation settings.
137      * @return FontVariationAxis[] the array of parsed font variation axis. {@code null} if settings
138      *                             has no font variation settings.
139      * @throws IllegalArgumentException If given string is not a valid font variation settings
140      *                                  format.
141      */
fromFontVariationSettings( @ullable String settings)142     public static @Nullable FontVariationAxis[] fromFontVariationSettings(
143             @Nullable String settings) {
144         List<FontVariationAxis> result = fromFontVariationSettingsForList(settings);
145         if (result.isEmpty()) {
146             return null;
147         }
148         return result.toArray(new FontVariationAxis[0]);
149     }
150 
151     /** @hide */
fromFontVariationSettingsForList( @ullable String settings)152     public static @NonNull List<FontVariationAxis> fromFontVariationSettingsForList(
153             @Nullable String settings) {
154         if (settings == null || settings.isEmpty()) {
155             return Collections.emptyList();
156         }
157         final ArrayList<FontVariationAxis> axisList = new ArrayList<>();
158         final int length = settings.length();
159         for (int i = 0; i < length; i++) {
160             final char c = settings.charAt(i);
161             if (Character.isWhitespace(c)) {
162                 continue;
163             }
164             if (!(c == '\'' || c == '"') || length < i + 6 || settings.charAt(i + 5) != c) {
165                 throw new IllegalArgumentException(
166                         "Tag should be wrapped with double or single quote: " + settings);
167             }
168             final String tagString = settings.substring(i + 1, i + 5);
169 
170             i += 6;  // Move to end of tag.
171             int endOfValueString = settings.indexOf(',', i);
172             if (endOfValueString == -1) {
173                 endOfValueString = length;
174             }
175             final float value;
176             try {
177                 // Float.parseFloat ignores leading/trailing whitespaces.
178                 value = Float.parseFloat(settings.substring(i, endOfValueString));
179             } catch (NumberFormatException e) {
180                 throw new IllegalArgumentException(
181                         "Failed to parse float string: " + e.getMessage());
182             }
183             axisList.add(new FontVariationAxis(tagString, value));
184             i = endOfValueString;
185         }
186         if (axisList.isEmpty()) {
187             return Collections.emptyList();
188         }
189         return axisList;
190     }
191 
192     /**
193      * Stringify the array of FontVariationAxis.
194      *
195      * @param axes an array of FontVariationAxis.
196      * @return String a valid font variation settings string.
197      */
toFontVariationSettings(@ullable FontVariationAxis[] axes)198     public static @NonNull String toFontVariationSettings(@Nullable FontVariationAxis[] axes) {
199         if (axes == null) {
200             return "";
201         }
202         return TextUtils.join(",", axes);
203     }
204 
205     /**
206      * Stringify the array of FontVariationAxis.
207      * @hide
208      */
toFontVariationSettings(@ullable List<FontVariationAxis> axes)209     public static @NonNull String toFontVariationSettings(@Nullable List<FontVariationAxis> axes) {
210         if (axes == null) {
211             return "";
212         }
213         return TextUtils.join(",", axes);
214     }
215 
216     @Override
equals(@ullable Object o)217     public boolean equals(@Nullable Object o) {
218         if (o == this) {
219             return true;
220         }
221         if (o == null || !(o instanceof FontVariationAxis)) {
222             return false;
223         }
224         FontVariationAxis axis = (FontVariationAxis) o;
225         return axis.mTag == mTag && axis.mStyleValue == mStyleValue;
226     }
227 
228     @Override
hashCode()229     public int hashCode() {
230         return Objects.hash(mTag, mStyleValue);
231     }
232 }
233 
234