KickJava   Java API By Example, From Geeks To Geeks.

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


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
18 /* $Id: TTFFile.java 463581 2006-10-13 07:45:19Z bdelacretaz $ */
19
20 package org.apache.fop.fonts.truetype;
21
22 import java.io.IOException JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.Map JavaDoc;
26 import java.util.List JavaDoc;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.fop.fonts.Glyphs;
31
32 /**
33  * Reads a TrueType file or a TrueType Collection.
34  * The TrueType spec can be found at the Microsoft.
35  * Typography site: http://www.microsoft.com/truetype/
36  */

37 public class TTFFile {
38
39     static final byte NTABS = 24;
40     static final int NMACGLYPHS = 258;
41     static final int MAX_CHAR_CODE = 255;
42     static final int ENC_BUF_SIZE = 1024;
43
44     /** Set to true to get even more debug output than with level DEBUG */
45     public static final boolean TRACE_ENABLED = false;
46
47     private String JavaDoc encoding = "WinAnsiEncoding"; // Default encoding
48

49     private short firstChar = 0;
50     private boolean isEmbeddable = true;
51     private boolean hasSerifs = true;
52     /**
53      * Table directory
54      */

55     protected Map JavaDoc dirTabs;
56     private Map JavaDoc kerningTab; // for CIDs
57
private Map JavaDoc ansiKerningTab; // For winAnsiEncoding
58
private List JavaDoc cmaps;
59     private List JavaDoc unicodeMapping;
60
61     private int upem; // unitsPerEm from "head" table
62
private int nhmtx; // Number of horizontal metrics
63
private int postFormat;
64     private int locaFormat;
65     /**
66      * Offset to last loca
67      */

68     protected long lastLoca = 0;
69     private int numberOfGlyphs; // Number of glyphs in font (read from "maxp" table)
70
private int nmGlyphs; // Used in fixWidths - remove?
71

72     /**
73      * Contains glyph data
74      */

75     protected TTFMtxEntry[] mtxTab; // Contains glyph data
76
private int[] mtxEncoded = null;
77
78     private String JavaDoc fontName = "";
79     private String JavaDoc fullName = "";
80     private String JavaDoc notice = "";
81     private String JavaDoc familyName = "";
82     private String JavaDoc subFamilyName = "";
83
84     private long italicAngle = 0;
85     private long isFixedPitch = 0;
86     private int fontBBox1 = 0;
87     private int fontBBox2 = 0;
88     private int fontBBox3 = 0;
89     private int fontBBox4 = 0;
90     private int capHeight = 0;
91     private int underlinePosition = 0;
92     private int underlineThickness = 0;
93     private int xHeight = 0;
94     //Effective ascender/descender
95
private int ascender = 0;
96     private int descender = 0;
97     //Ascender/descender from hhea table
98
private int hheaAscender = 0;
99     private int hheaDescender = 0;
100     //Ascender/descender from OS/2 table
101
private int os2Ascender = 0;
102     private int os2Descender = 0;
103
104     private short lastChar = 0;
105
106     private int[] ansiWidth;
107     private Map JavaDoc ansiIndex;
108     
109     // internal mapping of glyph indexes to unicode indexes
110
// used for quick mappings in this class
111
private Map JavaDoc glyphToUnicodeMap = new HashMap JavaDoc();
112     private Map JavaDoc unicodeToGlyphMap = new HashMap JavaDoc();
113
114     private TTFDirTabEntry currentDirTab;
115
116     /**
117      * logging instance
118      */

119     protected Log log = LogFactory.getLog(TTFFile.class);
120     
121     /**
122      * Key-value helper class
123      */

124     class UnicodeMapping {
125
126         private int unicodeIndex;
127         private int glyphIndex;
128
129         UnicodeMapping(int glyphIndex, int unicodeIndex) {
130             this.unicodeIndex = unicodeIndex;
131             this.glyphIndex = glyphIndex;
132             glyphToUnicodeMap.put(new Integer JavaDoc(glyphIndex),new Integer JavaDoc(unicodeIndex));
133             unicodeToGlyphMap.put(new Integer JavaDoc(unicodeIndex),new Integer JavaDoc(glyphIndex));
134         }
135
136         /**
137          * Returns the glyphIndex.
138          * @return the glyph index
139          */

140         public int getGlyphIndex() {
141             return glyphIndex;
142         }
143
144         /**
145          * Returns the unicodeIndex.
146          * @return the Unicode index
147          */

148         public int getUnicodeIndex() {
149             return unicodeIndex;
150         }
151     }
152
153     /**
154      * Position inputstream to position indicated
155      * in the dirtab offset + offset
156      */

157     boolean seekTab(FontFileReader in, String JavaDoc name,
158                   long offset) throws IOException JavaDoc {
159         TTFDirTabEntry dt = (TTFDirTabEntry)dirTabs.get(name);
160         if (dt == null) {
161             log.error("Dirtab " + name + " not found.");
162             return false;
163         } else {
164             in.seekSet(dt.getOffset() + offset);
165             this.currentDirTab = dt;
166         }
167         return true;
168     }
169
170     /**
171      * Convert from truetype unit to pdf unit based on the
172      * unitsPerEm field in the "head" table
173      * @param n truetype unit
174      * @return pdf unit
175      */

176     public int convertTTFUnit2PDFUnit(int n) {
177         int ret;
178         if (n < 0) {
179             long rest1 = n % upem;
180             long storrest = 1000 * rest1;
181             long ledd2 = (storrest != 0 ? rest1 / storrest : 0);
182             ret = -((-1000 * n) / upem - (int)ledd2);
183         } else {
184             ret = (n / upem) * 1000 + ((n % upem) * 1000) / upem;
185         }
186
187         return ret;
188     }
189
190     /**
191      * Read the cmap table,
192      * return false if the table is not present or only unsupported
193      * tables are present. Currently only unicode cmaps are supported.
194      * Set the unicodeIndex in the TTFMtxEntries and fills in the
195      * cmaps vector.
196      */

197     private boolean readCMAP(FontFileReader in) throws IOException JavaDoc {
198
199         unicodeMapping = new java.util.ArrayList JavaDoc();
200
201         //Read CMAP table and correct mtxTab.index
202
int mtxPtr = 0;
203
204         seekTab(in, "cmap", 2);
205         int numCMap = in.readTTFUShort(); // Number of cmap subtables
206
long cmapUniOffset = 0;
207
208         log.info(numCMap + " cmap tables");
209
210         //Read offset for all tables. We are only interested in the unicode table
211
for (int i = 0; i < numCMap; i++) {
212             int cmapPID = in.readTTFUShort();
213             int cmapEID = in.readTTFUShort();
214             long cmapOffset = in.readTTFULong();
215
216             log.debug("Platform ID: " + cmapPID + " Encoding: " + cmapEID);
217
218             if (cmapPID == 3 && cmapEID == 1) {
219                 cmapUniOffset = cmapOffset;
220             }
221         }
222
223         if (cmapUniOffset <= 0) {
224             log.fatal("Unicode cmap table not present");
225             log.fatal("Unsupported format: Aborting");
226             return false;
227         }
228
229         // Read unicode cmap
230
seekTab(in, "cmap", cmapUniOffset);
231         int cmapFormat = in.readTTFUShort();
232         /*int cmap_length =*/ in.readTTFUShort(); //skip cmap length
233

234         log.info("CMAP format: " + cmapFormat);
235
236         if (cmapFormat == 4) {
237             in.skip(2); // Skip version number
238
int cmapSegCountX2 = in.readTTFUShort();
239             int cmapSearchRange = in.readTTFUShort();
240             int cmapEntrySelector = in.readTTFUShort();
241             int cmapRangeShift = in.readTTFUShort();
242
243             if (log.isDebugEnabled()) {
244                 log.debug("segCountX2 : " + cmapSegCountX2);
245                 log.debug("searchRange : " + cmapSearchRange);
246                 log.debug("entrySelector: " + cmapEntrySelector);
247                 log.debug("rangeShift : " + cmapRangeShift);
248             }
249
250
251             int[] cmapEndCounts = new int[cmapSegCountX2 / 2];
252             int[] cmapStartCounts = new int[cmapSegCountX2 / 2];
253             int[] cmapDeltas = new int[cmapSegCountX2 / 2];
254             int[] cmapRangeOffsets = new int[cmapSegCountX2 / 2];
255
256             for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
257                 cmapEndCounts[i] = in.readTTFUShort();
258             }
259
260             in.skip(2); // Skip reservedPad
261

262             for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
263                 cmapStartCounts[i] = in.readTTFUShort();
264             }
265
266             for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
267                 cmapDeltas[i] = in.readTTFShort();
268             }
269
270             //int startRangeOffset = in.getCurrentPos();
271

