1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.fontbox.ttf; 18 19 import java.io.File; 20 import java.io.IOException; 21 import java.io.InputStream; 22 23 import java.util.ArrayList; 24 import java.util.Iterator; 25 import java.util.List; 26 27 /** 28 * A true type font file parser. 29 * 30 * @author Ben Litchfield (ben@benlitchfield.com) 31 * @version $Revision: 1.2 $ 32 */ 33 public class TTFParser 34 { 35 /** 36 * A simple command line program to test parsing of a TTF file. <br/> 37 * usage: java org.pdfbox.ttf.TTFParser <ttf-file> 38 * 39 * @param args The command line arguments. 40 * 41 * @throws IOException If there is an error while parsing the font file. 42 */ 43 public static void main( String[] args ) throws IOException 44 { 45 if( args.length != 1 ) 46 { 47 System.err.println( "usage: java org.pdfbox.ttf.TTFParser <ttf-file>" ); 48 System.exit( -1 ); 49 } 50 TTFParser parser = new TTFParser(); 51 TrueTypeFont font = parser.parseTTF( args[0] ); 52 System.out.println( "Font:" + font ); 53 } 54 55 /** 56 * Parse a file and get a true type font. 57 * @param ttfFile The TTF file. 58 * @return A true type font. 59 * @throws IOException If there is an error parsing the true type font. 60 */ 61 public TrueTypeFont parseTTF( String ttfFile ) throws IOException 62 { 63 RAFDataStream raf = new RAFDataStream( ttfFile, "r" ); 64 return parseTTF( raf ); 65 } 66 67 /** 68 * Parse a file and get a true type font. 69 * @param ttfFile The TTF file. 70 * @return A true type font. 71 * @throws IOException If there is an error parsing the true type font. 72 */ 73 public TrueTypeFont parseTTF( File ttfFile ) throws IOException 74 { 75 RAFDataStream raf = new RAFDataStream( ttfFile, "r" ); 76 return parseTTF( raf ); 77 } 78 79 /** 80 * Parse a file and get a true type font. 81 * @param ttfData The TTF data to parse. 82 * @return A true type font. 83 * @throws IOException If there is an error parsing the true type font. 84 */ 85 public TrueTypeFont parseTTF( InputStream ttfData ) throws IOException 86 { 87 return parseTTF( new MemoryTTFDataStream( ttfData )); 88 } 89 90 /** 91 * Parse a file and get a true type font. 92 * @param raf The TTF file. 93 * @return A true type font. 94 * @throws IOException If there is an error parsing the true type font. 95 */ 96 public TrueTypeFont parseTTF( TTFDataStream raf ) throws IOException 97 { 98 TrueTypeFont font = new TrueTypeFont( raf ); 99 font.setVersion( raf.read32Fixed() ); 100 int numberOfTables = raf.readUnsignedShort(); 101 int searchRange = raf.readUnsignedShort(); 102 int entrySelector = raf.readUnsignedShort(); 103 int rangeShift = raf.readUnsignedShort(); 104 for( int i=0; i<numberOfTables; i++ ) 105 { 106 TTFTable table = readTableDirectory( raf ); 107 font.addTable( table ); 108 } 109 List<TTFTable> initialized = new ArrayList<TTFTable>(); 110 //need to initialize a couple tables in a certain order 111 HeaderTable head = font.getHeader(); 112 raf.seek( head.getOffset() ); 113 head.initData( font, raf ); 114 initialized.add( head ); 115 116 117 HorizontalHeaderTable hh = font.getHorizontalHeader(); 118 raf.seek( hh.getOffset() ); 119 hh.initData( font, raf ); 120 initialized.add( hh ); 121 122 MaximumProfileTable maxp = font.getMaximumProfile(); 123 raf.seek( maxp.getOffset() ); 124 maxp.initData( font, raf ); 125 initialized.add( maxp ); 126 127 PostScriptTable post = font.getPostScript(); 128 raf.seek( post.getOffset() ); 129 post.initData( font, raf ); 130 initialized.add( post ); 131 132 IndexToLocationTable loc = font.getIndexToLocation(); 133 raf.seek( loc.getOffset() ); 134 loc.initData( font, raf ); 135 initialized.add( loc ); 136 137 Iterator<TTFTable> iter = font.getTables().iterator(); 138 while( iter.hasNext() ) 139 { 140 TTFTable table = iter.next(); 141 if( !initialized.contains( table ) ) 142 { 143 raf.seek( table.getOffset() ); 144 table.initData( font, raf ); 145 } 146 } 147 return font; 148 } 149 150 private TTFTable readTableDirectory( TTFDataStream raf ) throws IOException 151 { 152 TTFTable retval = null; 153 String tag = raf.readString( 4 ); 154 if( tag.equals( CMAPTable.TAG ) ) 155 { 156 retval = new CMAPTable(); 157 } 158 else if( tag.equals( GlyphTable.TAG ) ) 159 { 160 retval = new GlyphTable(); 161 } 162 else if( tag.equals( HeaderTable.TAG ) ) 163 { 164 retval = new HeaderTable(); 165 } 166 else if( tag.equals( HorizontalHeaderTable.TAG ) ) 167 { 168 retval = new HorizontalHeaderTable(); 169 } 170 else if( tag.equals( HorizontalMetricsTable.TAG ) ) 171 { 172 retval = new HorizontalMetricsTable(); 173 } 174 else if( tag.equals( IndexToLocationTable.TAG ) ) 175 { 176 retval = new IndexToLocationTable(); 177 } 178 else if( tag.equals( MaximumProfileTable.TAG ) ) 179 { 180 retval = new MaximumProfileTable(); 181 } 182 else if( tag.equals( NamingTable.TAG ) ) 183 { 184 retval = new NamingTable(); 185 } 186 else if( tag.equals( OS2WindowsMetricsTable.TAG ) ) 187 { 188 retval = new OS2WindowsMetricsTable(); 189 } 190 else if( tag.equals( PostScriptTable.TAG ) ) 191 { 192 retval = new PostScriptTable(); 193 } 194 else if( tag.equals( GlyphTable.TAG ) ) 195 { 196 retval = new GlyphTable(); 197 } 198 else if( tag.equals( GlyphTable.TAG ) ) 199 { 200 retval = new GlyphTable(); 201 } 202 else if( tag.equals( DigitalSignatureTable.TAG ) ) 203 { 204 retval = new DigitalSignatureTable(); 205 } 206 else 207 { 208 //unknown table type but read it anyway. 209 retval = new TTFTable(); 210 } 211 retval.setTag( tag ); 212 retval.setCheckSum( raf.readUnsignedInt() ); 213 retval.setOffset( raf.readUnsignedInt() ); 214 retval.setLength( raf.readUnsignedInt() ); 215 return retval; 216 } 217 }