KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lowagie > text > pdf > TrueTypeFont


1 /*
2  * $Id: TrueTypeFont.java 2423 2006-10-02 21:09:37Z xlv $
3  * $Name$
4  *
5  * Copyright 2001-2006 Paulo Soares
6  *
7  * The contents of this file are subject to the Mozilla Public License Version 1.1
8  * (the "License"); you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the License.
14  *
15  * The Original Code is 'iText, a free JAVA-PDF library'.
16  *
17  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
18  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
19  * All Rights Reserved.
20  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
21  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
22  *
23  * Contributor(s): all the names of the contributors are added in the source code
24  * where applicable.
25  *
26  * Alternatively, the contents of this file may be used under the terms of the
27  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
28  * provisions of LGPL are applicable instead of those above. If you wish to
29  * allow use of your version of this file only under the terms of the LGPL
30  * License and not to allow others to use your version of this file under
31  * the MPL, indicate your decision by deleting the provisions above and
32  * replace them with the notice and other provisions required by the LGPL.
33  * If you do not delete the provisions above, a recipient may use your version
34  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
35  *
36  * This library is free software; you can redistribute it and/or modify it
37  * under the terms of the MPL as stated above or under the terms of the GNU
38  * Library General Public License as published by the Free Software Foundation;
39  * either version 2 of the License, or any later version.
40  *
41  * This library is distributed in the hope that it will be useful, but WITHOUT
42  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
43  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
44  * details.
45  *
46  * If you didn't download this code from the following link, you should check if
47  * you aren't using an obsolete version:
48  * http://www.lowagie.com/iText/
49  */

50
51 package com.lowagie.text.pdf;
52
53 import java.io.File JavaDoc;
54 import java.io.IOException JavaDoc;
55 import java.util.ArrayList JavaDoc;
56 import java.util.HashMap JavaDoc;
57 import java.util.Iterator JavaDoc;
58 import java.util.Map JavaDoc;
59
60 import com.lowagie.text.DocumentException;
61 import com.lowagie.text.ExceptionConverter;
62 /** Reads a Truetype font
63  *
64  * @author Paulo Soares (psoares@consiste.pt)
65  */

