/* * Copyright (C) 2017 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.fonts; import android.annotation.NonNull; import android.annotation.Nullable; import android.text.TextUtils; import java.util.ArrayList; import java.util.regex.Pattern; /** * Class that holds information about single font variation axis. */ public final class FontVariationAxis { private final int mTag; private final String mTagString; private final float mStyleValue; /** * Construct FontVariationAxis. * * The axis tag must contain four ASCII characters. Tag string that are longer or shorter than * four characters, or contains characters outside of U+0020..U+007E are invalid. * * @throws IllegalArgumentException If given tag string is invalid. */ public FontVariationAxis(@NonNull String tagString, float styleValue) { if (!isValidTag(tagString)) { throw new IllegalArgumentException("Illegal tag pattern: " + tagString); } mTag = makeTag(tagString); mTagString = tagString; mStyleValue = styleValue; } /** * Returns the OpenType style tag value. * @hide */ public int getOpenTypeTagValue() { return mTag; } /** * Returns the variable font axis tag associated to this axis. */ public String getTag() { return mTagString; } /** * Returns the style value associated to the given axis for this font. */ public float getStyleValue() { return mStyleValue; } /** * Returns a valid font variation setting string for this object. */ @Override public @NonNull String toString() { return "'" + mTagString + "' " + Float.toString(mStyleValue); } /** * The 'tag' attribute value is read as four character values between U+0020 and U+007E * inclusive. */ private static final Pattern TAG_PATTERN = Pattern.compile("[\u0020-\u007E]{4}"); /** * Returns true if 'tagString' is valid for font variation axis tag. */ private static boolean isValidTag(String tagString) { return tagString != null && TAG_PATTERN.matcher(tagString).matches(); } /** * The 'styleValue' attribute has an optional leading '-', followed by '', * '.', or '.' where '' is one or more of [0-9]. */ private static final Pattern STYLE_VALUE_PATTERN = Pattern.compile("-?(([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))"); private static boolean isValidValueFormat(String valueString) { return valueString != null && STYLE_VALUE_PATTERN.matcher(valueString).matches(); } /** @hide */ public static int makeTag(String tagString) { final char c1 = tagString.charAt(0); final char c2 = tagString.charAt(1); final char c3 = tagString.charAt(2); final char c4 = tagString.charAt(3); return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; } /** * Construct FontVariationAxis array from font variation settings. * * The settings string is constructed from multiple pairs of axis tag and style values. The axis * tag must contain four ASCII characters and must be wrapped with single quotes (U+0027) or * double quotes (U+0022). Axis strings that are longer or shorter than four characters, or * contain characters outside of U+0020..U+007E are invalid. If a specified axis name is not * defined in the font, the settings will be ignored. * *
     *   FontVariationAxis.fromFontVariationSettings("'wdth' 1.0");
     *   FontVariationAxis.fromFontVariationSettings("'AX  ' 1.0, 'FB  ' 2.0");
     * 
* * @param settings font variation settings. * @return FontVariationAxis[] the array of parsed font variation axis. {@code null} if settings * has no font variation settings. * @throws IllegalArgumentException If given string is not a valid font variation settings * format. */ public static @Nullable FontVariationAxis[] fromFontVariationSettings( @Nullable String settings) { if (settings == null || settings.isEmpty()) { return null; } final ArrayList axisList = new ArrayList<>(); final int length = settings.length(); for (int i = 0; i < length; i++) { final char c = settings.charAt(i); if (Character.isWhitespace(c)) { continue; } if (!(c == '\'' || c == '"') || length < i + 6 || settings.charAt(i + 5) != c) { throw new IllegalArgumentException( "Tag should be wrapped with double or single quote: " + settings); } final String tagString = settings.substring(i + 1, i + 5); i += 6; // Move to end of tag. int endOfValueString = settings.indexOf(',', i); if (endOfValueString == -1) { endOfValueString = length; } final float value; try { // Float.parseFloat ignores leading/trailing whitespaces. value = Float.parseFloat(settings.substring(i, endOfValueString)); } catch (NumberFormatException e) { throw new IllegalArgumentException( "Failed to parse float string: " + e.getMessage()); } axisList.add(new FontVariationAxis(tagString, value)); i = endOfValueString; } if (axisList.isEmpty()) { return null; } return axisList.toArray(new FontVariationAxis[0]); } /** * Stringify the array of FontVariationAxis. * * @param axes an array of FontVariationAxis. * @return String a valid font variation settings string. */ public static @NonNull String toFontVariationSettings(@Nullable FontVariationAxis[] axes) { if (axes == null || axes.length == 0) { return ""; } return TextUtils.join(",", axes); } }