KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > fonts > TTFFile


1 /*
2  * $Id: TTFFile.java,v 1.6.2.9 2003/05/27 14:13:33 jeremias Exp $
3  * ============================================================================
4  * The Apache Software License, Version 1.1
5  * ============================================================================
6  *
7  * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without modifica-
10  * tion, are permitted provided that the following conditions are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * 3. The end-user documentation included with the redistribution, if any, must
20  * include the following acknowledgment: "This product includes software
21  * developed by the Apache Software Foundation (http://www.apache.org/)."
22  * Alternately, this acknowledgment may appear in the software itself, if
23  * and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. The names "FOP" and "Apache Software Foundation" must not be used to
26  * endorse or promote products derived from this software without prior
27  * written permission. For written permission, please contact
28  * apache@apache.org.
29  *
30  * 5. Products derived from this software may not be called "Apache", nor may
31  * "Apache" appear in their name, without prior written permission of the
32  * Apache Software Foundation.
33  *
34  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
35  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
36  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
37  * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
38  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
39  * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
40  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
41  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
42  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
43  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44  * ============================================================================
45  *
46  * This software consists of voluntary contributions made by many individuals
47  * on behalf of the Apache Software Foundation and was originally created by
48  * James Tauber <jtauber@jtauber.com>. For more information on the Apache
49  * Software Foundation, please see <http://www.apache.org/>.
50  */

51 package org.apache.fop.fonts;
52
53 import java.io.*;
54 import java.util.ArrayList JavaDoc;
55 import java.util.HashMap JavaDoc;
56 import java.util.Iterator JavaDoc;
57
58 /**
59  * Reads a TrueType file or a TrueType Collection.
60  * The TrueType spec can be found at the Microsoft
61  * Typography site: http://www.microsoft.com/truetype/
62  */