66 class TrueTypeFont extends BaseFont {
67
68     /** The code pages possible for a True Type font.
69      */

70     static final String JavaDoc codePages[] = {
71         "1252 Latin 1",
72         "1250 Latin 2: Eastern Europe",
73         "1251 Cyrillic",
74         "1253 Greek",
75         "1254 Turkish",
76         "1255 Hebrew",
77         "1256 Arabic",
78         "1257 Windows Baltic",
79         "1258 Vietnamese",
80         null,
81         null,
82         null,
83         null,
84         null,
85         null,
86         null,
87         "874 Thai",
88         "932 JIS/Japan",
89         "936 Chinese: Simplified chars--PRC and Singapore",
90         "949 Korean Wansung",
91         "950 Chinese: Traditional chars--Taiwan and Hong Kong",
92         "1361 Korean Johab",
93         null,
94         null,
95         null,
96         null,
97         null,
98         null,
99         null,
100         "Macintosh Character Set (US Roman)",
101         "OEM Character Set",
102         "Symbol Character Set",
103         null,
104         null,
105         null,
106         null,
107         null,
108         null,
109         null,
110         null,
111         null,
112         null,
113         null,
114         null,
115         null,
116         null,
117         null,
118         null,
119         "869 IBM Greek",
120         "866 MS-DOS Russian",
121         "865 MS-DOS Nordic",
122         "864 Arabic",
123         "863 MS-DOS Canadian French",
124         "862 Hebrew",
125         "861 MS-DOS Icelandic",
126         "860 MS-DOS Portuguese",
127         "857 IBM Turkish",
128         "855 IBM Cyrillic; primarily Russian",
129         "852 Latin 2",
130         "775 MS-DOS Baltic",
131         "737 Greek; former 437 G",
132         "708 Arabic; ASMO 708",
133         "850 WE/Latin 1",
134         "437 US"};
135  
136     protected boolean justNames = false;
137     /** Contains the location of the several tables. The key is the name of
138      * the table and the value is an <CODE>int[2]</CODE> where position 0
139      * is the offset from the start of the file and position 1 is the length
140      * of the table.
141      */

142     protected HashMap JavaDoc tables;
143     /** The file in use.
144      */

145     protected RandomAccessFileOrArray rf;
146     /** The file name.
147      */

148     protected String JavaDoc fileName;
149     
150     protected boolean cff = false;
151     
152     protected int cffOffset;
153     
154     protected int cffLength;
155     
156     /** The offset from the start of the file to the table directory.
157      * It is 0 for TTF and may vary for TTC depending on the chosen font.
158      */

159     protected int directoryOffset;
160     /** The index for the TTC font. It is an empty <CODE>String</CODE> for a
161      * TTF file.
162      */

163     protected String JavaDoc ttcIndex;
164     /** The style modifier */
165     protected String JavaDoc style = "";
166     /** The content of table 'head'.
167      */

168     protected FontHeader head = new FontHeader();
169     /** The content of table 'hhea'.
170      */

171     protected HorizontalHeader hhea = new HorizontalHeader();
172     /** The content of table 'OS/2'.
173      */

174     protected WindowsMetrics os_2 = new WindowsMetrics();
175     /** The width of the glyphs. This is essentially the content of table
176      * 'hmtx' normalized to 1000 units.
177      */

178     protected int GlyphWidths[];
179     
180     protected int bboxes[][];
181     /** The map containing the code information for the table 'cmap', encoding 1.0.
182      * The key is the code and the value is an <CODE>int[2]</CODE> where position 0
183      * is the glyph number and position 1 is the glyph width normalized to 1000
184      * units.
185      */

186     protected HashMap JavaDoc cmap10;
187     /** The map containing the code information for the table 'cmap', encoding 3.1
188      * in Unicode.
189      * <P>
190      * The key is the code and the value is an <CODE>int</CODE>[2] where position 0
191      * is the glyph number and position 1 is the glyph width normalized to 1000
192      * units.
193      */

194     protected HashMap JavaDoc cmap31;
195     /** The map containing the kerning information. It represents the content of
196      * table 'kern'. The key is an <CODE>Integer</CODE> where the top 16 bits
197      * are the glyph number for the first character and the lower 16 bits are the
198      * glyph number for the second character. The value is the amount of kerning in
199      * normalized 1000 units as an <CODE>Integer</CODE>. This value is usually negative.
200      */

201     protected IntHashtable kerning = new IntHashtable();
202     /**
203      * The font name.
204      * This name is usually extracted from the table 'name' with
205      * the 'Name ID' 6.
206      */

207     protected String JavaDoc fontName;
208     
209     /** The full name of the font
210      */

211     protected String JavaDoc fullName[][];
212
213     /** The family name of the font
214      */

215     protected String JavaDoc familyName[][];
216     /** The italic angle. It is usually extracted from the 'post' table or in it's
217      * absence with the code:
218      * <P>
219      * <PRE>
220      * -Math.atan2(hhea.caretSlopeRun, hhea.caretSlopeRise) * 180 / Math.PI
221      * </PRE>
222      */

223     protected double italicAngle;
224     /** <CODE>true</CODE> if all the glyphs have the same width.
225      */

226     protected boolean isFixedPitch = false;
227     
228     /** The components of table 'head'.
229      */

230     protected static class FontHeader {
231         /** A variable. */
232         int flags;
233         /** A variable. */
234         int unitsPerEm;
235         /** A variable. */
236         short xMin;
237         /** A variable. */
238         short yMin;
239         /** A variable. */
240         short xMax;
241         /** A variable. */
242         short yMax;
243         /** A variable. */
244         int macStyle;
245     }
246     
247     /** The components of table 'hhea'.
248      */

249     protected static class HorizontalHeader {
250         /** A variable. */
251         short Ascender;
252         /** A variable. */
253         short Descender;
254         /** A variable. */
255         short LineGap;
256         /** A variable. */
257         int advanceWidthMax;
258         /** A variable. */
259         short minLeftSideBearing;
260         /** A variable. */
261         short minRightSideBearing;
262         /** A variable. */
263         short xMaxExtent;
264         /** A variable. */
265         short caretSlopeRise;
266         /** A variable. */
267         short caretSlopeRun;
268         /** A variable. */
269         int numberOfHMetrics;
270     }
271     
272     /** The components of table 'OS/2'.
273      */

274     protected static class WindowsMetrics {
275         /** A variable. */
276         short xAvgCharWidth;
277         /** A variable. */
278         int usWeightClass;
279         /** A variable. */
280         int usWidthClass;
281         /** A variable. */
282         short fsType;
283         /** A variable. */
284         short ySubscriptXSize;
285         /** A variable. */
286         short ySubscriptYSize;
287         /** A variable. */
288         short ySubscriptXOffset;
289         /** A variable. */
290         short ySubscriptYOffset;
291         /** A variable. */
292         short ySuperscriptXSize;
293         /** A variable. */
294         short ySuperscriptYSize;
295         /** A variable. */
296         short ySuperscriptXOffset;
297         /** A variable. */
298         short ySuperscriptYOffset;
299         /** A variable. */
300         short yStrikeoutSize;
301         /** A variable. */
302         short yStrikeoutPosition;
303         /** A variable. */
304         short sFamilyClass;
305         /** A variable. */
306         byte panose[] = new byte[10];
307         /** A variable. */
308         byte achVendID[] = new byte[4];
309         /** A variable. */
310         int fsSelection;
311         /** A variable. */
312         int usFirstCharIndex;
313         /** A variable. */
314         int usLastCharIndex;
315         /** A variable. */
316         short sTypoAscender;
317         /** A variable. */
318         short sTypoDescender;
319         /** A variable. */
320         short sTypoLineGap;
321         /** A variable. */
322         int usWinAscent;
323         /** A variable. */
324         int usWinDescent;
325         /** A variable. */
326         int ulCodePageRange1;
327         /** A variable. */
328         int ulCodePageRange2;
329         /** A variable. */
330         int sCapHeight;
331     }
332     
333     /** This constructor is present to allow extending the class.
334      */

335     protected TrueTypeFont() {
336     }
337     
338     TrueTypeFont(String JavaDoc ttFile, String JavaDoc enc, boolean emb, byte ttfAfm[]) throws DocumentException, IOException JavaDoc {
339         this(ttFile, enc, emb, ttfAfm, false);
340     }
341     
342     /** Creates a new TrueType font.
343      * @param ttFile the location of the font on file. The file must end in '.ttf' or
344      * '.ttc' but can have modifiers after the name
345      * @param enc the encoding to be applied to this font
346      * @param emb true if the font is to be embedded in the PDF
347      * @param ttfAfm the font as a <CODE>byte</CODE> array
348      * @throws DocumentException the font is invalid
349      * @throws IOException the font file could not be read
350      */

351     TrueTypeFont(String JavaDoc ttFile, String JavaDoc enc, boolean emb, byte ttfAfm[], boolean justNames) throws DocumentException, IOException JavaDoc {
352         this.justNames = justNames;
353         String JavaDoc nameBase = getBaseName(ttFile);
354         String JavaDoc ttcName = getTTCName(nameBase);
355         if (nameBase.length() < ttFile.length()) {
356             style = ttFile.substring(nameBase.length());
357         }
358         encoding = enc;
359         embedded = emb;
360         fileName = ttcName;
361         fontType = FONT_TYPE_TT;
362         ttcIndex = "";
363         if (ttcName.length() < nameBase.length())
364             ttcIndex = nameBase.substring(ttcName.length() + 1);
365         if (fileName.toLowerCase().endsWith(".ttf") || fileName.toLowerCase().endsWith(".otf") || fileName.toLowerCase().endsWith(".ttc")) {
366             process(ttfAfm);
367             if (!justNames && embedded && os_2.fsType == 2)
368                 throw new DocumentException(fileName + style + " cannot be embedded due to licensing restrictions.");
369         }
370         else
371             throw new DocumentException(fileName + style + " is not a TTF, OTF or TTC font file.");
372         if (!encoding.startsWith("#"))
373             PdfEncodings.convertToBytes(" ", enc); // check if the encoding exists
374
createEncoding();
375     }
376     
377     /** Gets the name from a composed TTC file name.
378      * If I have for input "myfont.ttc,2" the return will
379      * be "myfont.ttc".
380      * @param name the full name
381      * @return the simple file name
382      */

383     protected static String JavaDoc getTTCName(String JavaDoc name) {
384         int idx = name.toLowerCase().indexOf(".ttc,");
385         if (idx < 0)
386             return name;
387         else
388             return name.substring(0, idx + 4);
389     }
390     
391     
392     /**
393      * Reads the tables 'head', 'hhea', 'OS/2' and 'post' filling several variables.
394      * @throws DocumentException the font is invalid
395      * @throws IOException the font file could not be read
396      */

397     void fillTables() throws DocumentException, IOException JavaDoc {
398         int table_location[];
399         table_location = (int[])tables.get("head");
400         if (table_location == null)
401             throw new DocumentException("Table 'head' does not exist in " + fileName + style);
402         rf.seek(table_location[0] + 16);
403         head.flags = rf.readUnsignedShort();
404         head.unitsPerEm = rf.readUnsignedShort();
405         rf.skipBytes(16);
406         head.xMin = rf.readShort();
407         head.yMin = rf.readShort();
408         head.xMax = rf.readShort();
409         head.yMax = rf.readShort();
410         head.macStyle = rf.readUnsignedShort();
411         
412         table_location = (int[])tables.get("hhea");
413         if (table_location == null)
414             throw new DocumentException("Table 'hhea' does not exist " + fileName + style);
415         rf.seek(table_location[0] + 4);
416         hhea.Ascender = rf.readShort();
417         hhea.Descender = rf.readShort();
418         hhea.LineGap = rf.readShort();
419         hhea.advanceWidthMax = rf.readUnsignedShort();
420         hhea.minLeftSideBearing = rf.readShort();
421         hhea.minRightSideBearing = rf.readShort();
422         hhea.xMaxExtent = rf.readShort();
423         hhea.caretSlopeRise = rf.readShort();
424         hhea.caretSlopeRun = rf.readShort();
425         rf.skipBytes(12);
426         hhea.numberOfHMetrics = rf.readUnsignedShort();
427         
428         table_location = (int[])tables.get("OS/2");
429         if (table_location == null)
430             throw new DocumentException("Table 'OS/2' does not exist in " + fileName + style);
431         rf.seek(table_location[0]);
432         int version = rf.readUnsignedShort();
433         os_2.xAvgCharWidth = rf.readShort();
434         os_2.usWeightClass = rf.readUnsignedShort();
435         os_2.usWidthClass = rf.readUnsignedShort();
436         os_2.fsType = rf.readShort();
437         os_2.ySubscriptXSize = rf.readShort();
438         os_2.ySubscriptYSize = rf.readShort();
439         os_2.ySubscriptXOffset = rf.readShort();
440         os_2.ySubscriptYOffset = rf.readShort();
441         os_2.ySuperscriptXSize = rf.readShort();
442         os_2.ySuperscriptYSize = rf.readShort();
443         os_2.ySuperscriptXOffset = rf.readShort();
444         os_2.ySuperscriptYOffset = rf.readShort();
445         os_2.yStrikeoutSize = rf.readShort();
446         os_2.yStrikeoutPosition = rf.readShort();
447         os_2.sFamilyClass = rf.readShort();
448         rf.readFully(os_2.panose);
449         rf.skipBytes(16);
450         rf.readFully(os_2.achVendID);
451         os_2.fsSelection = rf.readUnsignedShort();
452         os_2.usFirstCharIndex = rf.readUnsignedShort();
453         os_2.usLastCharIndex = rf.readUnsignedShort();
454         os_2.sTypoAscender = rf.readShort();
455         os_2.sTypoDescender = rf.readShort();
456         if (os_2.sTypoDescender > 0)
457             os_2.sTypoDescender = (short)(-os_2.sTypoDescender);
458         os_2.sTypoLineGap = rf.readShort();
459         os_2.usWinAscent = rf.readUnsignedShort();
460         os_2.usWinDescent = rf.readUnsignedShort();
461         os_2.ulCodePageRange1 = 0;
462         os_2.ulCodePageRange2 = 0;
463         if (version > 0) {
464             os_2.ulCodePageRange1 = rf.readInt();
465             os_2.ulCodePageRange2 = rf.readInt();
466         }
467         if (version > 1) {
468             rf.skipBytes(2);
469             os_2.sCapHeight = rf.readShort();
470         }
471         else
472             os_2.sCapHeight = (int)(0.7 * head.unitsPerEm);
473         
474         table_location = (int[])tables.get("post");
475         if (table_location == null) {
476             italicAngle = -Math.atan2(hhea.caretSlopeRun, hhea.caretSlopeRise) * 180 / Math.PI;
477             return;
478         }
479         rf.seek(table_location[0] + 4);
480         short mantissa = rf.readShort();
481         int fraction = rf.readUnsignedShort();
482         italicAngle = (double)mantissa + (double)fraction / 16384.0;
483         rf.skipBytes(4);
484         isFixedPitch = rf.readInt() != 0;
485     }
486     
487     /**
488      * Gets the Postscript font name.
489      * @throws DocumentException the font is invalid
490      * @throws IOException the font file could not be read
491      * @return the Postscript font name
492      */

493     String JavaDoc getBaseFont() throws DocumentException, IOException JavaDoc {
494         int table_location[];
495         table_location = (int[])tables.get("name");
496         if (table_location == null)
497             throw new DocumentException("Table 'name' does not exist in " + fileName + style);
498         rf.seek(table_location[0] + 2);
499         int numRecords = rf.readUnsignedShort();
500         int startOfStorage = rf.readUnsignedShort();
501         for (int k = 0; k < numRecords; ++k) {
502             int platformID = rf.readUnsignedShort();
503             int platformEncodingID = rf.readUnsignedShort();
504             int languageID = rf.readUnsignedShort();
505             int nameID = rf.readUnsignedShort();
506             int length = rf.readUnsignedShort();
507             int offset = rf.readUnsignedShort();
508             if (nameID == 6) {
509                 rf.seek(table_location[0] + startOfStorage + offset);
510                 if (platformID == 0 || platformID == 3)
511                     return readUnicodeString(length);
512                 else
513                     return readStandardString(length);
514             }
515         }
516         File JavaDoc file = new File JavaDoc(fileName);
517         return file.getName().replace(' ', '-');
518     }
519     
520     /** Extracts the names of the font in all the languages available.
521      * @param id the name id to retrieve
522      * @throws DocumentException on error
523      * @throws IOException on error
524      */

525     String JavaDoc[][] getNames(int id) throws DocumentException, IOException JavaDoc {
526         int table_location[];
527         table_location = (int[])tables.get("name");
528         if (table_location == null)
529             throw new DocumentException("Table 'name' does not exist in " + fileName + style);
530         rf.seek(table_location[0] + 2);
531         int numRecords = rf.readUnsignedShort();
532         int startOfStorage = rf.readUnsignedShort();
533         ArrayList JavaDoc names = new ArrayList JavaDoc();
534         for (int k = 0; k < numRecords; ++k) {
535             int platformID = rf.readUnsignedShort();
536             int platformEncodingID = rf.readUnsignedShort();
537             int languageID = rf.readUnsignedShort();
538             int nameID = rf.readUnsignedShort();
539             int length = rf.readUnsignedShort();
540             int offset = rf.readUnsignedShort();
541             if (nameID == id) {
542                 int pos = rf.getFilePointer();
543                 rf.seek(table_location[0] + startOfStorage + offset);
544                 String JavaDoc name;
545                 if (platformID == 0 || platformID == 3 || (platformID == 2 && platformEncodingID == 1)){
546                     name = readUnicodeString(length);
547                 }
548                 else {
549                     name = readStandardString(length);
550                 }
551                 names.add(new String JavaDoc[]{String.valueOf(platformID),
552                     String.valueOf(platformEncodingID), String.valueOf(languageID), name});
553                 rf.seek(pos);
554             }
555         }
556         String JavaDoc thisName[][] = new String JavaDoc[names.size()][];
557         for (int k = 0; k < names.size(); ++k)
558             thisName[k] = (String JavaDoc[])names.get(k);
559         return thisName;
560     }
561     
562     void checkCff() {
563         int table_location[];
564         table_location = (int[])tables.get("CFF ");
565         if (table_location != null) {
566             cff = true;
567             cffOffset = table_location[0];
568             cffLength = table_location[1];
569         }
570     }
571
572     /** Reads the font data.
573      * @param ttfAfm the font as a <CODE>byte</CODE> array, possibly <CODE>null</CODE>
574      * @throws DocumentException the font is invalid
575      * @throws IOException the font file could not be read
576      */

577     void process(byte ttfAfm[]) throws DocumentException, IOException JavaDoc {
578         tables = new HashMap JavaDoc();
579         
580         try {
581             if (ttfAfm == null)
582                 rf = new RandomAccessFileOrArray(fileName);
583             else
584                 rf = new RandomAccessFileOrArray(ttfAfm);
585             if (ttcIndex.length() > 0) {
586                 int dirIdx = Integer.parseInt(ttcIndex);
587                 if (dirIdx < 0)
588                     throw new DocumentException("The font index for " + fileName + " must be positive.");
589                 String JavaDoc mainTag = readStandardString(4);
590                 if (!mainTag.equals("ttcf"))
591                     throw new DocumentException(fileName + " is not a valid TTC file.");
592                 rf.skipBytes(4);
593                 int dirCount = rf.readInt();
594                 if (dirIdx >= dirCount)
595                     throw new DocumentException("The font index for " + fileName + " must be between 0 and " + (dirCount - 1) + ". It was " + dirIdx + ".");
596                 rf.skipBytes(dirIdx * 4);
597                 directoryOffset = rf.readInt();
598             }
599             rf.seek(directoryOffset);
600             int ttId = rf.readInt();
601             if (ttId != 0x00010000 && ttId != 0x4F54544F)
602                 throw new DocumentException(fileName + " is not a valid TTF or OTF file.");
603             int num_tables = rf.readUnsignedShort();
604             rf.skipBytes(6);
605             for (int k = 0; k < num_tables; ++k) {
606                 String JavaDoc tag = readStandardString(4);
607                 rf.skipBytes(4);
608                 int table_location[] = new int[2];
609                 table_location[0] = rf.readInt();
610                 table_location[1] = rf.readInt();
611                 tables.put(tag, table_location);
612             }
613             checkCff();
614             fontName = getBaseFont();
615             fullName = getNames(4); //full name
616
familyName = getNames(1); //family name
617
if (!justNames) {
618                 fillTables();
619                 readGlyphWidths();
620                 readCMaps();
621                 readKerning();
622                 readBbox();
623                 GlyphWidths = null;
624             }
625         }
626         finally {
627             if (rf != null) {
628                 rf.close();
629                 if (!embedded)
630                     rf = null;
631             }
632         }
633     }
634     
635     /** Reads a <CODE>String</CODE> from the font file as bytes using the Cp1252
636      * encoding.
637      * @param length the length of bytes to read
638      * @return the <CODE>String</CODE> read
639      * @throws IOException the font file could not be read
640      */

641     protected String JavaDoc readStandardString(int length) throws IOException JavaDoc {
642         byte buf[] = new byte[length];
643         rf.readFully(buf);
644         try {
645             return new String JavaDoc(buf, WINANSI);
646         }
647         catch (Exception JavaDoc e) {
648             throw new ExceptionConverter(e);
649         }
650     }
651     
652     /** Reads a Unicode <CODE>String</CODE> from the font file. Each character is
653      * represented by two bytes.
654      * @param length the length of bytes to read. The <CODE>String</CODE> will have <CODE>length</CODE>/2
655      * characters
656      * @return the <CODE>String</CODE> read
657      * @throws IOException the font file could not be read
658      */

659     protected String JavaDoc readUnicodeString(int length) throws IOException JavaDoc {
660         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
661         length /= 2;
662         for (int k = 0; k < length; ++k) {
663             buf.append(rf.readChar());
664         }
665         return buf.toString();
666     }
667     
668     /** Reads the glyphs widths. The widths are extracted from the table 'hmtx'.
669      * The glyphs are normalized to 1000 units.
670      * @throws DocumentException the font is invalid
671      * @throws IOException the font file could not be read
672      */

673     protected void readGlyphWidths() throws DocumentException, IOException JavaDoc {
674         int table_location[];
675         table_location = (int[])tables.get("hmtx");
676         if (table_location == null)
677             throw new DocumentException("Table 'hmtx' does not exist in " + fileName + style);
678         rf.seek(table_location[0]);
679         GlyphWidths = new int[hhea.numberOfHMetrics];
680         for (int k = 0; k < hhea.numberOfHMetrics; ++k) {
681             GlyphWidths[k] = (rf.readUnsignedShort() * 1000) / head.unitsPerEm;
682             rf.readUnsignedShort();
683         }
684     }
685     
686     /** Gets a glyph width.
687      * @param glyph the glyph to get the width of
688      * @return the width of the glyph in normalized 1000 units
689      */

690     protected int getGlyphWidth(int glyph) {
691         if (glyph >= GlyphWidths.length)
692             glyph = GlyphWidths.length - 1;
693         return GlyphWidths[glyph];
694     }
695     
696     private void readBbox() throws DocumentException, IOException JavaDoc {
697         int tableLocation[];
698         tableLocation = (int[])tables.get("head");
699         if (tableLocation == null)
700             throw new DocumentException("Table 'head' does not exist in " + fileName + style);
701         rf.seek(tableLocation[0] + TrueTypeFontSubSet.HEAD_LOCA_FORMAT_OFFSET);
702         boolean locaShortTable = (rf.readUnsignedShort() == 0);
703         tableLocation = (int[])tables.get("loca");
704         if (tableLocation == null)
705             return;
706         rf.seek(tableLocation[0]);
707         int locaTable[];
708         if (locaShortTable) {
709             int entries = tableLocation[1] / 2;
710             locaTable = new int[entries];
711             for (int k = 0; k < entries; ++k)
712                 locaTable[k] = rf.readUnsignedShort() * 2;
713         }
714         else {
715             int entries = tableLocation[1] / 4;
716             locaTable = new int[entries];
717             for (int k = 0; k < entries; ++k)
718                 locaTable[k] = rf.readInt();
719         }
720         tableLocation = (int[])tables.get("glyf");
721         if (tableLocation == null)
722             throw new DocumentException("Table 'glyf' does not exist in " + fileName + style);
723         int tableGlyphOffset = tableLocation[0];
724         bboxes = new int[locaTable.length - 1][];
725         for (int glyph = 0; glyph < locaTable.length - 1; ++glyph) {
726             int start = locaTable[glyph];
727             if (start != locaTable[glyph + 1]) {
728                 rf.seek(tableGlyphOffset + start + 2);
729                 bboxes[glyph] = new int[]{
730                     (rf.readShort() * 1000) / head.unitsPerEm,
731                     (rf.readShort() * 1000) / head.unitsPerEm,
732                     (rf.readShort() * 1000) / head.unitsPerEm,
733                     (rf.readShort() * 1000) / head.unitsPerEm};
734             }
735         }
736     }
737     
738     /** Reads the several maps from the table 'cmap'. The maps of interest are 1.0 for symbolic
739      * fonts and 3.1 for all others. A symbolic font is defined as having the map 3.0.
740      * @throws DocumentException the font is invalid
741      * @throws IOException the font file could not be read
742      */

743     void readCMaps() throws DocumentException, IOException JavaDoc {
744         int table_location[];
745         table_location = (int[])tables.get("cmap");
746         if (table_location == null)
747             throw new DocumentException("Table 'cmap' does not exist in " + fileName + style);
748         rf.seek(table_location[0]);
749         rf.skipBytes(2);
750         int num_tables = rf.readUnsignedShort();
751         fontSpecific = false;
752         int map10 = 0;
753         int map31 = 0;
754         int map30 = 0;
755         for (int k = 0; k < num_tables; ++k) {
756             int platId = rf.readUnsignedShort();
757             int platSpecId = rf.readUnsignedShort();
758             int offset = rf.readInt();
759             if (platId == 3 && platSpecId == 0) {
760                 fontSpecific = true;
761                 map30 = offset;
762             }
763             else if (platId == 3 && platSpecId == 1) {
764                 map31 = offset;
765             }
766             if (platId == 1 && platSpecId == 0) {
767                 map10 = offset;
768             }
769         }
770         if (map10 > 0) {
771             rf.seek(table_location[0] + map10);
772             int format = rf.readUnsignedShort();
773             switch (format) {
774                 case 0:
775                     cmap10 = readFormat0();
776                     break;
777                 case 4:
778                     cmap10 = readFormat4();
779                     break;
780                 case 6:
781                     cmap10 = readFormat6();
782                     break;
783             }
784         }
785         if (map31 > 0) {
786             rf.seek(table_location[0] + map31);
787             int format = rf.readUnsignedShort();
788             if (format == 4) {
789                 cmap31 = readFormat4();
790             }
791         }
792         if (map30 > 0) {
793             rf.seek(table_location[0] + map30);
794             int format = rf.readUnsignedShort();
795             if (format == 4) {
796                 cmap10 = readFormat4();
797             }
798         }
799     }
800     
801     /** The information in the maps of the table 'cmap' is coded in several formats.
802      * Format 0 is the Apple standard character to glyph index mapping table.
803      * @return a <CODE>HashMap</CODE> representing this map
804      * @throws IOException the font file could not be read
805      */

806     HashMap JavaDoc readFormat0() throws IOException JavaDoc {
807         HashMap JavaDoc h = new HashMap JavaDoc();
808         rf.skipBytes(4);
809         for (int k = 0; k < 256; ++k) {
810             int r[] = new int[2];
811             r[0] = rf.readUnsignedByte();
812             r[1] = getGlyphWidth(r[0]);
813             h.put(new Integer JavaDoc(k), r);
814         }
815         return h;
816     }
817     
818     /** The information in the maps of the table 'cmap' is coded in several formats.
819      * Format 4 is the Microsoft standard character to glyph index mapping table.
820      * @return a <CODE>HashMap</CODE> representing this map
821      * @throws IOException the font file could not be read
822      */

823     HashMap JavaDoc readFormat4() throws IOException JavaDoc {
824         HashMap JavaDoc h = new HashMap JavaDoc();
825         int table_lenght = rf.readUnsignedShort();
826         rf.skipBytes(2);
827         int segCount = rf.readUnsignedShort() / 2;
828         rf.skipBytes(6);
829         int endCount[] = new int[segCount];
830         for (int k = 0; k < segCount; ++k) {
831             endCount[k] = rf.readUnsignedShort();
832         }
833         rf.skipBytes(2);
834         int startCount[] = new int[segCount];
835         for (int k = 0; k < segCount; ++k) {
836             startCount[k] = rf.readUnsignedShort();
837         }
838         int idDelta[] = new int[segCount];
839         for (int k = 0; k < segCount; ++k) {
840             idDelta[k] = rf.readUnsignedShort();
841         }
842         int idRO[] = new int[segCount];
843         for (int k = 0; k < segCount; ++k) {
844             idRO[k] = rf.readUnsignedShort();
845         }
846         int glyphId[] = new int[table_lenght / 2 - 8 - segCount * 4];
847         for (int k = 0; k < glyphId.length; ++k) {
848             glyphId[k] = rf.readUnsignedShort();
849         }
850         for (int k = 0; k < segCount; ++k) {
851             int glyph;
852             for (int j = startCount[k]; j <= endCount[k] && j != 0xFFFF; ++j) {
853                 if (idRO[k] == 0) {
854                     glyph = (j + idDelta[k]) & 0xFFFF;
855                 }
856                 else {
857                     int idx = k + idRO[k] / 2 - segCount + j - startCount[k];
858                     if (idx >= glyphId.length)
859                         continue;
860                     glyph = (glyphId[idx] + idDelta[k]) & 0xFFFF;
861                 }
862                 int r[] = new int[2];
863                 r[0] = glyph;
864                 r[1] = getGlyphWidth(r[0]);
865                 h.put(new Integer JavaDoc(fontSpecific ? ((j & 0xff00) == 0xf000 ? j & 0xff : j) : j), r);
866             }
867         }
868         return h;
869     }
870     
871     /** The information in the maps of the table 'cmap' is coded in several formats.
872      * Format 6 is a trimmed table mapping. It is similar to format 0 but can have
873      * less than 256 entries.
874      * @return a <CODE>HashMap</CODE> representing this map
875      * @throws IOException the font file could not be read
876      */

877     HashMap JavaDoc readFormat6() throws IOException JavaDoc {
878         HashMap JavaDoc h = new HashMap JavaDoc();
879         rf.skipBytes(4);
880         int start_code = rf.readUnsignedShort();
881         int code_count = rf.readUnsignedShort();
882         for (int k = 0; k < code_count; ++k) {
883             int r[] = new int[2];
884             r[0] = rf.readUnsignedShort();
885             r[1] = getGlyphWidth(r[0]);
886             h.put(new Integer JavaDoc(k + start_code), r);
887         }
888         return h;
889     }
890     
891     /** Reads the kerning information from the 'kern' table.
892      * @throws IOException the font file could not be read
893      */

894     void readKerning() throws IOException JavaDoc {
895         int table_location[];
896         table_location = (int[])tables.get("kern");
897         if (table_location == null)
898             return;
899         rf.seek(table_location[0] + 2);
900         int nTables = rf.readUnsignedShort();
901         int checkpoint = table_location[0] + 4;
902         int length = 0;
903         for (int k = 0; k < nTables; ++k) {
904             checkpoint += length;
905             rf.seek(checkpoint);
906             rf.skipBytes(2);
907             length = rf.readUnsignedShort();
908             int coverage = rf.readUnsignedShort();
909             if ((coverage & 0xfff7) == 0x0001) {
910                 int nPairs = rf.readUnsignedShort();
911                 rf.skipBytes(6);
912                 for (int j = 0; j < nPairs; ++j) {
913                     int pair = rf.readInt();
914                     int value = ((int)rf.readShort() * 1000) / head.unitsPerEm;
915                     kerning.put(pair, value);
916                 }
917             }
918         }
919     }
920     
921     /** Gets the kerning between two Unicode chars.
922      * @param char1 the first char
923      * @param char2 the second char
924      * @return the kerning to be applied
925      */

926     public int getKerning(char char1, char char2) {
927         int metrics[] = getMetricsTT(char1);
928         if (metrics == null)
929             return 0;
930         int c1 = metrics[0];
931         metrics = getMetricsTT(char2);
932         if (metrics == null)
933             return 0;
934         int c2 = metrics[0];
935         return kerning.get((c1 << 16) + c2);
936     }
937     
938     /** Gets the width from the font according to the unicode char <CODE>c</CODE>.
939      * If the <CODE>name</CODE> is null it's a symbolic font.
940      * @param c the unicode char
941      * @param name the glyph name
942      * @return the width of the char
943      */

944     int getRawWidth(int c, String JavaDoc name) {
945         HashMap JavaDoc map = null;
946         if (name == null || cmap31 == null)
947             map = cmap10;
948         else
949             map = cmap31;
950         if (map == null)
951             return 0;
952         int metric[] = (int[])map.get(new Integer JavaDoc(c));
953         if (metric == null)
954             return 0;
955         return metric[1];
956     }
957     
958     /** Generates the font descriptor for this font.
959      * @return the PdfDictionary containing the font descriptor or <CODE>null</CODE>
960      * @param subsetPrefix the subset prefix
961      * @param fontStream the indirect reference to a PdfStream containing the font or <CODE>null</CODE>
962      * @throws DocumentException if there is an error
963      */

964     protected PdfDictionary getFontDescriptor(PdfIndirectReference fontStream, String JavaDoc subsetPrefix) {
965         PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR);
966         dic.put(PdfName.ASCENT, new PdfNumber((int)os_2.sTypoAscender * 1000 / head.unitsPerEm));
967         dic.put(PdfName.CAPHEIGHT, new PdfNumber((int)os_2.sCapHeight * 1000 / head.unitsPerEm));
968         dic.put(PdfName.DESCENT, new PdfNumber((int)os_2.sTypoDescender * 1000 / head.unitsPerEm));
969         dic.put(PdfName.FONTBBOX, new PdfRectangle(
970         (int)head.xMin * 1000 / head.unitsPerEm,
971         (int)head.yMin * 1000 / head.unitsPerEm,
972         (int)head.xMax * 1000 / head.unitsPerEm,
973         (int)head.yMax * 1000 / head.unitsPerEm));
974         if (cff) {
975             if (encoding.startsWith("Identity-"))
976                 dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName+"-"+encoding));
977             else
978                 dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style));
979         }
980         else
981             dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style));
982         dic.put(PdfName.ITALICANGLE, new PdfNumber(italicAngle));
983         dic.put(PdfName.STEMV, new PdfNumber(80));
984         if (fontStream != null) {
985             if (cff)
986                 dic.put(PdfName.FONTFILE3, fontStream);
987             else
988                 dic.put(PdfName.FONTFILE2, fontStream);
989         }
990         int flags = 0;
991         if (isFixedPitch)
992             flags |= 1;
993         flags |= fontSpecific ? 4 : 32;
994         if ((head.macStyle & 2) != 0)
995             flags |= 64;
996         if ((head.macStyle & 1) != 0)
997             flags |= 262144;
998         dic.put(PdfName.FLAGS, new PdfNumber(flags));
999         
1000        return dic;
1001    }
1002    
1003    /** Generates the font dictionary for this font.
1004     * @return the PdfDictionary containing the font dictionary
1005     * @param subsetPrefix the subset prefx
1006     * @param firstChar the first valid character
1007     * @param lastChar the last valid character
1008     * @param shortTag a 256 bytes long <CODE>byte</CODE> array where each unused byte is represented by 0
1009     * @param fontDescriptor the indirect reference to a PdfDictionary containing the font descriptor or <CODE>null</CODE>
1010     * @throws DocumentException if there is an error
1011     */