272             for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
273                 cmapRangeOffsets[i] = in.readTTFUShort();
274             }
275
276             int glyphIdArrayOffset = in.getCurrentPos();
277
278             // Insert the unicode id for the glyphs in mtxTab
279
// and fill in the cmaps ArrayList
280

281             for (int i = 0; i < cmapStartCounts.length; i++) {
282
283                 if (log.isTraceEnabled()) {
284                     log.trace(i + ": " + cmapStartCounts[i]
285                                                          + " - " + cmapEndCounts[i]);
286                 }
287
288                 for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) {
289
290                     // Update lastChar
291
if (j < 256 && j > lastChar) {
292                         lastChar = (short)j;
293                     }
294
295                     if (mtxPtr < mtxTab.length) {
296                         int glyphIdx;
297                         // the last character 65535 = .notdef
298
// may have a range offset
299
if (cmapRangeOffsets[i] != 0 && j != 65535) {
300                             int glyphOffset = glyphIdArrayOffset
301                                 + ((cmapRangeOffsets[i] / 2)
302                                     + (j - cmapStartCounts[i])
303                                     + (i)
304                                     - cmapSegCountX2 / 2) * 2;
305                             in.seekSet(glyphOffset);
306                             glyphIdx = (in.readTTFUShort() + cmapDeltas[i])
307                                        & 0xffff;
308
309                             unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
310                             mtxTab[glyphIdx].getUnicodeIndex().add(new Integer JavaDoc(j));
311
312
313                             // Also add winAnsiWidth
314
List JavaDoc v = (List JavaDoc)ansiIndex.get(new Integer JavaDoc(j));
315                             if (v != null) {
316                                 Iterator JavaDoc e = v.listIterator();
317                                 while (e.hasNext()) {
318                                     Integer JavaDoc aIdx = (Integer JavaDoc)e.next();
319                                     ansiWidth[aIdx.intValue()]
320                                         = mtxTab[glyphIdx].getWx();
321
322                                     if (log.isTraceEnabled()) {
323                                         log.trace("Added width "
324                                                 + mtxTab[glyphIdx].getWx()
325                                                 + " uni: " + j
326                                                 + " ansi: " + aIdx.intValue());
327                                     }
328                                 }
329                             }
330
331                             if (log.isTraceEnabled()) {
332                                 log.trace("Idx: "
333                                         + glyphIdx
334                                         + " Delta: " + cmapDeltas[i]
335                                         + " Unicode: " + j
336                                         + " name: " + mtxTab[glyphIdx].getName());
337                             }
338                         } else {
339                             glyphIdx = (j + cmapDeltas[i]) & 0xffff;
340
341                             if (glyphIdx < mtxTab.length) {
342                                 mtxTab[glyphIdx].getUnicodeIndex().add(new Integer JavaDoc(j));
343                             } else {
344                                 log.debug("Glyph " + glyphIdx
345                                                    + " out of range: "
346                                                    + mtxTab.length);
347                             }
348
349                             unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
350                             if (glyphIdx < mtxTab.length) {
351                                 mtxTab[glyphIdx].getUnicodeIndex().add(new Integer JavaDoc(j));
352                             } else {
353                                 log.debug("Glyph " + glyphIdx
354                                                    + " out of range: "
355                                                    + mtxTab.length);
356                             }
357
358                             // Also add winAnsiWidth
359
List JavaDoc v = (List JavaDoc)ansiIndex.get(new Integer JavaDoc(j));
360                             if (v != null) {
361                                 Iterator JavaDoc e = v.listIterator();
362                                 while (e.hasNext()) {
363                                     Integer JavaDoc aIdx = (Integer JavaDoc)e.next();
364                                     ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx();
365                                 }
366                             }
367
368                             //getLogger().debug("IIdx: " +
369
// mtxPtr +
370
// " Delta: " + cmap_deltas[i] +
371
// " Unicode: " + j +
372
// " name: " +
373
// mtxTab[(j+cmap_deltas[i]) & 0xffff].name);
374

375                         }
376                         if (glyphIdx < mtxTab.length) {
377                             if (mtxTab[glyphIdx].getUnicodeIndex().size() < 2) {
378                                 mtxPtr++;
379                             }
380                         }
381                     }
382                 }
383             }
384         }
385         return true;
386     }
387
388     /**
389      * Print first char/last char
390      */

391     private void printMaxMin() {
392         int min = 255;
393         int max = 0;
394         for (int i = 0; i < mtxTab.length; i++) {
395             if (mtxTab[i].getIndex() < min) {
396                 min = mtxTab[i].getIndex();
397             }
398             if (mtxTab[i].getIndex() > max) {
399                 max = mtxTab[i].getIndex();
400             }
401         }
402         log.info("Min: " + min);
403         log.info("Max: " + max);
404     }
405
406
407     /**
408      * Reads the font using a FontFileReader.
409      *
410      * @param in The FontFileReader to use
411      * @throws IOException In case of an I/O problem
412      */

