1 /* 2 * Copyright 2010 Google Inc. All Rights Reserved. 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 com.google.typography.font.sfntly.testutils; 18 19 import com.google.typography.font.sfntly.Font; 20 import com.google.typography.font.sfntly.Font.Builder; 21 import com.google.typography.font.sfntly.FontFactory; 22 import com.google.typography.font.sfntly.Tag; 23 import com.google.typography.font.sfntly.table.FontDataTable; 24 import com.google.typography.font.sfntly.table.Header; 25 import com.google.typography.font.sfntly.table.Table; 26 import com.google.typography.font.sfntly.table.core.CMap; 27 import com.google.typography.font.sfntly.table.core.CMapTable; 28 import com.google.typography.font.sfntly.table.core.FontHeaderTable; 29 import com.google.typography.font.sfntly.table.core.MaximumProfileTable; 30 import com.google.typography.font.sfntly.table.core.NameTable; 31 import com.google.typography.font.sfntly.table.core.NameTable.NameEntry; 32 33 import java.io.File; 34 import java.io.FileInputStream; 35 import java.io.IOException; 36 import java.io.InputStream; 37 import java.io.OutputStream; 38 import java.util.Comparator; 39 import java.util.Set; 40 import java.util.TreeSet; 41 import java.util.logging.Logger; 42 43 /** 44 * @author Stuart Gill 45 */ 46 public class TestFontUtils { 47 48 private static final Logger logger = 49 Logger.getLogger(Font.class.getCanonicalName()); 50 TestFontUtils()51 private TestFontUtils() {} 52 53 /** 54 * Open a file and read any fonts that it contains. 55 * In many cases this will be a single font but it may be multiple fonts. 56 * @param file the file holding the font(s) 57 * @return the fonts that came from the file 58 * @throws IOException 59 */ loadFont(File file)60 public static Font[] loadFont(File file) throws IOException { 61 return loadFont(file, true); 62 } 63 64 /** 65 * Open a file and read any fonts that it contains. 66 * In many cases this will be a single font but it may be multiple fonts. 67 * @param file the file holding the font(s) 68 * @param fingerprint fingerprinting on or off 69 * @return the fonts that came from the file 70 * @throws IOException 71 */ loadFont(File file, boolean fingerprint)72 public static Font[] loadFont(File file, boolean fingerprint) throws IOException { 73 FontFactory fontFactory = FontFactory.getInstance(); 74 fontFactory.fingerprintFont(fingerprint); 75 FileInputStream is = null; 76 try { 77 is = new FileInputStream(file); 78 return fontFactory.loadFonts(is); 79 } finally { 80 if (is != null) { 81 is.close(); 82 } 83 } 84 } 85 86 /** 87 * Open a file and read any fonts that it contains and check the fonts against supplied expected 88 * fingerprints. 89 * @param file the file holding the font(s) 90 * @param expectedFingerprints array of expected fingerprints for each font in the file 91 * @return the fonts that came from the file 92 * @throws IOException 93 */ loadFont(File file, byte[]... expectedFingerprints)94 public static Font[] loadFont(File file, byte[]... expectedFingerprints) throws IOException { 95 Font[] fonts = loadFont(file, true); 96 for (int i = 0; i < Math.min(fonts.length, expectedFingerprints.length); i++) { 97 if (expectedFingerprints[i] != fonts[i].digest()) { 98 throw new IOException("Did not get the expected fingerprint for font#" + 99 i + " in " + file.getPath() + " Has the file changed?"); 100 } 101 } 102 return fonts; 103 } 104 105 /** 106 * Open a file and read any fonts that it contains. 107 * In many cases this will be a single font but it may be multiple fonts. 108 * @param file the file holding the font(s) 109 * @return the fonts that came from the file 110 * @throws IOException 111 */ loadFontUsingByteArray(File file)112 public static Font[] loadFontUsingByteArray(File file) throws IOException { 113 return loadFontUsingByteArray(file, true); 114 } 115 116 /** 117 * Open a file and read any fonts that it contains. 118 * In many cases this will be a single font but it may be multiple fonts. 119 * @param file the file holding the font(s) 120 * @param fingerprint fingerprinting on or off 121 * @return the fonts that came from the file 122 * @throws IOException 123 */ loadFontUsingByteArray(File file, boolean fingerprint)124 public static Font[] loadFontUsingByteArray(File file, boolean fingerprint) throws IOException { 125 byte[] b = TestUtils.readFile(file); 126 FontFactory fontFactory = FontFactory.getInstance(); 127 fontFactory.fingerprintFont(fingerprint); 128 return fontFactory.loadFonts(b); 129 } 130 131 private static final Comparator<Table> TABLE_COMPARATOR_BY_OFFSET = new Comparator<Table>() { 132 @Override public int compare(Table o1, Table o2) { 133 return o1.header().offset() - o2.header().offset(); 134 } 135 }; 136 137 public static buildAndCheckFont(FontFactory fontFactory, InputStream is, int debug)138 Font[] buildAndCheckFont(FontFactory fontFactory, InputStream is, int debug) throws IOException { 139 140 Font[] fontArray = fontFactory.loadFonts(is); 141 142 for (Font font : fontArray) { 143 144 if (debug >= 1) { 145 logger.info(font.toString()); 146 } 147 148 FontHeaderTable head = font.getTable(Tag.head); 149 if (head != null) { 150 long magicNumber = head.magicNumber(); 151 if (0x5F0F3CF5 != magicNumber) { 152 throw new RuntimeException("Magic number is incorrect."); 153 } 154 if (debug >= 2) { 155 logger.fine("magic number = " + Long.toHexString(magicNumber)); 156 MaximumProfileTable maxp = font.getTable(Tag.maxp); 157 logger.fine("Number of glyphs = " + maxp.numGlyphs()); 158 logger.fine("\n------ Tables by File Location"); 159 } 160 } else { 161 // should find a bhed table if no head table 162 FontDataTable bhed = font.getTable(Tag.intValue("bhed")); 163 if (bhed == null) { 164 throw new RuntimeException("No head table or bhed"); 165 } 166 } 167 168 Set<Table> tables = new TreeSet<Table>(TABLE_COMPARATOR_BY_OFFSET); 169 tables.addAll(font.tableMap().values()); 170 171 for (Table table : tables) { 172 if (debug >= 2) { 173 System.out.println(table); 174 } 175 Header header = table.header(); 176 boolean csValid = table.calculatedChecksum() == header.checksum(); 177 if (!csValid) { 178 logger.severe("\t**** Checksum not valid!"); 179 logger.severe("\tcalculated = 0x" + Long.toHexString(table.calculatedChecksum())); 180 logger.severe("\tread = 0x" + Long.toHexString(header.checksum())); 181 throw new RuntimeException("Checksum for table " + Tag.stringValue(header.tag())); 182 } 183 } 184 185 if (debug >= 2) { 186 logger.fine("\n------ CMap Tables"); 187 CMapTable cmap = (CMapTable) font.getTable(Tag.cmap); 188 for (CMap cmt : cmap) { 189 logger.fine(cmt.toString()); 190 for (int c = 0; c < 256; c++) { 191 logger.finer(c + " = " + cmt.glyphId(c)); 192 } 193 } 194 195 logger.fine("\n----- Name Tables"); 196 NameTable name = (NameTable) font.getTable(Tag.name); 197 for (NameEntry entry : name) { 198 logger.finer(entry.toString()); 199 } 200 } 201 } 202 return fontArray; 203 } 204 serializeFont(File fontFile)205 public static File serializeFont(File fontFile) throws Exception { 206 Font[] fonts = TestFontUtils.loadFont(fontFile); 207 if (fonts == null || fonts.length != 1) { 208 return null; 209 } 210 String extension = TestUtils.extension(fontFile); 211 212 return serializeFont(fonts[0], extension); 213 } 214 serializeFont(Font font, String extension)215 public static File serializeFont(Font font, String extension) throws Exception { 216 File serializedFontFile = 217 File.createTempFile("serializedFont_", extension); 218 return TestFontUtils.serializeFont(font, serializedFontFile); 219 } 220 serializeFont(Font font, File serializedFontFile)221 public static File serializeFont(Font font, File serializedFontFile) throws Exception { 222 serializedFontFile.mkdirs(); 223 serializedFontFile.createNewFile(); 224 OutputStream os = null; 225 try { 226 os = TestUtils.createOutputStream(serializedFontFile); 227 FontFactory factory = FontFactory.getInstance(); 228 factory.serializeFont(font, os); 229 } finally { 230 if (os != null) { 231 os.close(); 232 } 233 } 234 return serializedFontFile; 235 } 236 builderForFontFile(File fontFile)237 public static Builder builderForFontFile(File fontFile) throws IOException { 238 return builderForFontFile(fontFile, true); 239 } 240 builderForFontFile(File fontFile, boolean fingerprint)241 public static Builder builderForFontFile(File fontFile, boolean fingerprint) throws IOException { 242 FileInputStream is = new FileInputStream(fontFile); 243 FontFactory otfFactory = FontFactory.getInstance(); 244 otfFactory.fingerprintFont(fingerprint); 245 Builder[] builder = otfFactory.loadFontsForBuilding(is); 246 return builder[0]; 247 } 248 } 249