63 public class TTFFile {
64     static final byte NTABS = 24;
65     static final int NMACGLYPHS = 258;
66     static final int MAX_CHAR_CODE = 255;
67     static final int ENC_BUF_SIZE = 1024;
68
69     static String JavaDoc encoding = "WinAnsiEncoding"; // Deafult encoding
70
short firstChar = 0;
71     boolean is_embeddable = true;
72     boolean hasSerifs = true;
73     HashMap JavaDoc dirTabs; // Table directory
74
HashMap JavaDoc kerningTab; // for CIDs
75
HashMap JavaDoc ansiKerningTab; // For winAnsiEncoding
76
ArrayList JavaDoc cmaps;
77     ArrayList JavaDoc unicodeMapping; //
78

79     int upem; // unitsPerEm from "head" table
80
int nhmtx; // Number of horizontal metrics
81
int post_format;
82     int loca_format;
83     long lastLoca = 0; // offset to last loca
84
int nglyphs; // Number of glyphs in font (read from "maxp" table)
85
int nmglyphs; // Used in fixWidths - remove?
86

87     TTFMtxEntry mtx_tab[]; // Contains glyph data
88
int[] mtx_encoded = null;
89
90     String JavaDoc fontName = "";
91     String JavaDoc fullName = "";
92     String JavaDoc notice = "";
93     String JavaDoc familyName = "";
94     String JavaDoc subFamilyName = "";
95
96     long italicAngle = 0;
97     long isFixedPitch = 0;
98     int fontBBox1 = 0;
99     int fontBBox2 = 0;
100     int fontBBox3 = 0;
101     int fontBBox4 = 0;
102     int capHeight = 0;
103     int underlinePosition = 0;
104     int underlineThickness = 0;
105     int xHeight = 0;
106     int ascender = 0;
107     int descender = 0;
108
109     short lastChar = 0;
110
111     int ansiWidth[];
112     HashMap JavaDoc ansiIndex;
113
114     private TTFDirTabEntry currentDirTab;
115
116     /**
117      * Position inputstream to position indicated
118      * in the dirtab offset + offset
119      */

120     void seek_tab(FontFileReader in, String JavaDoc name,
121                   long offset) throws IOException {
122         TTFDirTabEntry dt = (TTFDirTabEntry)dirTabs.get(name);
123         if (dt == null) {
124             System.out.println("Dirtab " + name + " not found.");
125         } else {
126             in.seek_set(dt.offset + offset);
127             this.currentDirTab = dt;
128         }
129     }
130
131     /**
132      * Convert from truetype unit to pdf unit based on the
133      * unitsPerEm field in the "head" table
134      * @param n truetype unit
135      * @return pdf unit
136      */

137     int get_ttf_funit(int n) {
138         int ret;
139         if (n < 0) {
140             long rest1 = n % upem;
141             long storrest = 1000 * rest1;
142             long ledd2 = rest1 / storrest;
143             ret = -((-1000 * n) / upem - (int)ledd2);
144         } else {
145             ret = (n / upem) * 1000 + ((n % upem) * 1000) / upem;
146         }
147
148         return ret;
149     }
150
151     /**
152      * Read the cmap table,
153      * return false if the table is not present or only unsupported
154      * tables are present. Currently only unicode cmaps are supported.
155      * Set the unicodeIndex in the TTFMtxEntries and fills in the
156      * cmaps ArrayList.
157      */

158     private boolean readCMAP(FontFileReader in) throws IOException {
159
160         unicodeMapping = new ArrayList JavaDoc();
161
162         /**
163          * Read CMAP table and correct mtx_tab.index
164          */

165         int mtxPtr = 0;
166
167         seek_tab(in, "cmap", 2);
168         int num_cmap = in.readTTFUShort(); // Number of cmap subtables
169
long cmap_unioffset = 0;
170
171         // System.out.println(num_cmap+" cmap tables");
172

173         /*
174          * Read offset for all tables
175          * We are only interested in the unicode table
176          */

177         for (int i = 0; i < num_cmap; i++) {
178             int cmap_pid = in.readTTFUShort();
179             int cmap_eid = in.readTTFUShort();
180             long cmap_offset = in.readTTFULong();
181
182             // System.out.println("Platform ID: "+cmap_pid+
183
// " Encoding: "+cmap_eid);
184

185             if (cmap_pid == 3 && cmap_eid == 1) {
186                 cmap_unioffset = cmap_offset;
187             }
188         }
189
190         if (cmap_unioffset <= 0) {
191             System.out.println("Unicode cmap table not present");
192             return false;
193         }
194
195         // Read unicode cmap
196
seek_tab(in, "cmap", cmap_unioffset);
197         int cmap_format = in.readTTFUShort();
198         int cmap_length = in.readTTFUShort();
199
200         // System.out.println("CMAP format: "+cmap_format);
201
if (cmap_format == 4) {
202             in.skip(2); // Skip version number
203
int cmap_segCountX2 = in.readTTFUShort();
204             int cmap_searchRange = in.readTTFUShort();
205             int cmap_entrySelector = in.readTTFUShort();
206             int cmap_rangeShift = in.readTTFUShort();
207
208             /*
209              * System.out.println("segCountX2 : "+cmap_segCountX2);
210              * System.out.println("searchRange : "+cmap_searchRange);
211              * System.out.println("entrySelector: "+cmap_entrySelector);
212              * System.out.println("rangeShift : "+cmap_rangeShift);
213              */

214
215             int cmap_endCounts[] = new int[cmap_segCountX2 / 2];
216             int cmap_startCounts[] = new int[cmap_segCountX2 / 2];
217             int cmap_deltas[] = new int[cmap_segCountX2 / 2];
218             int cmap_rangeOffsets[] = new int[cmap_segCountX2 / 2];
219
220             for (int i = 0; i < (cmap_segCountX2 / 2); i++) {
221                 cmap_endCounts[i] = in.readTTFUShort();
222             }
223
224             in.skip(2); // Skip reservedPad
225

226             for (int i = 0; i < (cmap_segCountX2 / 2); i++) {
227                 cmap_startCounts[i] = in.readTTFUShort();
228             }
229
230             for (int i = 0; i < (cmap_segCountX2 / 2); i++) {
231                 cmap_deltas[i] = in.readTTFShort();
232             }
233
234             //int startRangeOffset = in.getCurrentPos();
235

236             for (int i = 0; i < (cmap_segCountX2 / 2); i++) {
237                 cmap_rangeOffsets[i] = in.readTTFUShort();
238             }
239
240             int glyphIdArrayOffset = in.getCurrentPos();
241
242             // Insert the unicode id for the glyphs in mtx_tab
243
// and fill in the cmaps ArrayList
244

245             for (int i = 0; i < cmap_startCounts.length; i++) {
246                 /*
247                  * System.out.println(i+ ": "+cmap_startCounts[i]+
248                  * " - "+cmap_endCounts[i]);
249                  */

250                 for (int j = cmap_startCounts[i]; j <= cmap_endCounts[i];
251                         j++) {
252
253                     // Update lastChar
254
if (j < 256 && j > lastChar) {
255                         lastChar = (short)j;
256                     }
257
258                     if (mtxPtr < mtx_tab.length) {
259                         int glyphIdx;
260                         // the last character 65535 = .notdef
261
// may have a range offset
262
if (cmap_rangeOffsets[i] != 0 && j != 65535) {
263                             int glyphOffset =
264                                 glyphIdArrayOffset
265                                 + ((cmap_rangeOffsets[i] / 2) + (j - cmap_startCounts[i]) + (i) - cmap_segCountX2 / 2)
266                                   * 2;
267                             in.seek_set(glyphOffset);
268                             glyphIdx = (in.readTTFUShort() + cmap_deltas[i])
269                                        & 0xffff;
270
271                             unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
272                             mtx_tab[glyphIdx].unicodeIndex.add(new Integer JavaDoc(j));
273                             // Also add winAnsiWidth
274
ArrayList JavaDoc v = (ArrayList JavaDoc)ansiIndex.get(new Integer JavaDoc(j));
275                             if (v != null) {
276                                 for (int k = 0; k < v.size(); k++ ) {
277                                     Integer JavaDoc aIdx = (Integer JavaDoc)v.get(k);
278                                     ansiWidth[aIdx.intValue()] =
279                                     mtx_tab[glyphIdx].wx;
280                                     /*
281                                      * System.out.println("Added width "+
282                                      * mtx_tab[glyphIdx].wx +
283                                      * " uni: " + j +
284                                      * " ansi: " + aIdx.intValue());
285                                      */

286                                 }
287                             }
288                             /*
289                              * System.out.println("Idx: "+
290                              * glyphIdx +
291                              * " Delta: " + cmap_deltas[i]+
292                              * " Unicode: " + j +
293                              * " name: " +
294                              * mtx_tab[glyphIdx].name);
295                              */

296
297                         } else {
298
299                             glyphIdx = (j + cmap_deltas[i]) & 0xffff;
300
301                             if (glyphIdx < mtx_tab.length) {
302                                 mtx_tab[glyphIdx].unicodeIndex.add(new Integer JavaDoc(j));
303                             } else {
304                                 System.out.println("Glyph " + glyphIdx
305                                                    + " out of range: "
306                                                    + mtx_tab.length);
307                             }
308
309                             unicodeMapping.add(new UnicodeMapping(glyphIdx,
310                                     j));
311                             if (glyphIdx < mtx_tab.length) {
312                                 mtx_tab[glyphIdx].unicodeIndex.add(new Integer JavaDoc(j));
313                             } else {
314                                 System.out.println("Glyph " + glyphIdx
315                                                    + " out of range: "
316                                                    + mtx_tab.length);
317                             }
318
319
320
321                             // Also add winAnsiWidth
322
ArrayList JavaDoc v = (ArrayList JavaDoc)ansiIndex.get(new Integer JavaDoc(j));
323                             if (v != null) {
324                                 for (int k = 0; k < v.size(); k++ ) {
325                                     Integer JavaDoc aIdx = (Integer JavaDoc)v.get(k);
326                                     ansiWidth[aIdx.intValue()] =
327                                     mtx_tab[glyphIdx].wx;
328                                 }
329                             }
330
331                             /*
332                              * System.out.println("IIdx: "+
333                              * mtxPtr +
334                              * " Delta: " + cmap_deltas[i]+
335                              * " Unicode: " + j +
336                              * " name: " +
337                              * mtx_tab[(j+cmap_deltas[i]) & 0xffff].name);
338                              */

339                         }
340                         if (glyphIdx < mtx_tab.length) {
341                             if (mtx_tab[glyphIdx].unicodeIndex.size() < 2) {
342                                 mtxPtr++;
343                             }
344                         }
345                     }
346                 }
347             }
348         }
349         return true;
350     }
351
352
353
354     /**
355      * Print first char/last char
356      */

357     private void print_max_min() {
358         int min = 255;
359         int max = 0;
360         for (int i = 0; i < mtx_tab.length; i++) {
361             if (mtx_tab[i].index < min)
362                 min = mtx_tab[i].index;
363             if (mtx_tab[i].index > max)
364                 max = mtx_tab[i].index;
365         }
366         System.out.println("Min: " + min);
367         System.out.println("Max: " + max);
368     }
369
370     public void readFont(FontFileReader in) throws IOException {
371         readFont(in, (String JavaDoc)null);
372     }
373
374     /**
375      * initialize the ansiWidths array (for winAnsiEncoding)
376      * and fill with the missingwidth
377      */

378     private void initAnsiWidths() {
379         ansiWidth = new int[256];
380         for (int i = 0; i < 256; i++) {
381             ansiWidth[i] = mtx_tab[0].wx;
382         }
383
384         // Create an index hash to the ansiWidth
385
// Can't just index the winAnsiEncoding when inserting widths
386
// same char (eg bullet) is repeated more than one place
387
ansiIndex = new HashMap JavaDoc();
388         for (int i = 32; i < Glyphs.winAnsiEncoding.length; i++) {
389             Integer JavaDoc ansi = new Integer JavaDoc(i);
390             Integer JavaDoc uni = new Integer JavaDoc((int)Glyphs.winAnsiEncoding[i]);
391
392             ArrayList JavaDoc v = (ArrayList JavaDoc)ansiIndex.get(uni);
393             if (v == null) {
394                 v = new ArrayList JavaDoc();
395                 ansiIndex.put(uni, v);
396             }
397             v.add(ansi);
398         }
399     }
400
401
402     /**
403      * Read the font data
404      * If the fontfile is a TrueType Collection (.ttc file)
405      * The name of the font to read data for must be supplied,
406      * else the name is ignored
407      */

408     public void readFont(FontFileReader in, String JavaDoc name) throws IOException {
409
410         /*
411          * Check if TrueType collection, and that the name
412          * exists in the collection
413          */

414         if (!checkTTC(in, name, true)) {
415             if (name == null) {
416                 throw new IllegalArgumentException JavaDoc(
417                     "For TrueType collection you must specify which font "
418                     + "to select (-ttcname)");
419             } else {
420                 throw new IOException(
421                     "Name does not exist in the TrueType collection: " + name);
422             }
423         }
424
425         readDirTabs(in);
426         readFontHeader(in);
427         getNumGlyphs(in);
428         System.out.println("Number of glyphs in font: " + nglyphs);
429         readHorizontalHeader(in);
430         readHorizontalMetrics(in);
431         initAnsiWidths();
432         readPostscript(in);
433         readOS2(in);
434         readIndexToLocation(in);
435         readGlyf(in);
436         readName(in);
437         readPCLT(in);
438         readCMAP(in); // Read cmap table and fill in ansiwidths
439
createCMaps(); // Create cmaps for bfentries
440
// print_max_min();
441

442         readKerning(in);
443     }
444
445     private void createCMaps() {
446         cmaps = new ArrayList JavaDoc();
447         TTFCmapEntry tce = new TTFCmapEntry();
448
449         UnicodeMapping um = (UnicodeMapping)unicodeMapping.get(0);
450         UnicodeMapping lastMapping = um;
451
452         tce.unicodeStart = um.uIdx;
453         tce.glyphStartIndex = um.gIdx;
454
455         for (int i = 1; i< unicodeMapping.size(); i++) {
456             um = (UnicodeMapping)unicodeMapping.get(i);
457             if (((lastMapping.uIdx + 1) != um.uIdx)
458                     || ((lastMapping.gIdx + 1) != um.gIdx)) {
459                 tce.unicodeEnd = lastMapping.uIdx;
460                 cmaps.add(tce);
461
462                 tce = new TTFCmapEntry();
463                 tce.unicodeStart = um.uIdx;
464                 tce.glyphStartIndex = um.gIdx;
465             }
466             lastMapping = um;
467         }
468
469         tce.unicodeEnd = um.uIdx;
470         cmaps.add(tce);
471     }
472
473     public void printStuff() {
474         System.out.println("Font name: " + fontName);
475         System.out.println("Full name: " + fullName);
476         System.out.println("Family name: " + familyName);
477         System.out.println("Subfamily name: " + subFamilyName);
478         System.out.println("Notice: " + notice);
479         System.out.println("xHeight: " + (int)get_ttf_funit(xHeight));
480         System.out.println("capheight: " + (int)get_ttf_funit(capHeight));
481
482         int italic = (int)(italicAngle >> 16);
483         System.out.println("Italic: " + italic);
484         System.out.print("ItalicAngle: " + (short)(italicAngle / 0x10000));
485         if ((italicAngle % 0x10000) > 0) {
486             System.out.print("."
487                              + (short)((italicAngle % 0x10000) * 1000)
488                                / 0x10000);
489         }
490         System.out.println();
491         System.out.println("Ascender: " + get_ttf_funit(ascender));
492         System.out.println("Descender: " + get_ttf_funit(descender));
493         System.out.println("FontBBox: [" + (int)get_ttf_funit(fontBBox1)
494                            + " " + (int)get_ttf_funit(fontBBox2) + " "
495                            + (int)get_ttf_funit(fontBBox3) + " "
496                            + (int)get_ttf_funit(fontBBox4) + "]");
497     }
498
499     public static void main(String JavaDoc[] args) {
500         try {
501             TTFFile ttfFile = new TTFFile();
502             FontFileReader reader = new FontFileReader(args[0]);
503
504             String JavaDoc name = null;
505             if (args.length >= 2) {
506                 name = args[1];
507             }
508
509             ttfFile.readFont(reader, name);
510             ttfFile.printStuff();
511
512         } catch (IOException ioe) {
513             System.out.println(ioe.toString());
514         }
515     }
516
517     public String JavaDoc getWindowsName() {
518         return new String JavaDoc(familyName + "," + subFamilyName);
519     }
520
521     public String JavaDoc getPostscriptName() {
522         if ("Regular".equals(subFamilyName) || "Roman".equals(subFamilyName)) {
523             return familyName;
524         } else {
525             return familyName + "," + subFamilyName;
526         }
527     }
528
529     public String JavaDoc getFamilyName() {
530         return familyName;
531     }
532
533     public String JavaDoc getCharSetName() {
534         return encoding;
535     }
536
537     public int getCapHeight() {
538         return (int)get_ttf_funit(capHeight);
539     }
540
541     public int getXHeight() {
542         return (int)get_ttf_funit(xHeight);
543     }
544
545     public int getFlags() {
546         int flags = 32; // Use Adobe Standard charset
547
if (italicAngle != 0) {
548             flags = flags | 64;
549         }
550         if (isFixedPitch != 0) {
551             flags = flags | 2;
552         }
553         if (hasSerifs) {
554             flags = flags | 1;
555         }
556         return flags;
557     }
558
559
560     public String JavaDoc getStemV() {
561         return "0";
562     }
563
564     public String JavaDoc getItalicAngle() {
565         String JavaDoc ia = Short.toString((short)(italicAngle / 0x10000));
566
567         // This is the correct italic angle, however only int italic
568
// angles are supported at the moment so this is commented out.
569
/*
570          * if ((italicAngle % 0x10000) > 0 )
571          * ia=ia+(comma+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000)));
572          */

573         return ia;
574     }
575
576     public int[] getFontBBox() {
577         int[] fbb = new int[4];
578         fbb[0] = (int)get_ttf_funit(fontBBox1);
579         fbb[1] = (int)get_ttf_funit(fontBBox2);
580         fbb[2] = (int)get_ttf_funit(fontBBox3);
581         fbb[3] = (int)get_ttf_funit(fontBBox4);
582
583         return fbb;
584     }
585
586     public int getLowerCaseAscent() {
587         return (int)get_ttf_funit(ascender);
588     }
589
590     public int getLowerCaseDescent() {
591         return (int)get_ttf_funit(descender);
592     }
593
594     // This is only for WinAnsiEncoding, so the last char is
595
// the last char < 256
596
public short getLastChar() {
597         return lastChar;
598     }
599
600     public short getFirstChar() {
601         return firstChar;
602     }
603
604     public int[] getWidths() {
605         int[] wx = new int[mtx_tab.length];
606         for (int i = 0; i < wx.length; i++) {
607             wx[i] = (int)get_ttf_funit(mtx_tab[i].wx);
608         }
609
610         return wx;
611     }
612
613     public int getCharWidth(int idx) {
614         return (int)get_ttf_funit(ansiWidth[idx]);
615     }
616
617     public HashMap JavaDoc getKerning() {
618         return kerningTab;
619     }
620
621     public HashMap JavaDoc getAnsiKerning() {
622         return ansiKerningTab;
623     }
624
625     public boolean isEmbeddable() {
626         return is_embeddable;
627     }
628
629
630     /**
631      * Read Table Directory from the current position in the
632      * FontFileReader and fill the global HashMap dirTabs
633      * with the table name (String) as key and a TTFDirTabEntry
634      * as value.
635      */

636     protected void readDirTabs(FontFileReader in) throws IOException {
637         in.skip(4); // TTF_FIXED_SIZE
638
int ntabs = in.readTTFUShort();
639         in.skip(6); // 3xTTF_USHORT_SIZE
640

641         dirTabs = new HashMap JavaDoc();
642         TTFDirTabEntry[] pd = new TTFDirTabEntry[ntabs];
643         // System.out.println("Reading " + ntabs + " dir tables");
644
for (int i = 0; i < ntabs; i++) {
645             pd[i] = new TTFDirTabEntry();
646             dirTabs.put(pd[i].read(in), pd[i]);
647         }
648     }
649
650     /**
651      * Read the "head" table, this reads the bounding box and
652      * sets the upem (unitsPerEM) variable
653      */

654     protected void readFontHeader(FontFileReader in) throws IOException {
655         seek_tab(in, "head", 2 * 4 + 2 * 4 + 2);
656         upem = in.readTTFUShort();
657
658         in.skip(16);
659
660         fontBBox1 = in.readTTFShort();
661         fontBBox2 = in.readTTFShort();
662         fontBBox3 = in.readTTFShort();
663         fontBBox4 = in.readTTFShort();
664
665         in.skip(2 + 2 + 2);
666
667         loca_format = in.readTTFShort();
668     }
669
670     /**
671      * Read the number of glyphs from the "maxp" table
672      */

673     protected void getNumGlyphs(FontFileReader in) throws IOException {
674         seek_tab(in, "maxp", 4);
675         nglyphs = in.readTTFUShort();
676     }
677
678
679     /**
680      * Read the "hhea" table to find the ascender and descender and
681      * size of "hmtx" table, i.e. a fixed size font might have only
682      * one width
683      */

684     protected void readHorizontalHeader(FontFileReader in)
685             throws IOException {
686         seek_tab(in, "hhea", 4);
687         ascender = in.readTTFShort(); // Use sTypoAscender in "OS/2" table?
688
descender = in.readTTFShort(); // Use sTypoDescender in "OS/2" table?
689

690         in.skip(2 + 2 + 3 * 2 + 8 * 2);
691         nhmtx = in.readTTFUShort();
692         // System.out.println("Number of horizontal metrics: " + nhmtx);
693

694         //Check OS/2 table for ascender/descender if necessary
695
if (ascender == 0 || descender == 0) {
696             seek_tab(in, "OS/2", 68);
697             if (this.currentDirTab.length >= 78) {
698                 ascender = in.readTTFShort(); //sTypoAscender
699
descender = in.readTTFShort(); //sTypoDescender
700
}
701         }
702     }
703
704     /**
705      * Read "hmtx" table and put the horizontal metrics
706      * in the mtx_tab array. If the number of metrics is less
707      * than the number of glyphs (eg fixed size fonts), extend
708      * the mtx_tab array and fill in the missing widths
709      */

710     protected void readHorizontalMetrics(FontFileReader in)
711             throws IOException {
712         seek_tab(in, "hmtx", 0);
713
714         int mtx_size = (nglyphs > nhmtx) ? nglyphs : nhmtx;
715         mtx_tab = new TTFMtxEntry[mtx_size];
716
717         // System.out.println("*** Widths array: \n");
718
for (int i = 0; i < mtx_size; i++) {
719             mtx_tab[i] = new TTFMtxEntry();
720         }
721         for (int i = 0; i < nhmtx; i++) {
722             mtx_tab[i].wx = in.readTTFUShort();
723             mtx_tab[i].lsb = in.readTTFShort();
724             /*
725              * System.out.println(" width["+i+"] = "+
726              * get_ttf_funit(mtx_tab[i].wx)+";");
727              */

728         }
729
730         if (nhmtx < mtx_size) {
731             // Fill in the missing widths
732
int lastWidth = mtx_tab[nhmtx - 1].wx;
733             for (int i = nhmtx; i < mtx_size; i++) {
734                 mtx_tab[i].wx = lastWidth;
735                 mtx_tab[i].lsb = in.readTTFShort();
736             }
737         }
738     }
739
740
741     /**
742      * Read the "post" table
743      * containing the postscript names of the glyphs.
744      */

745     private final void readPostscript(FontFileReader in) throws IOException {
746         String JavaDoc[] ps_glyphs_buf;
747         int i, k, l;
748
749         seek_tab(in, "post", 0);
750         post_format = in.readTTFLong();
751         italicAngle = in.readTTFULong();
752         underlinePosition = in.readTTFShort();
753         underlineThickness = in.readTTFShort();
754         isFixedPitch = in.readTTFULong();
755
756         in.skip(4 * 4);
757
758         // System.out.println("Post format: "+post_format);
759
switch (post_format) {
760         case 0x00010000:
761             // System.out.println("Postscript format 1");
762
for (i = 0; i < Glyphs.mac_glyph_names.length; i++) {
763                 mtx_tab[i].name = Glyphs.mac_glyph_names[i];
764             }
765             break;
766         case 0x00020000:
767             // System.out.println("Postscript format 2");
768
int numGlyphStrings = 0;
769             l = in.readTTFUShort(); // Num Glyphs
770
// short minIndex=256;
771
for (i = 0; i < l; i++) { // Read indexes
772
mtx_tab[i].index = in.readTTFUShort();
773                 // if (minIndex > mtx_tab[i].index)
774
// minIndex=(short)mtx_tab[i].index;
775

776                 if (mtx_tab[i].index > 257)
777                     numGlyphStrings++;
778
779                     // System.out.println("Post index: "+mtx_tab[i].index);
780
}
781             // firstChar=minIndex;
782
ps_glyphs_buf = new String JavaDoc[numGlyphStrings];
783             // System.out.println("Reading " + numGlyphStrings +
784
// " glyphnames"+
785
// ", was n num glyphs="+l);
786
for (i = 0; i < ps_glyphs_buf.length; i++) {
787                 ps_glyphs_buf[i] = in.readTTFString(in.readTTFUByte());
788             }
789
790             for (i = 0; i < l; i++) {
791                 if (mtx_tab[i].index < NMACGLYPHS) {
792                     mtx_tab[i].name =
793                         Glyphs.mac_glyph_names[mtx_tab[i].index];
794                 } else {
795                     if (!mtx_tab[i].isIndexReserved()) {
796                         k = mtx_tab[i].index - NMACGLYPHS;
797                         /*
798                          * System.out.println(k+" i="+i+" mtx="+mtx_tab.length+
799                          * " ps="+ps_glyphs_buf.length);
800                          */

801                         mtx_tab[i].name = ps_glyphs_buf[k];
802                     }
803                 }
804             }
805
806             break;
807         case 0x00030000:
808             // Postscript format 3 contains no glyph names
809
System.out.println("Postscript format 3");
810             break;
811         default:
812             System.out.println("Unknown Postscript format : " + post_format);
813         }
814     }
815
816
817     /**
818      * Read the "OS/2" table
819      */

820     private final void readOS2(FontFileReader in) throws IOException {
821         // Check if font is embeddable
822
if (dirTabs.get("OS/2") != null) {
823             seek_tab(in, "OS/2", 2 * 4);
824             int fsType = in.readTTFUShort();
825             if (fsType == 2) {
826                 is_embeddable = false;
827             } else {
828                 is_embeddable = true;
829             }
830         } else {
831             is_embeddable = true;
832         }
833     }
834
835     /**
836      * Read the "loca" table
837      */

838     protected final void readIndexToLocation(FontFileReader in)
839             throws IOException {
840         seek_tab(in, "loca", 0);
841         for (int i = 0; i < nglyphs; i++) {
842             mtx_tab[i].offset = (loca_format == 1 ? in.readTTFULong()
843                                  : (in.readTTFUShort() << 1));
844         }
845         lastLoca = (loca_format == 1 ? in.readTTFULong()
846                     : (in.readTTFUShort() << 1));
847     }
848
849     /**
850      * Read the "glyf" table to find the bounding boxes
851      */

852     private final void readGlyf(FontFileReader in) throws IOException {
853         TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("glyf");
854         for (int i = 0; i < (nglyphs - 1); i++) {
855             if (mtx_tab[i].offset != mtx_tab[i + 1].offset) {
856                 in.seek_set(dirTab.offset + mtx_tab[i].offset);
857                 in.skip(2);
858                 mtx_tab[i].bbox[0] = in.readTTFShort();
859                 mtx_tab[i].bbox[1] = in.readTTFShort();
860                 mtx_tab[i].bbox[2] = in.readTTFShort();
861                 mtx_tab[i].bbox[3] = in.readTTFShort();
862             } else {
863                 mtx_tab[i].bbox[0] = mtx_tab[0].bbox[0];
864                 mtx_tab[i].bbox[1] = mtx_tab[0].bbox[1];
865                 mtx_tab[i].bbox[2] = mtx_tab[0].bbox[2];
866                 mtx_tab[i].bbox[3] = mtx_tab[0].bbox[3];
867             }
868         }
869
870
871         long n = ((TTFDirTabEntry)dirTabs.get("glyf")).offset;
872         for (int i = 0; i < nglyphs; i++) {
873             if ((i + 1) >= mtx_tab.length
874                     || mtx_tab[i].offset != mtx_tab[i + 1].offset) {
875                 in.seek_set(n + mtx_tab[i].offset);
876                 in.skip(2);
877                 mtx_tab[i].bbox[0] = in.readTTFShort();
878                 mtx_tab[i].bbox[1] = in.readTTFShort();
879                 mtx_tab[i].bbox[2] = in.readTTFShort();
880                 mtx_tab[i].bbox[3] = in.readTTFShort();
881             } else {
882                 mtx_tab[i].bbox[0] = mtx_tab[0].bbox[0];
883                 mtx_tab[i].bbox[1] = mtx_tab[0].bbox[0];
884                 mtx_tab[i].bbox[2] = mtx_tab[0].bbox[0];
885                 mtx_tab[i].bbox[3] = mtx_tab[0].bbox[0];
886             }
887             // System.out.println(mtx_tab[i].toString(this));
888
}
889     }
890
891     /**
892      * Read the "name" table
893      */

894     private final void readName(FontFileReader in) throws IOException {
895         seek_tab(in, "name", 2);
896         int i = in.getCurrentPos();
897         int n = in.readTTFUShort();
898         int j = in.readTTFUShort() + i - 2;
899         i += 2 * 2;
900
901         while (n-- > 0) {
902             // System.out.println("Iteration: "+n);
903
in.seek_set(i);
904             int platform_id = in.readTTFUShort();
905             int encoding_id = in.readTTFUShort();
906             int language_id = in.readTTFUShort();
907
908             int k = in.readTTFUShort();
909             int l = in.readTTFUShort();
910
911             if (((platform_id == 1 || platform_id == 3) && (encoding_id == 0 || encoding_id == 1))
912                     && (k == 1 || k == 2 || k == 0 || k == 4 || k == 6)) {
913                 // if (k==1 || k==2 || k==0 || k==4 || k==6) {
914
in.seek_set(j + in.readTTFUShort());
915                 String JavaDoc txt = in.readTTFString(l);
916                 // System.out.println(platform_id+" "+encoding_id+
917
// " "+k+" "+txt);
918
switch (k) {
919                 case 0:
920                     notice = txt;
921                     break;
922                 case 1:
923                     familyName = txt;
924                     break;
925                 case 2:
926                     subFamilyName = txt;
927                     break;
928                 case 4:
929                     fullName = txt;
930                     break;
931                 case 6:
932                     fontName = txt;
933                     break;
934                 }
935                 if (!notice.equals("") &&!fullName.equals("")
936                         &&!fontName.equals("") &&!familyName.equals("")
937                         &&!subFamilyName.equals("")) {
938                     break;
939                 }
940             }
941             i += 6 * 2;
942         }
943     }
944
945     /**
946      * Read the "PCLT" table to find xHeight and capHeight
947      */

948     private final void readPCLT(FontFileReader in) throws IOException {
949         TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("PCLT");
950         if (dirTab != null) {
951             in.seek_set(dirTab.offset + 4 + 4 + 2);
952             xHeight = in.readTTFUShort();
953             in.skip(2 * 2);
954             capHeight = in.readTTFUShort();
955             in.skip(2 + 16 + 8 + 6 + 1 + 1);
956
957             int serifStyle = in.readTTFUByte();
958             serifStyle = serifStyle >> 6;
959             serifStyle = serifStyle & 3;
960             if (serifStyle == 1) {
961                 hasSerifs = false;
962             } else {
963                 hasSerifs = true;
964             }
965
966         } else {
967             // Approximate capHeight from height of "H"
968
// It's most unlikly that a font misses the PCLT table
969
// This also assumes that psocriptnames exists ("H")
970
// Should look it up int the cmap (that wouldn't help
971
// for charsets without H anyway...)
972
for (int i = 0; i < mtx_tab.length; i++) {
973                 if ("H".equals(mtx_tab[i].name)) {
974                     capHeight = mtx_tab[i].bbox[3] - mtx_tab[i].bbox[1];
975                 }
976             }
977         }
978     }
979
980     /**
981      * Read the kerning table, create a table for both CIDs and
982      * winAnsiEncoding
983      */

984     private final void readKerning(FontFileReader in) throws IOException {
985         // Read kerning
986
kerningTab = new HashMap JavaDoc();
987         ansiKerningTab = new HashMap JavaDoc();
988         TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("kern");
989         if (dirTab != null) {
990             seek_tab(in, "kern", 2);
991             for (int n = in.readTTFUShort(); n > 0; n--) {
992                 in.skip(2 * 2);
993                 int k = in.readTTFUShort();
994                 if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) {
995                     return;
996                 }
997                 if ((k >> 8) != 0) {
998                     continue;
999                 }
1000
1001                k = in.readTTFUShort();
1002                in.skip(3 * 2);
1003                while (k-- > 0) {
1004                    int i = in.readTTFUShort();
1005                    int j = in.readTTFUShort();
1006                    int kpx = in.readTTFShort();
1007                    if (kpx != 0) {
1008                        // CID table
1009
Integer JavaDoc iObj = new Integer JavaDoc(i);
1010                        HashMap JavaDoc adjTab = (HashMap JavaDoc)kerningTab.get(iObj);
1011                        if (adjTab == null) {
1012                            adjTab = new HashMap JavaDoc();
1013                        }
1014                        adjTab.put(new Integer JavaDoc(j),
1015                                   new Integer JavaDoc((int)get_ttf_funit(kpx)));
1016                        kerningTab.put(iObj, adjTab);
1017                    }
1018                }
1019            }
1020            // System.out.println(kerningTab.toString());
1021

1022            // Create winAnsiEncoded kerning table
1023

1024            for (Iterator JavaDoc ae = kerningTab.keySet().iterator(); ae.hasNext(); ) {
1025                Integer JavaDoc cidKey = (Integer JavaDoc)ae.next();
1026                HashMap JavaDoc akpx = new HashMap JavaDoc();
1027                HashMap JavaDoc ckpx = (HashMap JavaDoc)kerningTab.get(cidKey);
1028
1029                for (Iterator JavaDoc aee = ckpx.keySet().iterator(); aee.hasNext(); ) {
1030                    Integer JavaDoc cidKey2 = (Integer JavaDoc)aee.next();
1031                    Integer JavaDoc kern = (Integer JavaDoc)ckpx.get(cidKey2);
1032
1033                    ArrayList JavaDoc unicodeIndex = mtx_tab[cidKey2.intValue()].unicodeIndex;
1034                    for (int i = 0; i < unicodeIndex.size(); i++) {
1035                        Integer JavaDoc unicodeKey = (Integer JavaDoc)unicodeIndex.get(i);
1036                        Integer JavaDoc[] ansiKeys =
1037                            unicodeToWinAnsi(unicodeKey.intValue());
1038                        for (int u = 0; u < ansiKeys.length; u++) {
1039                            akpx.put(ansiKeys[u], kern);
1040                        }
1041                    }
1042                }
1043
1044                if (akpx.size() > 0) {
1045                    ArrayList JavaDoc unicodeIndex = mtx_tab[cidKey.intValue()].unicodeIndex;
1046                    for (int i = 0; i< unicodeIndex.size(); i++) {
1047                        Integer JavaDoc unicodeKey = (Integer JavaDoc)unicodeIndex.get(i);
1048                        Integer JavaDoc[] ansiKeys =
1049                            unicodeToWinAnsi(unicodeKey.intValue());
1050                        for (int u = 0; u < ansiKeys.length; u++) {
1051                            ansiKerningTab.put(ansiKeys[u], akpx);
1052                        }
1053                    }
1054                }
1055            }
1056        }
1057    }
1058
1059    /**
1060     * Return a ArrayList with TTFCmapEntry
1061     */