413     public void readFont(FontFileReader in) throws IOException JavaDoc {
414         readFont(in, (String JavaDoc)null);
415     }
416
417     /**
418      * initialize the ansiWidths array (for winAnsiEncoding)
419      * and fill with the missingwidth
420      */

421     private void initAnsiWidths() {
422         ansiWidth = new int[256];
423         for (int i = 0; i < 256; i++) {
424             ansiWidth[i] = mtxTab[0].getWx();
425         }
426
427         // Create an index hash to the ansiWidth
428
// Can't just index the winAnsiEncoding when inserting widths
429
// same char (eg bullet) is repeated more than one place
430
ansiIndex = new java.util.HashMap JavaDoc();
431         for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
432             Integer JavaDoc ansi = new Integer JavaDoc(i);
433             Integer JavaDoc uni = new Integer JavaDoc((int)Glyphs.WINANSI_ENCODING[i]);
434
435             List JavaDoc v = (List JavaDoc)ansiIndex.get(uni);
436             if (v == null) {
437                 v = new java.util.ArrayList JavaDoc();
438                 ansiIndex.put(uni, v);
439             }
440             v.add(ansi);
441         }
442     }
443
444     /**
445      * Read the font data.
446      * If the fontfile is a TrueType Collection (.ttc file)
447      * the name of the font to read data for must be supplied,
448      * else the name is ignored.
449      *
450      * @param in The FontFileReader to use
451      * @param name The name of the font
452      * @return boolean Returns true if the font is valid
453      * @throws IOException In case of an I/O problem
454      */

455     public boolean readFont(FontFileReader in, String JavaDoc name) throws IOException JavaDoc {
456
457         /*
458          * Check if TrueType collection, and that the name
459          * exists in the collection
460          */

461         if (!checkTTC(in, name)) {
462             if (name == null) {
463                 throw new IllegalArgumentException JavaDoc(
464                     "For TrueType collection you must specify which font "
465                     + "to select (-ttcname)");
466             } else {
467                 throw new IOException JavaDoc(
468                     "Name does not exist in the TrueType collection: " + name);
469             }
470         }
471
472         readDirTabs(in);
473         readFontHeader(in);
474         getNumGlyphs(in);
475         log.info("Number of glyphs in font: " + numberOfGlyphs);
476         readHorizontalHeader(in);
477         readHorizontalMetrics(in);
478         initAnsiWidths();
479         readPostScript(in);
480         readOS2(in);
481         determineAscDesc();
482         readIndexToLocation(in);
483         readGlyf(in);
484         readName(in);
485         boolean pcltFound = readPCLT(in);
486         // Read cmap table and fill in ansiwidths
487
boolean valid = readCMAP(in);
488         if (!valid) {
489             return false;
490         }
491         // Create cmaps for bfentries
492
createCMaps();
493         // print_max_min();
494

495         readKerning(in);
496         guessVerticalMetricsFromGlyphBBox();
497         return true;
498     }
499
500     private void createCMaps() {
501         cmaps = new java.util.ArrayList JavaDoc();
502         TTFCmapEntry tce = new TTFCmapEntry();
503
504         Iterator JavaDoc e = unicodeMapping.listIterator();
505         UnicodeMapping um = (UnicodeMapping)e.next();
506         UnicodeMapping lastMapping = um;
507
508         tce.setUnicodeStart(um.getUnicodeIndex());
509         tce.setGlyphStartIndex(um.getGlyphIndex());
510
511         while (e.hasNext()) {
512             um = (UnicodeMapping)e.next();
513             if (((lastMapping.getUnicodeIndex() + 1) != um.getUnicodeIndex())
514                     || ((lastMapping.getGlyphIndex() + 1) != um.getGlyphIndex())) {
515                 tce.setUnicodeEnd(lastMapping.getUnicodeIndex());
516                 cmaps.add(tce);
517
518                 tce = new TTFCmapEntry();
519                 tce.setUnicodeStart(um.getUnicodeIndex());
520                 tce.setGlyphStartIndex(um.getGlyphIndex());
521             }
522             lastMapping = um;
523         }
524
525         tce.setUnicodeEnd(um.getUnicodeIndex());
526         cmaps.add(tce);
527     }
528
529     /**
530      * Returns the Windows name of the font.
531      * @return String The Windows name
532      */

533     public String JavaDoc getWindowsName() {
534         return familyName + "," + subFamilyName;
535     }
536
537     /**
538      * Returns the PostScript name of the font.
539      * @return String The PostScript name
540      */

541     public String JavaDoc getPostScriptName() {
542         if ("Regular".equals(subFamilyName) || "Roman".equals(subFamilyName)) {
543             return familyName;
544         } else {
545             return familyName + "," + subFamilyName;
546         }
547     }
548
549     /**
550      * Returns the font family name of the font.
551      * @return String The family name
552      */

553     public String JavaDoc getFamilyName() {
554         return familyName;
555     }
556
557     /**
558      * Returns the name of the character set used.
559      * @return String The caracter set
560      */

561     public String JavaDoc getCharSetName() {
562         return encoding;
563     }
564
565     /**
566      * Returns the CapHeight attribute of the font.
567      * @return int The CapHeight
568      */

569     public int getCapHeight() {
570         return (int)convertTTFUnit2PDFUnit(capHeight);
571     }
572
573     /**
574      * Returns the XHeight attribute of the font.
575      * @return int The XHeight
576      */

577     public int getXHeight() {
578         return (int)convertTTFUnit2PDFUnit(xHeight);
579     }
580
581     /**
582      * Returns the Flags attribute of the font.
583      * @return int The Flags
584      */

585     public int getFlags() {
586         int flags = 32; // Use Adobe Standard charset
587
if (italicAngle != 0) {
588             flags = flags | 64;
589         }
590         if (isFixedPitch != 0) {
591             flags = flags | 2;
592         }
593         if (hasSerifs) {
594             flags = flags | 1;
595         }
596         return flags;
597     }
598
599
600     /**
601      * Returns the StemV attribute of the font.
602      * @return String The StemV
603      */

604     public String JavaDoc getStemV() {
605         return "0";
606     }
607
608     /**
609      * Returns the ItalicAngle attribute of the font.
610      * @return String The ItalicAngle
611      */

612     public String JavaDoc getItalicAngle() {
613         String JavaDoc ia = Short.toString((short)(italicAngle / 0x10000));
614
615         // This is the correct italic angle, however only int italic
616
// angles are supported at the moment so this is commented out.
617
/*
618          * if ((italicAngle % 0x10000) > 0 )
619          * ia=ia+(comma+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000)));
620          */

621         return ia;
622     }
623
624     /**
625      * Returns the font bounding box.
626      * @return int[] The font bbox
627      */

