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