1012    protected PdfDictionary getFontBaseType(PdfIndirectReference fontDescriptor, String JavaDoc subsetPrefix, int firstChar, int lastChar, byte shortTag[]) {
1013        PdfDictionary dic = new PdfDictionary(PdfName.FONT);
1014        if (cff) {
1015            dic.put(PdfName.SUBTYPE, PdfName.TYPE1);
1016            dic.put(PdfName.BASEFONT, new PdfName(fontName + style));
1017        }
1018        else {
1019            dic.put(PdfName.SUBTYPE, PdfName.TRUETYPE);
1020            dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style));
1021        }
1022        dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style));
1023        if (!fontSpecific) {
1024            for (int k = firstChar; k <= lastChar; ++k) {
1025                if (!differences[k].equals(notdef)) {
1026                    firstChar = k;
1027                    break;
1028                }
1029            }
1030        if (encoding.equals("Cp1252") || encoding.equals("MacRoman"))
1031                dic.put(PdfName.ENCODING, encoding.equals("Cp1252") ? PdfName.WIN_ANSI_ENCODING : PdfName.MAC_ROMAN_ENCODING);
1032            else {
1033                PdfDictionary enc = new PdfDictionary(PdfName.ENCODING);
1034                PdfArray dif = new PdfArray();
1035                boolean gap = true;
1036                for (int k = firstChar; k <= lastChar; ++k) {
1037                    if (shortTag[k] != 0) {
1038                        if (gap) {
1039                            dif.add(new PdfNumber(k));
1040                            gap = false;
1041                        }
1042                        dif.add(new PdfName(differences[k]));
1043                    }
1044                    else
1045                        gap = true;
1046                }
1047                enc.put(PdfName.DIFFERENCES, dif);
1048                dic.put(PdfName.ENCODING, enc);
1049            }
1050        }
1051        dic.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar));
1052        dic.put(PdfName.LASTCHAR, new PdfNumber(lastChar));
1053        PdfArray wd = new PdfArray();
1054        for (int k = firstChar; k <= lastChar; ++k) {
1055            if (shortTag[k] == 0)
1056                wd.add(new PdfNumber(0));
1057            else
1058                wd.add(new PdfNumber(widths[k]));
1059        }
1060        dic.put(PdfName.WIDTHS, wd);
1061        if (fontDescriptor != null)
1062            dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor);
1063        return dic;
1064    }
1065    
1066    protected byte[] getFullFont() throws IOException JavaDoc {
1067        RandomAccessFileOrArray rf2 = null;
1068        try {
1069            rf2 = new RandomAccessFileOrArray(rf);
1070            rf2.reOpen();
1071            byte b[] = new byte[rf2.length()];
1072            rf2.readFully(b);
1073            return b;
1074        }
1075        finally {
1076            try {if (rf2 != null) {rf2.close();}} catch (Exception JavaDoc e) {}
1077        }
1078    }
1079    
1080    protected static int[] compactRanges(ArrayList JavaDoc ranges) {
1081        ArrayList JavaDoc simp = new ArrayList JavaDoc();
1082        for (int k = 0; k < ranges.size(); ++k) {
1083            int[] r = (int[])ranges.get(k);
1084            for (int j = 0; j < r.length; j += 2) {
1085                simp.add(new int[]{Math.max(0, Math.min(r[j], r[j + 1])), Math.min(0xffff, Math.max(r[j], r[j + 1]))});
1086            }
1087        }
1088        for (int k1 = 0; k1 < simp.size() - 1; ++k1) {
1089            for (int k2 = k1 + 1; k2 < simp.size(); ++k2) {
1090                int[] r1 = (int[])simp.get(k1);
1091                int[] r2 = (int[])simp.get(k2);
1092                if ((r1[0] >= r2[0] && r1[0] <= r2[1]) || (r1[1] >= r2[0] && r1[0] <= r2[1])) {
1093                    r1[0] = Math.min(r1[0], r2[0]);
1094                    r1[1] = Math.max(r1[1], r2[1]);
1095                    simp.remove(k2);
1096                    --k2;
1097                }
1098            }
1099        }
1100        int[] s = new int[simp.size() * 2];
1101        for (int k = 0; k < simp.size(); ++k) {
1102            int[] r = (int[])simp.get(k);
1103            s[k * 2] = r[0];
1104            s[k * 2 + 1] = r[1];
1105        }
1106        return s;
1107    }
1108    
1109    protected void addRangeUni(HashMap JavaDoc longTag, boolean includeMetrics, boolean subsetp) {
1110        if (!subsetp && (subsetRanges != null || directoryOffset > 0)) {
1111            int[] rg = (subsetRanges == null && directoryOffset > 0) ? new int[]{0, 0xffff} : compactRanges(subsetRanges);
1112            HashMap JavaDoc usemap;
1113            if (!fontSpecific && cmap31 != null)
1114                usemap = cmap31;
1115            else if (fontSpecific && cmap10 != null)
1116                usemap = cmap10;
1117            else if (cmap31 != null)
1118                usemap = cmap31;
1119            else
1120                usemap = cmap10;
1121            for (Iterator JavaDoc it = usemap.entrySet().iterator(); it.hasNext();) {
1122                Map.Entry JavaDoc e = (Map.Entry JavaDoc)it.next();
1123                int[] v = (int[])e.getValue();
1124                Integer JavaDoc gi = new Integer JavaDoc(v[0]);
1125                if (longTag.containsKey(gi))
1126                    continue;
1127                int c = ((Integer JavaDoc)e.getKey()).intValue();
1128                boolean skip = true;
1129                for (int k = 0; k < rg.length; k += 2) {
1130                    if (c >= rg[k] && c <= rg[k + 1]) {
1131                        skip = false;
1132                        break;
1133                    }
1134                }
1135                if (!skip)
1136                    longTag.put(gi, includeMetrics ? new int[]{v[0], v[1], c} : null);
1137            }
1138        }
1139    }
1140    
1141    /** Outputs to the writer the font dictionaries and streams.
1142     * @param writer the writer for this document
1143     * @param ref the font indirect reference
1144     * @param params several parameters that depend on the font type
1145     * @throws IOException on error
1146     * @throws DocumentException error in generating the object
1147     */