628     public int[] getFontBBox() {
629         final int[] fbb = new int[4];
630         fbb[0] = (int)convertTTFUnit2PDFUnit(fontBBox1);
631         fbb[1] = (int)convertTTFUnit2PDFUnit(fontBBox2);
632         fbb[2] = (int)convertTTFUnit2PDFUnit(fontBBox3);
633         fbb[3] = (int)convertTTFUnit2PDFUnit(fontBBox4);
634
635         return fbb;
636     }
637
638     /**
639      * Returns the LowerCaseAscent attribute of the font.
640      * @return int The LowerCaseAscent
641      */

642     public int getLowerCaseAscent() {
643         return (int)convertTTFUnit2PDFUnit(ascender);
644     }
645
646     /**
647      * Returns the LowerCaseDescent attribute of the font.
648      * @return int The LowerCaseDescent
649      */

650     public int getLowerCaseDescent() {
651         return (int)convertTTFUnit2PDFUnit(descender);
652     }
653
654     /**
655      * Returns the index of the last character, but this is for WinAnsiEncoding
656      * only, so the last char is < 256.
657      * @return short Index of the last character (<256)
658      */

659     public short getLastChar() {
660         return lastChar;
661     }
662
663     /**
664      * Returns the index of the first character.
665      * @return short Index of the first character
666      */

667     public short getFirstChar() {
668         return firstChar;
669     }
670
671     /**
672      * Returns an array of character widths.
673      * @return int[] The character widths
674      */

675     public int[] getWidths() {
676         int[] wx = new int[mtxTab.length];
677         for (int i = 0; i < wx.length; i++) {
678             wx[i] = (int)convertTTFUnit2PDFUnit(mtxTab[i].getWx());
679         }
680
681         return wx;
682     }
683
684     /**
685      * Returns the width of a given character.
686      * @param idx Index of the character
687      * @return int Standard width
688      */

689     public int getCharWidth(int idx) {
690         return (int)convertTTFUnit2PDFUnit(ansiWidth[idx]);
691     }
692
693     /**
694      * Returns the kerning table.
695      * @return Map The kerning table
696      */

697     public Map JavaDoc getKerning() {
698         return kerningTab;
699     }
700
701     /**
702      * Returns the ANSI kerning table.
703      * @return Map The ANSI kerning table
704      */

705     public Map JavaDoc getAnsiKerning() {
706         return ansiKerningTab;
707     }
708
709     /**
710      * Indicates if the font may be embedded.
711      * @return boolean True if it may be embedded
712      */

713     public boolean isEmbeddable() {
714         return isEmbeddable;
715     }
716
717
718     /**
719      * Read Table Directory from the current position in the
720      * FontFileReader and fill the global HashMap dirTabs
721      * with the table name (String) as key and a TTFDirTabEntry
722      * as value.
723      * @param in FontFileReader to read the table directory from
724      * @throws IOException in case of an I/O problem
725      */

726     protected void readDirTabs(FontFileReader in) throws IOException JavaDoc {
727         in.skip(4); // TTF_FIXED_SIZE
728
int ntabs = in.readTTFUShort();
729         in.skip(6); // 3xTTF_USHORT_SIZE
730

731         dirTabs = new java.util.HashMap JavaDoc();
732         TTFDirTabEntry[] pd = new TTFDirTabEntry[ntabs];
733         log.debug("Reading " + ntabs + " dir tables");
734         for (int i = 0; i < ntabs; i++) {
735             pd[i] = new TTFDirTabEntry();
736             dirTabs.put(pd[i].read(in), pd[i]);
737         }
738         log.debug("dir tables: " + dirTabs.keySet());
739     }
740
741     /**
742      * Read the "head" table, this reads the bounding box and
743      * sets the upem (unitsPerEM) variable
744      * @param in FontFileReader to read the header from
745      * @throws IOException in case of an I/O problem
746      */

747     protected void readFontHeader(FontFileReader in) throws IOException JavaDoc {
748         seekTab(in, "head", 2 * 4 + 2 * 4 + 2);
749         upem = in.readTTFUShort();
750         log.debug("unit per em: " + upem);
751
752         in.skip(16);
753
754         fontBBox1 = in.readTTFShort();
755         fontBBox2 = in.readTTFShort();
756         fontBBox3 = in.readTTFShort();
757         fontBBox4 = in.readTTFShort();
758
759         in.skip(2 + 2 + 2);
760
761         locaFormat = in.readTTFShort();
762     }
763
764     /**
765      * Read the number of glyphs from the "maxp" table
766      * @param in FontFileReader to read the number of glyphs from
767      * @throws IOException in case of an I/O problem
768      */

769     protected void getNumGlyphs(FontFileReader in) throws IOException JavaDoc {
770         seekTab(in, "maxp", 4);
771         numberOfGlyphs = in.readTTFUShort();
772     }
773
774
775     /**
776      * Read the "hhea" table to find the ascender and descender and
777      * size of "hmtx" table, as a fixed size font might have only
778      * one width.
779      * @param in FontFileReader to read the hhea table from
780      * @throws IOException in case of an I/O problem
781      */

782     protected void readHorizontalHeader(FontFileReader in)
783             throws IOException JavaDoc {
784         seekTab(in, "hhea", 4);
785         hheaAscender = in.readTTFShort();
786         log.debug("hhea.Ascender: " + hheaAscender + " " + convertTTFUnit2PDFUnit(hheaAscender));
787         hheaDescender = in.readTTFShort();
788         log.debug("hhea.Descender: " + hheaDescender + " " + convertTTFUnit2PDFUnit(hheaDescender));
789         
790         in.skip(2 + 2 + 3 * 2 + 8 * 2);
791         nhmtx = in.readTTFUShort();
792         log.debug("Number of horizontal metrics: " + nhmtx);
793
794
795     }
796
797     /**
798      * Read "hmtx" table and put the horizontal metrics
799      * in the mtxTab array. If the number of metrics is less
800      * than the number of glyphs (eg fixed size fonts), extend
801      * the mtxTab array and fill in the missing widths
802      * @param in FontFileReader to read the hmtx table from
803      * @throws IOException in case of an I/O problem
804      */

805     protected void readHorizontalMetrics(FontFileReader in)
806             throws IOException JavaDoc {
807         seekTab(in, "hmtx", 0);
808
809         int mtxSize = Math.max(numberOfGlyphs, nhmtx);
810         mtxTab = new TTFMtxEntry[mtxSize];
811
812         if (TRACE_ENABLED) {
813             log.debug("*** Widths array: \n");
814         }
815         for (int i = 0; i < mtxSize; i++) {
816             mtxTab[i] = new TTFMtxEntry();
817         }
818         for (int i = 0; i < nhmtx; i++) {
819             mtxTab[i].setWx(in.readTTFUShort());
820             mtxTab[i].setLsb(in.readTTFUShort());
821
822             if (TRACE_ENABLED) {
823                 if (log.isDebugEnabled()) {
824                     log.debug(" width[" + i + "] = "
825                         + convertTTFUnit2PDFUnit(mtxTab[i].getWx()) + ";");
826                 }
827             }
828         }
829
830         if (nhmtx < mtxSize) {
831             // Fill in the missing widths
832
int lastWidth = mtxTab[nhmtx - 1].getWx();
833             for (int i = nhmtx; i < mtxSize; i++) {
834                 mtxTab[i].setWx(lastWidth);
835                 mtxTab[i].setLsb(in.readTTFUShort());
836             }
837         }
838     }
839
840
841     /**
842      * Read the "post" table
843      * containing the PostScript names of the glyphs.
844      */

