• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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