1148    void writeFont(PdfWriter writer, PdfIndirectReference ref, Object JavaDoc params[]) throws DocumentException, IOException JavaDoc {
1149        int firstChar = ((Integer JavaDoc)params[0]).intValue();
1150        int lastChar = ((Integer JavaDoc)params[1]).intValue();
1151        byte shortTag[] = (byte[])params[2];
1152        boolean subsetp = ((Boolean JavaDoc)params[3]).booleanValue() && subset;
1153        
1154        if (!subsetp) {
1155            firstChar = 0;
1156            lastChar = shortTag.length - 1;
1157            for (int k = 0; k < shortTag.length; ++k)
1158                shortTag[k] = 1;
1159        }
1160        PdfIndirectReference ind_font = null;
1161        PdfObject pobj = null;
1162        PdfIndirectObject obj = null;
1163        String JavaDoc subsetPrefix = "";
1164        if (embedded) {
1165            if (cff) {
1166                RandomAccessFileOrArray rf2 = new RandomAccessFileOrArray(rf);
1167                byte b[] = new byte[cffLength];
1168                try {
1169                    rf2.reOpen();
1170                    rf2.seek(cffOffset);
1171                    rf2.readFully(b);
1172                }
1173                finally {
1174                    try {
1175                        rf2.close();
1176                    }
1177                    catch (Exception JavaDoc e) {
1178                        // empty on purpose
1179
}
1180                }
1181                pobj = new StreamFont(b, "Type1C");
1182                obj = writer.addToBody(pobj);
1183                ind_font = obj.getIndirectReference();
1184            }
1185            else {
1186                if (subsetp)
1187                    subsetPrefix = createSubsetPrefix();
1188                HashMap JavaDoc glyphs = new HashMap JavaDoc();
1189                for (int k = firstChar; k <= lastChar; ++k) {
1190                    if (shortTag[k] != 0) {
1191                        int[] metrics = null;
1192                        if (specialMap != null) {
1193                            int[] cd = GlyphList.nameToUnicode(differences[k]);
1194                            if (cd != null)
1195                                metrics = getMetricsTT(cd[0]);
1196                        }
1197                        else {
1198                            if (fontSpecific)
1199                                metrics = getMetricsTT(k);
1200                            else
1201                                metrics = getMetricsTT(unicodeDifferences[k]);
1202                        }
1203                        if (metrics != null)
1204                            glyphs.put(new Integer JavaDoc(metrics[0]), null);
1205                    }
1206                }
1207                addRangeUni(glyphs, false, subsetp);
1208                byte[] b = null;
1209                if (subsetp || directoryOffset != 0 || subsetRanges != null) {
1210                    TrueTypeFontSubSet sb = new TrueTypeFontSubSet(fileName, new RandomAccessFileOrArray(rf), glyphs, directoryOffset, true, !subsetp);
1211                    b = sb.process();
1212                }
1213                else {
1214                    b = getFullFont();
1215                }
1216                int lengths[] = new int[]{b.length};
1217                pobj = new StreamFont(b, lengths);
1218                obj = writer.addToBody(pobj);
1219                ind_font = obj.getIndirectReference();
1220            }
1221        }
1222        pobj = getFontDescriptor(ind_font, subsetPrefix);
1223        if (pobj != null){
1224            obj = writer.addToBody(pobj);
1225            ind_font = obj.getIndirectReference();
1226        }
1227        pobj = getFontBaseType(ind_font, subsetPrefix, firstChar, lastChar, shortTag);
1228        writer.addToBody(pobj, ref);
1229    }
1230    
1231    /** Gets the font parameter identified by <CODE>key</CODE>. Valid values
1232     * for <CODE>key</CODE> are <CODE>ASCENT</CODE>, <CODE>CAPHEIGHT</CODE>, <CODE>DESCENT</CODE>
1233     * and <CODE>ITALICANGLE</CODE>.
1234     * @param key the parameter to be extracted
1235     * @param fontSize the font size in points
1236     * @return the parameter in points
1237     */