845     private final void readPostScript(FontFileReader in) throws IOException JavaDoc {
846         seekTab(in, "post", 0);
847         postFormat = in.readTTFLong();
848         italicAngle = in.readTTFULong();
849         underlinePosition = in.readTTFShort();
850         underlineThickness = in.readTTFShort();
851         isFixedPitch = in.readTTFULong();
852
853         //Skip memory usage values
854
in.skip(4 * 4);
855
856         log.debug("PostScript format: 0x" + Integer.toHexString(postFormat));
857         switch (postFormat) {
858         case 0x00010000:
859             log.debug("PostScript format 1");
860             for (int i = 0; i < Glyphs.MAC_GLYPH_NAMES.length; i++) {
861                 mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[i]);
862             }
863             break;
864         case 0x00020000:
865             log.debug("PostScript format 2");
866             int numGlyphStrings = 0;
867
868             // Read Number of Glyphs
869
int l = in.readTTFUShort();
870
871             // Read indexes
872
for (int i = 0; i < l; i++) {
873                 mtxTab[i].setIndex(in.readTTFUShort());
874
875                 if (mtxTab[i].getIndex() > 257) {
876                     //Index is not in the Macintosh standard set
877
numGlyphStrings++;
878                 }
879
880                 if (log.isTraceEnabled()) {
881                     log.trace("PostScript index: " + mtxTab[i].getIndexAsString());
882                 }
883             }
884
885             // firstChar=minIndex;
886
String JavaDoc[] psGlyphsBuffer = new String JavaDoc[numGlyphStrings];
887             if (log.isDebugEnabled()) {
888                 log.debug("Reading " + numGlyphStrings
889                         + " glyphnames, that are not in the standard Macintosh"
890                         + " set. Total number of glyphs=" + l);
891             }
892             for (int i = 0; i < psGlyphsBuffer.length; i++) {
893                 psGlyphsBuffer[i] = in.readTTFString(in.readTTFUByte());
894             }
895
896             //Set glyph names
897
for (int i = 0; i < l; i++) {
898                 if (mtxTab[i].getIndex() < NMACGLYPHS) {
899                     mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[mtxTab[i].getIndex()]);
900                 } else {
901                     if (!mtxTab[i].isIndexReserved()) {
902                         int k = mtxTab[i].getIndex() - NMACGLYPHS;
903
904                         if (log.isTraceEnabled()) {
905                             log.trace(k + " i=" + i + " mtx=" + mtxTab.length
906                                 + " ps=" + psGlyphsBuffer.length);
907                         }
908
909                         mtxTab[i].setName(psGlyphsBuffer[k]);
910                     }
911                 }
912             }
913
914             break;
915         case 0x00030000:
916             // PostScript format 3 contains no glyph names
917
log.debug("PostScript format 3");
918             break;
919         default:
920             log.error("Unknown PostScript format: " + postFormat);
921         }
922     }
923
924
925     /**
926      * Read the "OS/2" table
927      */

928     private final void readOS2(FontFileReader in) throws IOException JavaDoc {
929         // Check if font is embeddable
930
if (dirTabs.get("OS/2") != null) {
931             seekTab(in, "OS/2", 2 * 4);
932             int fsType = in.readTTFUShort();
933             if (fsType == 2) {
934                 isEmbeddable = false;
935             } else {
936                 isEmbeddable = true;
937             }
938             in.skip(11 * 2);
939             in.skip(10); //panose array
940
in.skip(4 * 4); //unicode ranges
941
in.skip(4);
942             in.skip(3 * 2);
943             int v;
944             os2Ascender = in.readTTFShort(); //sTypoAscender
945
log.debug("sTypoAscender: " + os2Ascender
946                         + " " + convertTTFUnit2PDFUnit(os2Ascender));
947             os2Descender = in.readTTFShort(); //sTypoDescender
948
log.debug("sTypoDescender: " + os2Descender
949                         + " " + convertTTFUnit2PDFUnit(os2Descender));
950             v = in.readTTFShort(); //sTypoLineGap
951
log.debug("sTypoLineGap: " + v);
952             v = in.readTTFUShort(); //usWinAscent
953
log.debug("usWinAscent: " + v + " " + convertTTFUnit2PDFUnit(v));
954             v = in.readTTFUShort(); //usWinDescent
955
log.debug("usWinDescent: " + v + " " + convertTTFUnit2PDFUnit(v));
956             in.skip(2 * 4);
957             v = in.readTTFShort(); //sxHeight
958
log.debug("sxHeight: " + v);
959             v = in.readTTFShort(); //sCapHeight
960
log.debug("sCapHeight: " + v);
961             
962         } else {
963             isEmbeddable = true;
964         }
965     }
966
967     /**
968      * Read the "loca" table.
969      * @param in FontFileReader to read from
970      * @throws IOException In case of a I/O problem
971      */

972     protected final void readIndexToLocation(FontFileReader in)
973             throws IOException JavaDoc {
974         if (!seekTab(in, "loca", 0)) {
975             throw new IOException JavaDoc("'loca' table not found, happens when the font file doesn't"
976                     + " contain TrueType outlines (trying to read an OpenType CFF font maybe?)");
977         }
978         for (int i = 0; i < numberOfGlyphs; i++) {
979             mtxTab[i].setOffset(locaFormat == 1 ? in.readTTFULong()
980                                  : (in.readTTFUShort() << 1));
981         }
982         lastLoca = (locaFormat == 1 ? in.readTTFULong()
983                     : (in.readTTFUShort() << 1));
984     }
985
986     /**
987      * Read the "glyf" table to find the bounding boxes.
988      * @param in FontFileReader to read from
989      * @throws IOException In case of a I/O problem
990      */

