1 /*
2  * Copyright (C) 2018 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.icu.util.ULocale;
20 import android.os.LocaleList;
21 import android.util.Pair;
22 
23 import java.io.File;
24 import java.util.Arrays;
25 import java.util.HashSet;
26 import java.util.Locale;
27 import java.util.Objects;
28 import java.util.Set;
29 
30 public class NativeSystemFontHelper {
31     static {
32         System.loadLibrary("ctsgraphics_jni");
33     }
34 
35     /**
36      *  Helper class for representing the system font obtained in native code.
37      */
38     public static class FontDescriptor {
39         String mFilePath;
40         int mWeight;
41         int mSlant;
42         int mIndex;
43         FontVariationAxis[] mAxes;
44         LocaleList mLocale;
45 
46         @Override
equals(Object o)47         public boolean equals(Object o) {
48             if (o == this) {
49                 return true;
50             }
51             if (o == null || !(o instanceof FontDescriptor)) {
52                 return false;
53             }
54             FontDescriptor f = (FontDescriptor) o;
55             return f.mFilePath.equals(mFilePath)
56                 && f.mWeight == mWeight
57                 && f.mSlant == mSlant
58                 && f.mIndex == mIndex
59                 && Arrays.equals(f.mAxes, mAxes)
60                 && localeListEquals(f.mLocale, mLocale);
61         }
62 
63         @Override
hashCode()64         public int hashCode() {
65             return Objects.hash(mFilePath, mWeight, mSlant, mIndex, Arrays.hashCode(mAxes),
66                 mLocale);
67         }
68 
localeEquals(Locale left, Locale right)69         public boolean localeEquals(Locale left, Locale right) {
70             ULocale ulocLeft = ULocale.addLikelySubtags(ULocale.forLocale(left));
71             ULocale ulocRight = ULocale.addLikelySubtags(ULocale.forLocale(right));
72             return ulocLeft.equals(ulocRight);
73         }
74 
localeListEquals(LocaleList left, LocaleList right)75         public boolean localeListEquals(LocaleList left, LocaleList right) {
76             if (left == right) {
77                 return true;
78             }
79             if (left == null || right == null) {
80                 return false;
81             }
82 
83             if (left.size() != right.size()) {
84                 return false;
85             }
86             for (int i = 0; i < left.size(); ++i) {
87                 if (!localeEquals(left.get(i), right.get(i))) {
88                     return false;
89                 }
90             }
91             return true;
92         }
93 
94         @Override
toString()95         public String toString() {
96             return "Font {"
97                 + " path = " + mFilePath
98                 + " weight = " + mWeight
99                 + " slant = " + mSlant
100                 + " index = " + mIndex
101                 + " axes = " + FontVariationAxis.toFontVariationSettings(mAxes)
102                 + " locale = " + mLocale
103                 + "}";
104         }
105     }
106 
tagToStr(int tag)107     private static String tagToStr(int tag) {
108         char[] buf = new char[4];
109         buf[0] = (char) ((tag >> 24) & 0xFF);
110         buf[1] = (char) ((tag >> 16) & 0xFF);
111         buf[2] = (char) ((tag >> 8) & 0xFF);
112         buf[3] = (char) (tag & 0xFF);
113         return String.valueOf(buf);
114     }
115 
getAvailableFonts()116     public static Set<FontDescriptor> getAvailableFonts() {
117         long iterPtr = nOpenIterator();
118         HashSet<FontDescriptor> nativeFonts = new HashSet<>();
119         try {
120             for (long fontPtr = nNext(iterPtr); fontPtr != 0; fontPtr = nNext(iterPtr)) {
121                 try {
122                     FontDescriptor font = new FontDescriptor();
123                     font.mFilePath = nGetFilePath(fontPtr);
124                     font.mWeight = nGetWeight(fontPtr);
125                     font.mSlant = nIsItalic(fontPtr)
126                         ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
127                     font.mIndex = nGetCollectionIndex(fontPtr);
128                     int axesSize = nGetAxisCount(fontPtr);
129                     font.mAxes = new FontVariationAxis[axesSize];
130                     for (int i = 0; i < font.mAxes.length; ++i) {
131                         font.mAxes[i] = new FontVariationAxis(
132                                 tagToStr(nGetAxisTag(fontPtr, i)), nGetAxisValue(fontPtr, i));
133                     }
134 
135                     font.mLocale = LocaleList.forLanguageTags(nGetLocale(fontPtr));
136                     nativeFonts.add(font);
137                 } finally {
138                     nCloseFont(fontPtr);
139                 }
140             }
141         } finally {
142             nCloseIterator(iterPtr);
143         }
144         return nativeFonts;
145     }
146 
matchFamilyStyleCharacter(String familyName, int weight, boolean italic, String languageTags, int familyVariant, String text)147     public static Pair<File, Integer> matchFamilyStyleCharacter(String familyName, int weight,
148             boolean italic, String languageTags, int familyVariant, String text) {
149         final long fontPtr = nMatchFamilyStyleCharacter(familyName, weight, italic, languageTags,
150                 familyVariant, text);
151         final int runLength = nMatchFamilyStyleCharacter_runLength(familyName, weight, italic,
152                 languageTags, familyVariant, text);
153         try {
154             return new Pair<>(new File(nGetFilePath(fontPtr)), runLength);
155         } finally {
156             nCloseFont(fontPtr);
157         }
158     }
159 
nOpenIterator()160     private static native long nOpenIterator();
nCloseIterator(long ptr)161     private static native void nCloseIterator(long ptr);
nNext(long ptr)162     private static native long nNext(long ptr);
nCloseFont(long ptr)163     private static native void nCloseFont(long ptr);
nGetFilePath(long ptr)164     private static native String nGetFilePath(long ptr);
nGetWeight(long ptr)165     private static native int nGetWeight(long ptr);
nIsItalic(long ptr)166     private static native boolean nIsItalic(long ptr);
nGetLocale(long ptr)167     private static native String nGetLocale(long ptr);
nGetCollectionIndex(long ptr)168     private static native int nGetCollectionIndex(long ptr);
nGetAxisCount(long ptr)169     private static native int nGetAxisCount(long ptr);
nGetAxisTag(long ptr, int index)170     private static native int nGetAxisTag(long ptr, int index);
nGetAxisValue(long ptr, int index)171     private static native float nGetAxisValue(long ptr, int index);
nMatchFamilyStyleCharacter(String familyName, int weight, boolean italic, String languageTags, int familyVariant, String text)172     private static native long nMatchFamilyStyleCharacter(String familyName, int weight,
173             boolean italic, String languageTags, int familyVariant, String text);
nMatchFamilyStyleCharacter_runLength(String familyName, int weight, boolean italic, String languageTags, int familyVariant, String text)174     private static native int nMatchFamilyStyleCharacter_runLength(String familyName, int weight,
175             boolean italic, String languageTags, int familyVariant, String text);
176 }
177