1238    public float getFontDescriptor(int key, float fontSize) {
1239        switch (key) {
1240            case ASCENT:
1241                return (float)os_2.sTypoAscender * fontSize / (float)head.unitsPerEm;
1242            case CAPHEIGHT:
1243                return (float)os_2.sCapHeight * fontSize / (float)head.unitsPerEm;
1244            case DESCENT:
1245                return (float)os_2.sTypoDescender * fontSize / (float)head.unitsPerEm;
1246            case ITALICANGLE:
1247                return (float)italicAngle;
1248            case BBOXLLX:
1249                return fontSize * (int)head.xMin / head.unitsPerEm;
1250            case BBOXLLY:
1251                return fontSize * (int)head.yMin / head.unitsPerEm;
1252            case BBOXURX:
1253                return fontSize * (int)head.xMax / head.unitsPerEm;
1254            case BBOXURY:
1255                return fontSize * (int)head.yMax / head.unitsPerEm;
1256            case AWT_ASCENT:
1257                return fontSize * (int)hhea.Ascender / head.unitsPerEm;
1258            case AWT_DESCENT:
1259                return fontSize * (int)hhea.Descender / head.unitsPerEm;
1260            case AWT_LEADING:
1261                return fontSize * (int)hhea.LineGap / head.unitsPerEm;
1262            case AWT_MAXADVANCE:
1263                return fontSize * (int)hhea.advanceWidthMax / head.unitsPerEm;
1264        }
1265        return 0;
1266    }
1267    
1268    /** Gets the glyph index and metrics for a character.
1269     * @param c the character
1270     * @return an <CODE>int</CODE> array with {glyph index, width}
1271     */