991     private final void readGlyf(FontFileReader in) throws IOException JavaDoc {
992         TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("glyf");
993         if (dirTab == null) {
994             throw new IOException JavaDoc("glyf table not found, cannot continue");
995         }
996         for (int i = 0; i < (numberOfGlyphs - 1); i++) {
997             if (mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
998                 in.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
999                 in.skip(2);
1000                final int[] bbox = {
1001                    in.readTTFShort(),
1002                    in.readTTFShort(),
1003                    in.readTTFShort(),
1004                    in.readTTFShort()};
1005                mtxTab[i].setBoundingBox(bbox);
1006            } else {
1007                mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox());
1008            }
1009        }
1010
1011
1012        long n = ((TTFDirTabEntry)dirTabs.get("glyf")).getOffset();
1013        for (int i = 0; i < numberOfGlyphs; i++) {
1014            if ((i + 1) >= mtxTab.length
1015                    || mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
1016                in.seekSet(n + mtxTab[i].getOffset());
1017                in.skip(2);
1018                final int[] bbox = {
1019                    in.readTTFShort(),
1020                    in.readTTFShort(),
1021                    in.readTTFShort(),
1022                    in.readTTFShort()};
1023                mtxTab[i].setBoundingBox(bbox);
1024            } else {
1025                /**@todo Verify that this is correct, looks like a copy/paste bug (jm)*/
1026                final int bbox0 = mtxTab[0].getBoundingBox()[0];
1027                final int[] bbox = {bbox0, bbox0, bbox0, bbox0};
1028                mtxTab[i].setBoundingBox(bbox);
1029                /* Original code
1030                mtxTab[i].bbox[0] = mtxTab[0].bbox[0];
1031                mtxTab[i].bbox[1] = mtxTab[0].bbox[0];
1032                mtxTab[i].bbox[2] = mtxTab[0].bbox[0];
1033                mtxTab[i].bbox[3] = mtxTab[0].bbox[0]; */

1034            }
1035            if (log.isTraceEnabled()) {
1036                log.trace(mtxTab[i].toString(this));
1037            }
1038        }
1039    }
1040
1041    /**
1042     * Read the "name" table.
1043     * @param in FontFileReader to read from
1044     * @throws IOException In case of a I/O problem
1045     */

1046    private final void readName(FontFileReader in) throws IOException JavaDoc {
1047        seekTab(in, "name", 2);
1048        int i = in.getCurrentPos();
1049        int n = in.readTTFUShort();
1050        int j = in.readTTFUShort() + i - 2;
1051        i += 2 * 2;
1052
1053        while (n-- > 0) {
1054            // getLogger().debug("Iteration: " + n);
1055
in.seekSet(i);
1056            final int platformID = in.readTTFUShort();
1057            final int encodingID = in.readTTFUShort();
1058            final int languageID = in.readTTFUShort();
1059
1060            int k = in.readTTFUShort();
1061            int l = in.readTTFUShort();
1062
1063            if (((platformID == 1 || platformID == 3)
1064                    && (encodingID == 0 || encodingID == 1))
1065                    && (k == 1 || k == 2 || k == 0 || k == 4 || k == 6)) {
1066                in.seekSet(j + in.readTTFUShort());
1067                String JavaDoc txt = in.readTTFString(l);
1068                
1069                log.debug(platformID + " "
1070                    + encodingID + " "
1071                    + languageID + " "
1072                    + k + " " + txt);
1073                switch (k) {
1074                case 0:
1075                    notice = txt;
1076                    break;
1077                case 1:
1078                    familyName = txt;
1079                    break;
1080                case 2:
1081                    subFamilyName = txt;
1082                    break;
1083                case 4:
1084                    fullName = txt;
1085                    break;
1086                case 6:
1087                    fontName = txt;
1088                    break;
1089                }
1090                if (!notice.equals("")
1091                        && !fullName.equals("")
1092                        && !fontName.equals("")
1093                        && !familyName.equals("")
1094                        && !subFamilyName.equals("")) {
1095                    break;
1096                }
1097            }
1098            i += 6 * 2;
1099        }
1100    }
1101
1102    /**
1103     * Read the "PCLT" table to find xHeight and capHeight.
1104     * @param in FontFileReader to read from
1105     * @throws IOException In case of a I/O problem
1106     */

1107    private final boolean readPCLT(FontFileReader in) throws IOException JavaDoc {
1108        TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("PCLT");
1109        if (dirTab != null) {
1110            in.seekSet(dirTab.getOffset() + 4 + 4 + 2);
1111            xHeight = in.readTTFUShort();
1112            log.debug("xHeight from PCLT: " + xHeight
1113                            + " " + convertTTFUnit2PDFUnit(xHeight));
1114            in.skip(2 * 2);
1115            capHeight = in.readTTFUShort();
1116            log.debug("capHeight from PCLT: " + capHeight
1117                            + " " + convertTTFUnit2PDFUnit(capHeight));
1118            in.skip(2 + 16 + 8 + 6 + 1 + 1);
1119
1120            int serifStyle = in.readTTFUByte();
1121            serifStyle = serifStyle >> 6;
1122            serifStyle = serifStyle & 3;
1123            if (serifStyle == 1) {
1124                hasSerifs = false;
1125            } else {
1126                hasSerifs = true;
1127            }
1128            return true;
1129        } else {
1130            return false;
1131        }
1132    }
1133
1134    /**
1135     * Determines the right source for the ascender and descender values. The problem here is
1136     * that the interpretation of these values is not the same for every font. There doesn't seem
1137     * to be a uniform definition of an ascender and a descender. In some fonts
1138     * the hhea values are defined after the Apple interpretation, but not in every font. The
1139     * same problem is in the OS/2 table. FOP needs the ascender and descender to determine the
1140     * baseline so we need values which add up more or less to the "em box". However, due to
1141     * accent modifiers a character can grow beyond the em box.
1142     */

