• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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.annotation.IntRange;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 
23 import dalvik.annotation.optimization.FastNative;
24 
25 import java.nio.ByteBuffer;
26 import java.nio.ByteOrder;
27 
28 /**
29  * Provides a utility for font file operations.
30  * @hide
31  */
32 public class FontFileUtil {
33 
FontFileUtil()34     private FontFileUtil() {}  // Do not instanciate
35 
36     /**
37      * Unpack the weight value from packed integer.
38      */
unpackWeight(int packed)39     public static int unpackWeight(int packed) {
40         return packed & 0xFFFF;
41     }
42 
43     /**
44      * Unpack the italic value from packed integer.
45      */
unpackItalic(int packed)46     public static boolean unpackItalic(int packed) {
47         return (packed & 0x10000) != 0;
48     }
49 
50     /**
51      * Returns true if the analyzeStyle succeeded
52      */
isSuccess(int packed)53     public static boolean isSuccess(int packed) {
54         return packed != ANALYZE_ERROR;
55     }
56 
pack(@ntRangefrom = 0, to = 1000) int weight, boolean italic)57     private static int pack(@IntRange(from = 0, to = 1000) int weight, boolean italic) {
58         return weight | (italic ? 0x10000 : 0);
59     }
60 
61     private static final int SFNT_VERSION_1 = 0x00010000;
62     private static final int SFNT_VERSION_OTTO = 0x4F54544F;
63     private static final int TTC_TAG = 0x74746366;
64     private static final int OS2_TABLE_TAG = 0x4F532F32;
65 
66     private static final int ANALYZE_ERROR = 0xFFFFFFFF;
67 
68     /**
69      * Analyze the font file returns packed style info
70      */
analyzeStyle(@onNull ByteBuffer buffer, @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] varSettings)71     public static final int analyzeStyle(@NonNull ByteBuffer buffer,
72             @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] varSettings) {
73         int weight = -1;
74         int italic = -1;
75         if (varSettings != null) {
76             for (FontVariationAxis axis :varSettings) {
77                 if ("wght".equals(axis.getTag())) {
78                     weight = (int) axis.getStyleValue();
79                 } else if ("ital".equals(axis.getTag())) {
80                     italic = (axis.getStyleValue() == 1.0f) ? 1 : 0;
81                 }
82             }
83         }
84 
85         if (weight != -1 && italic != -1) {
86             // Both weight/italic style are specifeid by variation settings.
87             // No need to look into OS/2 table.
88             // TODO: Good to look HVAR table to check if this font supports wght/ital axes.
89             return pack(weight, italic == 1);
90         }
91 
92         ByteOrder originalOrder = buffer.order();
93         buffer.order(ByteOrder.BIG_ENDIAN);
94         try {
95             int fontFileOffset = 0;
96             int magicNumber = buffer.getInt(0);
97             if (magicNumber == TTC_TAG) {
98                 // TTC file.
99                 if (ttcIndex >= buffer.getInt(8 /* offset to number of fonts in TTC */)) {
100                     return ANALYZE_ERROR;
101                 }
102                 fontFileOffset = buffer.getInt(
103                     12 /* offset to array of offsets of font files */ + 4 * ttcIndex);
104             }
105             int sfntVersion = buffer.getInt(fontFileOffset);
106 
107             if (sfntVersion != SFNT_VERSION_1 && sfntVersion != SFNT_VERSION_OTTO) {
108                 return ANALYZE_ERROR;
109             }
110 
111             int numTables = buffer.getShort(fontFileOffset + 4 /* offset to number of tables */);
112             int os2TableOffset = -1;
113             for (int i = 0; i < numTables; ++i) {
114                 int tableOffset = fontFileOffset + 12 /* size of offset table */
115                         + i * 16 /* size of table record */;
116                 if (buffer.getInt(tableOffset) == OS2_TABLE_TAG) {
117                     os2TableOffset = buffer.getInt(tableOffset + 8 /* offset to the table */);
118                     break;
119                 }
120             }
121 
122             if (os2TableOffset == -1) {
123                 // Couldn't find OS/2 table. use regular style
124                 return pack(400, false);
125             }
126 
127             int weightFromOS2 = buffer.getShort(os2TableOffset + 4 /* offset to weight class */);
128             boolean italicFromOS2 =
129                     (buffer.getShort(os2TableOffset + 62 /* offset to fsSelection */) & 1) != 0;
130             return pack(weight == -1 ? weightFromOS2 : weight,
131                     italic == -1 ? italicFromOS2 : italic == 1);
132         } finally {
133             buffer.order(originalOrder);
134         }
135     }
136 
137     /**
138      * Analyze head OpenType table and return fontRevision value as 32bit integer.
139      *
140      * The font revision is stored in 16.16 bit fixed point value. This function returns this fixed
141      * point value as 32 bit integer, i.e. the value multiplied with 65536.
142      *
143      * IllegalArgumentException will be thrown for invalid font data.
144      * If the font file is invalid, returns -1L.
145      *
146      * @param buffer a buffer of OpenType font
147      * @param index a font index
148      * @return font revision that shifted 16 bits left.
149      */
getRevision(@onNull ByteBuffer buffer, @IntRange(from = 0) int index)150     public static long getRevision(@NonNull ByteBuffer buffer, @IntRange(from = 0) int index) {
151         return nGetFontRevision(buffer, index);
152     }
153 
154     /**
155      * Analyze name OpenType table and return PostScript name.
156      *
157      * IllegalArgumentException will be thrown for invalid font data.
158      * null will be returned if not found or the PostScript name is invalid.
159      *
160      * @param buffer a buffer of OpenType font
161      * @param index a font index
162      * @return a post script name or null if it is invalid or not found.
163      */
getPostScriptName(@onNull ByteBuffer buffer, @IntRange(from = 0) int index)164     public static String getPostScriptName(@NonNull ByteBuffer buffer,
165             @IntRange(from = 0) int index) {
166         return nGetFontPostScriptName(buffer, index);
167     }
168 
169     /**
170      * Analyze name OpenType table and return true if the font has PostScript Type 1 glyphs.
171      *
172      * IllegalArgumentException will be thrown for invalid font data.
173      * -1 will be returned if the byte buffer is not a OpenType font data.
174      * 0 will be returned if the font file doesn't have PostScript Type 1 glyphs, i.e. ttf file.
175      * 1 will be returned if the font file has PostScript Type 1 glyphs, i.e. otf file.
176      *
177      * @param buffer a buffer of OpenType font
178      * @param index a font index
179      * @return a post script name or null if it is invalid or not found.
180      */
isPostScriptType1Font(@onNull ByteBuffer buffer, @IntRange(from = 0) int index)181     public static int isPostScriptType1Font(@NonNull ByteBuffer buffer,
182             @IntRange(from = 0) int index) {
183         return nIsPostScriptType1Font(buffer, index);
184     }
185 
186     /**
187      * Analyze the file content and returns 1 if the font file is an OpenType collection file, 0 if
188      * the font file is a OpenType font file, -1 otherwise.
189      */
isCollectionFont(@onNull ByteBuffer buffer)190     public static int isCollectionFont(@NonNull ByteBuffer buffer) {
191         ByteBuffer copied = buffer.slice();
192         copied.order(ByteOrder.BIG_ENDIAN);
193         int magicNumber = copied.getInt(0);
194         if (magicNumber == TTC_TAG) {
195             return 1;
196         } else if (magicNumber == SFNT_VERSION_1 || magicNumber == SFNT_VERSION_OTTO) {
197             return 0;
198         } else {
199             return -1;
200         }
201     }
202 
203     @FastNative
nGetFontRevision(@onNull ByteBuffer buffer, @IntRange(from = 0) int index)204     private static native long nGetFontRevision(@NonNull ByteBuffer buffer,
205             @IntRange(from = 0) int index);
206 
207     @FastNative
nGetFontPostScriptName(@onNull ByteBuffer buffer, @IntRange(from = 0) int index)208     private static native String nGetFontPostScriptName(@NonNull ByteBuffer buffer,
209             @IntRange(from = 0) int index);
210 
211     @FastNative
nIsPostScriptType1Font(@onNull ByteBuffer buffer, @IntRange(from = 0) int index)212     private static native int nIsPostScriptType1Font(@NonNull ByteBuffer buffer,
213             @IntRange(from = 0) int index);
214 }
215