1 /*
2  * Copyright (C) 2014 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;
18 
19 import android.content.res.AssetManager;
20 import android.graphics.fonts.FontVariationAxis;
21 import android.text.FontConfig;
22 import android.util.Log;
23 import dalvik.annotation.optimization.CriticalNative;
24 
25 import java.io.FileInputStream;
26 import java.io.IOException;
27 import java.nio.ByteBuffer;
28 import java.nio.channels.FileChannel;
29 
30 /**
31  * A family of typefaces with different styles.
32  *
33  * @hide
34  */
35 public class FontFamily {
36 
37     private static String TAG = "FontFamily";
38 
39     /**
40      * @hide
41      */
42     public long mNativePtr;
43 
44     // Points native font family builder. Must be zero after freezing this family.
45     private long mBuilderPtr;
46 
FontFamily()47     public FontFamily() {
48         mBuilderPtr = nInitBuilder(null, 0);
49     }
50 
FontFamily(String lang, int variant)51     public FontFamily(String lang, int variant) {
52         mBuilderPtr = nInitBuilder(lang, variant);
53     }
54 
55     /**
56      * Finalize the FontFamily creation.
57      *
58      * @return boolean returns false if some error happens in native code, e.g. broken font file is
59      *                 passed, etc.
60      */
freeze()61     public boolean freeze() {
62         if (mBuilderPtr == 0) {
63             throw new IllegalStateException("This FontFamily is already frozen");
64         }
65         mNativePtr = nCreateFamily(mBuilderPtr);
66         mBuilderPtr = 0;
67         return mNativePtr != 0;
68     }
69 
abortCreation()70     public void abortCreation() {
71         if (mBuilderPtr == 0) {
72             throw new IllegalStateException("This FontFamily is already frozen or abandoned");
73         }
74         nAbort(mBuilderPtr);
75         mBuilderPtr = 0;
76     }
77 
78     @Override
finalize()79     protected void finalize() throws Throwable {
80         try {
81             if (mNativePtr != 0) {
82                 nUnrefFamily(mNativePtr);
83             }
84             if (mBuilderPtr != 0) {
85                 nAbort(mBuilderPtr);
86             }
87         } finally {
88             super.finalize();
89         }
90     }
91 
addFont(String path, int ttcIndex, FontVariationAxis[] axes, int weight, int italic)92     public boolean addFont(String path, int ttcIndex, FontVariationAxis[] axes, int weight,
93             int italic) {
94         if (mBuilderPtr == 0) {
95             throw new IllegalStateException("Unable to call addFont after freezing.");
96         }
97         try (FileInputStream file = new FileInputStream(path)) {
98             FileChannel fileChannel = file.getChannel();
99             long fontSize = fileChannel.size();
100             ByteBuffer fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
101             if (axes != null) {
102                 for (FontVariationAxis axis : axes) {
103                     nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue());
104                 }
105             }
106             return nAddFont(mBuilderPtr, fontBuffer, ttcIndex, weight, italic);
107         } catch (IOException e) {
108             Log.e(TAG, "Error mapping font file " + path);
109             return false;
110         }
111     }
112 
addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes, int weight, int italic)113     public boolean addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes,
114             int weight, int italic) {
115         if (mBuilderPtr == 0) {
116             throw new IllegalStateException("Unable to call addFontWeightStyle after freezing.");
117         }
118         if (axes != null) {
119             for (FontVariationAxis axis : axes) {
120                 nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue());
121             }
122         }
123         return nAddFontWeightStyle(mBuilderPtr, font, ttcIndex, weight, italic);
124     }
125 
126     /**
127      * @param mgr The AssetManager to use for this context.
128      * @param path The path to the font file to load.
129      * @param cookie If available, the resource cookie given by Resources.
130      * @param isAsset {@code true} if this is from the assets/ folder, {@code false} if from
131      *            resources
132      * @param weight The weight of the font. If 0 is given, the weight and italic will be resolved
133      *            using the OS/2 table in the font.
134      * @param isItalic Whether this font is italic. If the weight is set to 0, this will be resolved
135      *            using the OS/2 table in the font.
136      * @return
137      */
addFontFromAssetManager(AssetManager mgr, String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic, FontVariationAxis[] axes)138     public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie,
139             boolean isAsset, int ttcIndex, int weight, int isItalic,
140             FontVariationAxis[] axes) {
141         if (mBuilderPtr == 0) {
142             throw new IllegalStateException("Unable to call addFontFromAsset after freezing.");
143         }
144         if (axes != null) {
145             for (FontVariationAxis axis : axes) {
146                 nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue());
147             }
148         }
149         return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, ttcIndex, weight,
150                 isItalic);
151     }
152 
153     /**
154      * Allow creating unsupported FontFamily.
155      *
156      * For compatibility reasons, we still need to create a FontFamily object even if Minikin failed
157      * to find any usable 'cmap' table for some reasons, e.g. broken 'cmap' table, no 'cmap' table
158      * encoded with Unicode code points, etc. Without calling this method, the freeze() method will
159      * return null if Minikin fails to find any usable 'cmap' table. By calling this method, the
160      * freeze() won't fail and will create an empty FontFamily. This empty FontFamily is placed at
161      * the top of the fallback chain but is never used. if we don't create this empty FontFamily
162      * and put it at top, bad things (performance regressions, unexpected glyph selection) will
163      * happen.
164      */
allowUnsupportedFont()165     public void allowUnsupportedFont() {
166         if (mBuilderPtr == 0) {
167             throw new IllegalStateException("Unable to allow unsupported font.");
168         }
169         nAllowUnsupportedFont(mBuilderPtr);
170     }
171 
172     // TODO: Remove once internal user stop using private API.
nAddFont(long builderPtr, ByteBuffer font, int ttcIndex)173     private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
174         return nAddFont(builderPtr, font, ttcIndex, -1, -1);
175     }
176 
nInitBuilder(String lang, int variant)177     private static native long nInitBuilder(String lang, int variant);
178 
179     @CriticalNative
nCreateFamily(long mBuilderPtr)180     private static native long nCreateFamily(long mBuilderPtr);
181 
182     @CriticalNative
nAllowUnsupportedFont(long builderPtr)183     private static native void nAllowUnsupportedFont(long builderPtr);
184 
185     @CriticalNative
nAbort(long mBuilderPtr)186     private static native void nAbort(long mBuilderPtr);
187 
188     @CriticalNative
nUnrefFamily(long nativePtr)189     private static native void nUnrefFamily(long nativePtr);
190     // By passing -1 to weigth argument, the weight value is resolved by OS/2 table in the font.
191     // By passing -1 to italic argument, the italic value is resolved by OS/2 table in the font.
nAddFont(long builderPtr, ByteBuffer font, int ttcIndex, int weight, int isItalic)192     private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex,
193             int weight, int isItalic);
nAddFontWeightStyle(long builderPtr, ByteBuffer font, int ttcIndex, int weight, int isItalic)194     private static native boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
195             int ttcIndex, int weight, int isItalic);
nAddFontFromAssetManager(long builderPtr, AssetManager mgr, String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic)196     private static native boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr,
197             String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic);
198 
199     // The added axis values are only valid for the next nAddFont* method call.
200     @CriticalNative
nAddAxisValue(long builderPtr, int tag, float value)201     private static native void nAddAxisValue(long builderPtr, int tag, float value);
202 }
203