1143    private void determineAscDesc() {
1144        int hheaBoxHeight = hheaAscender - hheaDescender;
1145        int os2BoxHeight = os2Ascender - os2Descender;
1146        if (os2Ascender > 0 && os2BoxHeight <= upem) {
1147            ascender = os2Ascender;
1148            descender = os2Descender;
1149        } else if (hheaAscender > 0 && hheaBoxHeight <= upem) {
1150            ascender = hheaAscender;
1151            descender = hheaDescender;
1152        } else {
1153            if (os2Ascender > 0) {
1154                //Fall back to info from OS/2 if possible
1155
ascender = os2Ascender;
1156                descender = os2Descender;
1157            } else {
1158                ascender = hheaAscender;
1159                descender = hheaDescender;
1160            }
1161        }
1162
1163        log.debug("Font box height: " + (ascender - descender));
1164        if (ascender - descender > upem) {
1165            log.warn("Ascender and descender together are larger than the em box."
1166                    + " This could lead to a wrong baseline placement in Apache FOP.");
1167        }
1168    }
1169
1170    private void guessVerticalMetricsFromGlyphBBox() {
1171        // Approximate capHeight from height of "H"
1172
// It's most unlikely that a font misses the PCLT table
1173
// This also assumes that postscriptnames exists ("H")
1174
// Should look it up int the cmap (that wouldn't help
1175
// for charsets without H anyway...)
1176
// Same for xHeight with the letter "x"
1177
int localCapHeight = 0;
1178        int localXHeight = 0;
1179        int localAscender = 0;
1180        int localDescender = 0;
1181        for (int i = 0; i < mtxTab.length; i++) {
1182            if ("H".equals(mtxTab[i].getName())) {
1183                localCapHeight = mtxTab[i].getBoundingBox()[3];
1184            } else if ("x".equals(mtxTab[i].getName())) {
1185                localXHeight = mtxTab[i].getBoundingBox()[3];
1186            } else if ("d".equals(mtxTab[i].getName())) {
1187                localAscender = mtxTab[i].getBoundingBox()[3];
1188            } else if ("p".equals(mtxTab[i].getName())) {
1189                localDescender = mtxTab[i].getBoundingBox()[1];
1190            } else {
1191                // OpenType Fonts with a version 3.0 "post" table don't have glyph names.
1192
// Use Unicode indices instead.
1193
List JavaDoc unicodeIndex = mtxTab[i].getUnicodeIndex();
1194                if (unicodeIndex.size() > 0) {
1195                    //Only the first index is used
1196
char ch = (char)((Integer JavaDoc)unicodeIndex.get(0)).intValue();
1197                    if (ch == 'H') {
1198                        localCapHeight = mtxTab[i].getBoundingBox()[3];
1199                    } else if (ch == 'x') {
1200                        localXHeight = mtxTab[i].getBoundingBox()[3];
1201                    } else if (ch == 'd') {
1202                        localAscender = mtxTab[i].getBoundingBox()[3];
1203                    } else if (ch == 'p') {
1204                        localDescender = mtxTab[i].getBoundingBox()[1];
1205                    }
1206                }
1207            }
1208        }
1209        log.debug("Ascender from glyph 'd': " + localAscender
1210                + " " + convertTTFUnit2PDFUnit(localAscender));
1211        log.debug("Descender from glyph 'p': " + localDescender
1212                + " " + convertTTFUnit2PDFUnit(localDescender));
1213        if (ascender - descender > upem) {
1214            log.debug("Replacing specified ascender/descender with derived values to get values"
1215                    + " which fit in the em box.");
1216            ascender = localAscender;
1217            descender = localDescender;
1218        }
1219
1220        log.debug("xHeight from glyph 'x': " + localXHeight
1221                + " " + convertTTFUnit2PDFUnit(localXHeight));
1222        log.debug("CapHeight from glyph 'H': " + localCapHeight
1223                + " " + convertTTFUnit2PDFUnit(localCapHeight));
1224        if (capHeight == 0) {
1225            capHeight = localCapHeight;
1226            if (localCapHeight == 0) {
1227                log.warn("capHeight value could not be determined."
1228                        + " The font may not work as expected.");
1229            }
1230        }
1231        if (xHeight == 0) {
1232            xHeight = localXHeight;
1233            if (xHeight == 0) {
1234                log.warn("xHeight value could not be determined."
1235                        + " The font may not work as expected.");
1236            }
1237        }
1238    }
1239
1240    /**
1241     * Read the kerning table, create a table for both CIDs and
1242     * winAnsiEncoding.
1243     * @param in FontFileReader to read from
1244     * @throws IOException In case of a I/O problem
1245     */

1246    private final void readKerning(FontFileReader in) throws IOException JavaDoc {
1247        // Read kerning
1248
kerningTab = new java.util.HashMap JavaDoc();
1249        ansiKerningTab = new java.util.HashMap JavaDoc();
1250        TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("kern");
1251        if (dirTab != null) {
1252            seekTab(in, "kern", 2);
1253            for (int n = in.readTTFUShort(); n > 0; n--) {
1254                in.skip(2 * 2);
1255                int k = in.readTTFUShort();
1256                if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) {
1257                    return;
1258                }
1259                if ((k >> 8) != 0) {
1260                    continue;
1261                }
1262
1263                k = in.readTTFUShort();
1264                in.skip(3 * 2);
1265                while (k-- > 0) {
1266                    int i = in.readTTFUShort();
1267                    int j = in.readTTFUShort();
1268                    int kpx = in.readTTFShort();
1269                    if (kpx != 0) {
1270                        // CID kerning table entry, using unicode indexes
1271
final Integer JavaDoc iObj = glyphToUnicode(i);
1272                        final Integer JavaDoc u2 = glyphToUnicode(j);
1273                        if(iObj==null) {
1274                            // happens for many fonts (Ubuntu font set),
1275
// stray entries in the kerning table??
1276
log.warn("Unicode index (1) not found for glyph " + i);
1277                        } else if(u2==null) {
1278                            log.warn("Unicode index (2) not found for glyph " + i);
1279                        } else {
1280                            Map JavaDoc adjTab = (Map JavaDoc)kerningTab.get(iObj);
1281                            if (adjTab == null) {
1282                                adjTab = new java.util.HashMap JavaDoc();
1283                            }
1284                            adjTab.put(u2,new Integer JavaDoc((int)convertTTFUnit2PDFUnit(kpx)));
1285                            kerningTab.put(iObj, adjTab);
1286                        }
1287                    }
1288                }
1289            }
1290
1291            // Create winAnsiEncoded kerning table from kerningTab
1292
// (could probably be simplified, for now we remap back to CID indexes and then to winAnsi)
1293
Iterator JavaDoc ae = kerningTab.keySet().iterator();
1294            while (ae.hasNext()) {
1295                Integer JavaDoc unicodeKey1 = (Integer JavaDoc)ae.next();
1296                Integer JavaDoc cidKey1 = unicodeToGlyph(unicodeKey1.intValue());
1297                Map JavaDoc akpx = new java.util.HashMap JavaDoc();
1298                Map JavaDoc ckpx = (Map JavaDoc)kerningTab.get(unicodeKey1);
1299
1300                Iterator JavaDoc aee = ckpx.keySet().iterator();
1301                while (aee.hasNext()) {
1302                    Integer JavaDoc unicodeKey2 = (Integer JavaDoc)aee.next();
1303                    Integer JavaDoc cidKey2 = unicodeToGlyph(unicodeKey2.intValue());
1304                    Integer JavaDoc kern = (Integer JavaDoc)ckpx.get(unicodeKey2);
1305
1306                    Iterator JavaDoc uniMap = mtxTab[cidKey2.intValue()].getUnicodeIndex().listIterator();
1307                    while (uniMap.hasNext()) {
1308                        Integer JavaDoc unicodeKey = (Integer JavaDoc)uniMap.next();
1309                        Integer JavaDoc[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
1310                        for (int u = 0; u < ansiKeys.length; u++) {
1311                            akpx.put(ansiKeys[u], kern);
1312                        }
1313                    }
1314                }
1315
1316                if (akpx.size() > 0) {
1317                    Iterator JavaDoc uniMap = mtxTab[cidKey1.intValue()].getUnicodeIndex().listIterator();
1318                    while (uniMap.hasNext()) {
1319                        Integer JavaDoc unicodeKey = (Integer JavaDoc)uniMap.next();
1320                        Integer JavaDoc[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
1321                        for (int u = 0; u < ansiKeys.length; u++) {
1322                            ansiKerningTab.put(ansiKeys[u], akpx);
1323                        }
1324                    }
1325                }
1326            }
1327        }
1328    }
1329
1330    /**
1331     * Return a List with TTFCmapEntry.
1332     * @return A list of TTFCmapEntry objects
1333     */

1334    public List JavaDoc getCMaps() {
1335        return cmaps;
1336    }
1337
1338    /**
1339     * Check if this is a TrueType collection and that the given
1340     * name exists in the collection.
1341     * If it does, set offset in fontfile to the beginning of
1342     * the Table Directory for that font.
1343     * @param in FontFileReader to read from
1344     * @param name The name to check
1345     * @return True if not collection or font name present, false otherwise
1346     * @throws IOException In case of an I/O problem
1347     */

1348    protected final boolean checkTTC(FontFileReader in, String JavaDoc name) throws IOException JavaDoc {
1349        String JavaDoc tag = in.readTTFString(4);
1350
1351        if ("ttcf".equals(tag)) {
1352            // This is a TrueType Collection
1353
in.skip(4);
1354
1355            // Read directory offsets
1356
int numDirectories = (int)in.readTTFULong();
1357            // int numDirectories=in.readTTFUShort();
1358
long[] dirOffsets = new long[numDirectories];
1359            for (int i = 0; i < numDirectories; i++) {
1360                dirOffsets[i] = in.readTTFULong();
1361            }
1362
1363            log.info("This is a TrueType collection file with "
1364                                   + numDirectories + " fonts");
1365            log.info("Containing the following fonts: ");
1366            // Read all the directories and name tables to check
1367
// If the font exists - this is a bit ugly, but...
1368
boolean found = false;
1369
1370            // Iterate through all name tables even if font
1371
// Is found, just to show all the names
1372
long dirTabOffset = 0;
1373            for (int i = 0; (i < numDirectories); i++) {
1374                in.seekSet(dirOffsets[i]);
1375                readDirTabs(in);
1376
1377                readName(in);
1378
1379                if (fullName.equals(name)) {
1380                    found = true;
1381                    dirTabOffset = dirOffsets[i];
1382                    log.info(fullName + " <-- selected");
1383                } else {
1384                    log.info(fullName);
1385                }
1386
1387                // Reset names
1388
notice = "";
1389                fullName = "";
1390                familyName = "";
1391                fontName = "";
1392                subFamilyName = "";
1393            }
1394
1395            in.seekSet(dirTabOffset);
1396            return found;
1397        } else {
1398            in.seekSet(0);
1399            return true;
1400        }
1401    }
1402
1403    /*
1404     * Helper classes, they are not very efficient, but that really
1405     * doesn't matter...
1406     */

1407    private Integer JavaDoc[] unicodeToWinAnsi(int unicode) {
1408        List JavaDoc ret = new java.util.ArrayList JavaDoc();
1409        for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
1410            if (unicode == Glyphs.WINANSI_ENCODING[i]) {
1411                ret.add(new Integer JavaDoc(i));
1412            }
1413        }
1414        return (Integer JavaDoc[])ret.toArray(new Integer JavaDoc[0]);
1415    }
1416
1417    /**
1418     * Dumps a few informational values to System.out.
1419     */