1272    public int[] getMetricsTT(int c) {
1273        if (!fontSpecific && cmap31 != null)
1274            return (int[])cmap31.get(new Integer JavaDoc(c));
1275        if (fontSpecific && cmap10 != null)
1276            return (int[])cmap10.get(new Integer JavaDoc(c));
1277        if (cmap31 != null)
1278            return (int[])cmap31.get(new Integer JavaDoc(c));
1279        if (cmap10 != null)
1280            return (int[])cmap10.get(new Integer JavaDoc(c));
1281        return null;
1282    }
1283
1284    /** Gets the postscript font name.
1285     * @return the postscript font name
1286     */

1287    public String JavaDoc getPostscriptFontName() {
1288        return fontName;
1289    }
1290
1291    /** Gets the code pages supported by the font.
1292     * @return the code pages supported by the font
1293     */

1294    public String JavaDoc[] getCodePagesSupported() {
1295        long cp = (((long)os_2.ulCodePageRange2) << 32) + ((long)os_2.ulCodePageRange1 & 0xffffffffL);
1296        int count = 0;
1297        long bit = 1;
1298        for (int k = 0; k < 64; ++k) {
1299            if ((cp & bit) != 0 && codePages[k] != null)
1300                ++count;
1301            bit <<= 1;
1302        }
1303        String JavaDoc ret[] = new String JavaDoc[count];
1304        count = 0;
1305        bit = 1;
1306        for (int k = 0; k < 64; ++k) {
1307            if ((cp & bit) != 0 && codePages[k] != null)
1308                ret[count++] = codePages[k];
1309            bit <<= 1;
1310        }
1311        return ret;
1312    }
1313    
1314    /** Gets the full name of the font. If it is a True Type font
1315     * each array element will have {Platform ID, Platform Encoding ID,
1316     * Language ID, font name}. The interpretation of this values can be
1317     * found in the Open Type specification, chapter 2, in the 'name' table.<br>
1318     * For the other fonts the array has a single element with {"", "", "",
1319     * font name}.
1320     * @return the full name of the font
1321     */