1062    public ArrayList JavaDoc getCMaps() {
1063        return cmaps;
1064    }
1065
1066    /**
1067     * Check if this is a TrueType collection and that the given
1068     * name exists in the collection.
1069     * If it does, set offset in fontfile to the beginning of
1070     * the Table Directory for that font
1071     * @return true if not collection or font name present, false
1072     * otherwise
1073     */

1074    protected final boolean checkTTC(FontFileReader in, String JavaDoc name,
1075                                     boolean verbose) throws IOException {
1076        String JavaDoc tag = in.readTTFString(4);
1077
1078        if ("ttcf".equals(tag)) {
1079            // This is a TrueType Collection
1080
in.skip(4);
1081
1082            // Read directory offsets
1083
int numDirectories = (int)in.readTTFULong();
1084            // int numDirectories=in.readTTFUShort();
1085
long[] dirOffsets = new long[numDirectories];
1086            for (int i = 0; i < numDirectories; i++) {
1087                dirOffsets[i] = in.readTTFULong();
1088            }
1089
1090            if (verbose) {
1091                System.out.println("This is a TrueType collection file with "
1092                                   + numDirectories + " fonts");
1093                System.out.println("Containing the following fonts: ");
1094            }
1095            // Read all the directories and name tables to check
1096
// If the font exists - this is a bit ugly, but...
1097
boolean found = false;
1098
1099            // Iterate through all name tables even if font
1100
// Is found, just to show all the names
1101
long dirTabOffset = 0;
1102            for (int i = 0; (i < numDirectories); i++) {
1103                in.seek_set(dirOffsets[i]);
1104                readDirTabs(in);
1105
1106                readName(in);
1107
1108                if (fullName.equals(name)) {
1109                    found = true;
1110                    dirTabOffset = dirOffsets[i];
1111                    if (verbose) {
1112                        System.out.println("* " + fullName);
1113                    }
1114                } else {
1115                    if (verbose) {
1116                        System.out.println(fullName);
1117                    }
1118                }
1119
1120                // Reset names
1121
notice = "";
1122                fullName = "";
1123                familyName = "";
1124                fontName = "";
1125                subFamilyName = "";
1126            }
1127
1128            in.seek_set(dirTabOffset);
1129            return found;
1130        } else {
1131            in.seek_set(0);
1132            return true;
1133        }
1134    }
1135
1136    /*
1137     * Helper classes, they are not very efficient, but that really
1138     * doesn't matter...
1139     */

1140    private Integer JavaDoc[] unicodeToWinAnsi(int unicode) {
1141        ArrayList JavaDoc ret = new ArrayList JavaDoc();
1142        for (int i = 32; i < Glyphs.winAnsiEncoding.length; i++) {
1143            if (unicode == Glyphs.winAnsiEncoding[i]) {
1144                ret.add(new Integer JavaDoc(i));
1145            }
1146        }
1147        Integer JavaDoc[] itg = new Integer JavaDoc[ret.size()];
1148        ret.toArray(itg);
1149        return itg;
1150    }
1151
1152}
1153
1154
1155/**
1156 * Key-value helper class
1157 */

1158class UnicodeMapping {
1159    int uIdx;
1160    int gIdx;
1161    UnicodeMapping(int gIdx, int uIdx) {
1162        this.uIdx = uIdx;
1163        this.gIdx = gIdx;
1164    }
1165
1166}
1167
Popular Tags