1420    public void printStuff() {
1421        System.out.println("Font name: " + fontName);
1422        System.out.println("Full name: " + fullName);
1423        System.out.println("Family name: " + familyName);
1424        System.out.println("Subfamily name: " + subFamilyName);
1425        System.out.println("Notice: " + notice);
1426        System.out.println("xHeight: " + (int)convertTTFUnit2PDFUnit(xHeight));
1427        System.out.println("capheight: " + (int)convertTTFUnit2PDFUnit(capHeight));
1428
1429        int italic = (int)(italicAngle >> 16);
1430        System.out.println("Italic: " + italic);
1431        System.out.print("ItalicAngle: " + (short)(italicAngle / 0x10000));
1432        if ((italicAngle % 0x10000) > 0) {
1433            System.out.print("."
1434                             + (short)((italicAngle % 0x10000) * 1000)
1435                               / 0x10000);
1436        }
1437        System.out.println();
1438        System.out.println("Ascender: " + convertTTFUnit2PDFUnit(ascender));
1439        System.out.println("Descender: " + convertTTFUnit2PDFUnit(descender));
1440        System.out.println("FontBBox: [" + (int)convertTTFUnit2PDFUnit(fontBBox1)
1441                           + " " + (int)convertTTFUnit2PDFUnit(fontBBox2) + " "
1442                           + (int)convertTTFUnit2PDFUnit(fontBBox3) + " "
1443                           + (int)convertTTFUnit2PDFUnit(fontBBox4) + "]");
1444    }
1445
1446    /**
1447     * Map a glyph index to the corresponding unicode code point
1448     *
1449     * @param glyphIndex
1450     * @return unicode code point
1451     * @throws IOException if glyphIndex not found
1452     */

1453    private Integer JavaDoc glyphToUnicode(int glyphIndex) throws IOException JavaDoc {
1454        return (Integer JavaDoc) glyphToUnicodeMap.get(new Integer JavaDoc(glyphIndex));
1455    }
1456    
1457    /**
1458     * Map a unicode code point to the corresponding glyph index
1459     *
1460     * @param unicodeIndex unicode code point
1461     * @return glyph index
1462     * @throws IOException if unicodeIndex not found
1463     */

1464    private Integer JavaDoc unicodeToGlyph(int unicodeIndex) throws IOException JavaDoc {
1465        final Integer JavaDoc result =
1466            (Integer JavaDoc) unicodeToGlyphMap.get(new Integer JavaDoc(unicodeIndex));
1467        if (result == null) {
1468            throw new IOException JavaDoc(
1469                    "Glyph index not found for unicode value " + unicodeIndex);
1470        }
1471        return result;
1472    }
1473    
1474    /**
1475     * Static main method to get info about a TrueType font.
1476     * @param args The command line arguments
1477     */

1478    public static void main(String JavaDoc[] args) {
1479        try {
1480            TTFFile ttfFile = new TTFFile();
1481
1482            FontFileReader reader = new FontFileReader(args[0]);
1483
1484            String JavaDoc name = null;
1485            if (args.length >= 2) {
1486                name = args[1];
1487            }
1488
1489            ttfFile.readFont(reader, name);
1490            ttfFile.printStuff();
1491
1492        } catch (IOException JavaDoc ioe) {
1493            System.err.println("Problem reading font: " + ioe.toString());
1494            ioe.printStackTrace(System.err);
1495        }
1496    }
1497}
Popular Tags