1322    public String JavaDoc[][] getFullFontName() {
1323        return fullName;
1324    }
1325    
1326    /** Gets the family name of the font. If it is a True Type font
1327     * each array element will have {Platform ID, Platform Encoding ID,
1328     * Language ID, font name}. The interpretation of this values can be
1329     * found in the Open Type specification, chapter 2, in the 'name' table.<br>
1330     * For the other fonts the array has a single element with {"", "", "",
1331     * font name}.
1332     * @return the family name of the font
1333     */

1334    public String JavaDoc[][] getFamilyFontName() {
1335        return familyName;
1336    }
1337    
1338    /** Checks if the font has any kerning pairs.
1339     * @return <CODE>true</CODE> if the font has any kerning pairs
1340     */

1341    public boolean hasKernPairs() {
1342        return kerning.size() > 0;
1343    }
1344    
1345    /**
1346     * Sets the font name that will appear in the pdf font dictionary.
1347     * Use with care as it can easily make a font unreadable if not embedded.
1348     * @param name the new font name
1349     */

1350    public void setPostscriptFontName(String JavaDoc name) {
1351        fontName = name;
1352    }
1353    
1354    /**
1355     * Sets the kerning between two Unicode chars.
1356     * @param char1 the first char
1357     * @param char2 the second char
1358     * @param kern the kerning to apply in normalized 1000 units
1359     * @return <code>true</code> if the kerning was applied, <code>false</code> otherwise
1360     */

1361    public boolean setKerning(char char1, char char2, int kern) {
1362        int metrics[] = getMetricsTT(char1);
1363        if (metrics == null)
1364            return false;
1365        int c1 = metrics[0];
1366        metrics = getMetricsTT(char2);
1367        if (metrics == null)
1368            return false;
1369        int c2 = metrics[0];
1370        kerning.put((c1 << 16) + c2, kern);
1371        return true;
1372    }
1373    
1374    protected int[] getRawCharBBox(int c, String JavaDoc name) {
1375        HashMap JavaDoc map = null;
1376        if (name == null || cmap31 == null)
1377            map = cmap10;
1378        else
1379            map = cmap31;
1380        if (map == null)
1381            return null;
1382        int metric[] = (int[])map.get(new Integer JavaDoc(c));
1383        if (metric == null || bboxes == null)
1384            return null;
1385        return bboxes[metric[0]];
1386    }
1387}
Popular Tags