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

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

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

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

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

149     protected String fileName;
150     
151     protected boolean cff = false;
152     
153     protected int cffOffset;
154     
155     protected int cffLength;
156     
157     /** The offset from the start of the file to the table directory.
158      * It is 0 for TTF and may vary for TTC depending on the chosen font.
159      */
    
160     protected int directoryOffset;
161     /** The index for the TTC font. It is an empty <CODE>String</CODE> for a
162      * TTF file.
163      */
    
164     protected String ttcIndex;
165     /** The style modifier */
166     protected String style = "";
167     /** The content of table 'head'.
168      */

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

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

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

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

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

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

205     protected IntHashtable kerning = new IntHashtable();
206     /**
207      * The font name.
208      * This name is usually extracted from the table 'name' with
209      * the 'Name ID' 6.
210      */

211     protected String fontName;
212     
213     /** The full name of the font
214      */
    
215     protected String fullName[][];
216
217     /** All the names of the Names-Table
218      */

219     protected String allNameEntries[][];
220     
221     /** The family name of the font
222      */
    
223     protected String familyName[][];
224     /** The italic angle. It is usually extracted from the 'post' table or in it's
225      * absence with the code:
226      * <P>
227      * <PRE>
228      * -Math.atan2(hhea.caretSlopeRun, hhea.caretSlopeRise) * 180 / Math.PI
229      * </PRE>
230      */

231     protected double italicAngle;
232     /** <CODE>true</CODE> if all the glyphs have the same width.
233      */

234     protected boolean isFixedPitch = false;
235     
236     protected int underlinePosition;
237     
238     protected int underlineThickness;
239     
240     /** The components of table 'head'.
241      */

242     protected static class FontHeader {
243         /** A variable. */
244         int flags;
245         /** A variable. */
246         int unitsPerEm;
247         /** A variable. */
248         short xMin;
249         /** A variable. */
250         short yMin;
251         /** A variable. */
252         short xMax;
253         /** A variable. */
254         short yMax;
255         /** A variable. */
256         int macStyle;
257     }
258     
259     /** The components of table 'hhea'.
260      */

261     protected static class HorizontalHeader {
262         /** A variable. */
263         short Ascender;
264         /** A variable. */
265         short Descender;
266         /** A variable. */
267         short LineGap;
268         /** A variable. */
269         int advanceWidthMax;
270         /** A variable. */
271         short minLeftSideBearing;
272         /** A variable. */
273         short minRightSideBearing;
274         /** A variable. */
275         short xMaxExtent;
276         /** A variable. */
277         short caretSlopeRise;
278         /** A variable. */
279         short caretSlopeRun;
280         /** A variable. */
281         int numberOfHMetrics;
282     }
283     
284     /** The components of table 'OS/2'.
285      */

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

347     protected TrueTypeFont() {
348     }
349     
350     /** Creates a new TrueType font.
351      * @param ttFile the location of the font on file. The file must end in '.ttf' or
352      * '.ttc' but can have modifiers after the name
353      * @param enc the encoding to be applied to this font
354      * @param emb true if the font is to be embedded in the PDF
355      * @param ttfAfm the font as a <CODE>byte</CODE> array
356      * @throws DocumentException the font is invalid
357      * @throws IOException the font file could not be read
358      * @since    2.1.5
359      */

360     TrueTypeFont(String ttFile, String enc, boolean emb, byte ttfAfm[], boolean justNames, boolean forceRead) throws DocumentException, IOException {
361         this.justNames = justNames;
362         String nameBase = getBaseName(ttFile);
363         String ttcName = getTTCName(nameBase);
364         if (nameBase.length() < ttFile.length()) {
365             style = ttFile.substring(nameBase.length());
366         }
367         encoding = enc;
368         embedded = emb;
369         fileName = ttcName;
370         fontType = FONT_TYPE_TT;
371         ttcIndex = "";
372         if (ttcName.length() < nameBase.length())
373             ttcIndex = nameBase.substring(ttcName.length() + 1);
374         if (fileName.toLowerCase().endsWith(".ttf") || fileName.toLowerCase().endsWith(".otf") || fileName.toLowerCase().endsWith(".ttc")) {
375             process(ttfAfm, forceRead);
376             if (!justNames && embedded && os_2.fsType == 2)
377                 throw new DocumentException(fileName + style + " cannot be embedded due to licensing restrictions.");
378         }
379         else
380             throw new DocumentException(fileName + style + " is not a TTF, OTF or TTC font file.");
381         if (!encoding.startsWith("#"))
382             PdfEncodings.convertToBytes(" ", enc); // check if the encoding exists
383         createEncoding();
384     }
385     
386     /** Gets the name from a composed TTC file name.
387      * If I have for input "myfont.ttc,2" the return will
388      * be "myfont.ttc".
389      * @param name the full name
390      * @return the simple file name
391      */
    
392     protected static String getTTCName(String name) {
393         int idx = name.toLowerCase().indexOf(".ttc,");
394         if (idx < 0)
395             return name;
396         else
397             return name.substring(0, idx + 4);
398     }
399     
400     
401     /**
402      * Reads the tables 'head', 'hhea', 'OS/2' and 'post' filling several variables.
403      * @throws DocumentException the font is invalid
404      * @throws IOException the font file could not be read
405      */

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

503     String getBaseFont() throws DocumentException, IOException {
504         int table_location[];
505         table_location = (int[])tables.get("name");
506         if (table_location == null)
507             throw new DocumentException("Table 'name' does not exist in " + fileName + style);
508         rf.seek(table_location[0] + 2);
509         int numRecords = rf.readUnsignedShort();
510         int startOfStorage = rf.readUnsignedShort();
511         for (int k = 0; k < numRecords; ++k) {
512             int platformID = rf.readUnsignedShort();
513             int platformEncodingID = rf.readUnsignedShort();
514             int languageID = rf.readUnsignedShort();
515             int nameID = rf.readUnsignedShort();
516             int length = rf.readUnsignedShort();
517             int offset = rf.readUnsignedShort();
518             if (nameID == 6) {
519                 rf.seek(table_location[0] + startOfStorage + offset);
520                 if (platformID == 0 || platformID == 3)
521                     return readUnicodeString(length);
522                 else
523                     return readStandardString(length);
524             }
525         }
526         File file = new File(fileName);
527         return file.getName().replace(' ', '-');
528     }
529     
530     /** Extracts the names of the font in all the languages available.
531      * @param id the name id to retrieve
532      * @throws DocumentException on error
533      * @throws IOException on error
534      */
    
535     String[][] getNames(int id) throws DocumentException, IOException {
536         int table_location[];
537         table_location = (int[])tables.get("name");
538         if (table_location == null)
539             throw new DocumentException("Table 'name' does not exist in " + fileName + style);
540         rf.seek(table_location[0] + 2);
541         int numRecords = rf.readUnsignedShort();
542         int startOfStorage = rf.readUnsignedShort();
543         ArrayList names = new ArrayList();
544         for (int k = 0; k < numRecords; ++k) {
545             int platformID = rf.readUnsignedShort();
546             int platformEncodingID = rf.readUnsignedShort();
547             int languageID = rf.readUnsignedShort();
548             int nameID = rf.readUnsignedShort();
549             int length = rf.readUnsignedShort();
550             int offset = rf.readUnsignedShort();
551             if (nameID == id) {
552                 int pos = rf.getFilePointer();
553                 rf.seek(table_location[0] + startOfStorage + offset);
554                 String name;
555                 if (platformID == 0 || platformID == 3 || (platformID == 2 && platformEncodingID == 1)){
556                     name = readUnicodeString(length);
557                 }
558                 else {
559                     name = readStandardString(length);
560                 }
561                 names.add(new String[]{String.valueOf(platformID),
562                     String.valueOf(platformEncodingID), String.valueOf(languageID), name});
563                 rf.seek(pos);
564             }
565         }
566         String thisName[][] = new String[names.size()][];
567         for (int k = 0; k < names.size(); ++k)
568             thisName[k] = (String[])names.get(k);
569         return thisName;
570     }
571     
572     /** Extracts all the names of the names-Table
573      * @throws DocumentException on error
574      * @throws IOException on error
575      */
    
576     String[][] getAllNames() throws DocumentException, IOException {
577         int table_location[];
578         table_location = (int[])tables.get("name");
579         if (table_location == null)
580             throw new DocumentException("Table 'name' does not exist in " + fileName + style);
581         rf.seek(table_location[0] + 2);
582         int numRecords = rf.readUnsignedShort();
583         int startOfStorage = rf.readUnsignedShort();
584         ArrayList names = new ArrayList();
585         for (int k = 0; k < numRecords; ++k) {
586             int platformID = rf.readUnsignedShort();
587             int platformEncodingID = rf.readUnsignedShort();
588             int languageID = rf.readUnsignedShort();
589             int nameID = rf.readUnsignedShort();
590             int length = rf.readUnsignedShort();
591             int offset = rf.readUnsignedShort();
592             int pos = rf.getFilePointer();
593             rf.seek(table_location[0] + startOfStorage + offset);
594             String name;
595             if (platformID == 0 || platformID == 3 || (platformID == 2 && platformEncodingID == 1)){
596                 name = readUnicodeString(length);
597             }
598             else {
599                 name = readStandardString(length);
600             }
601             names.add(new String[]{String.valueOf(nameID), String.valueOf(platformID),
602                     String.valueOf(platformEncodingID), String.valueOf(languageID), name});
603             rf.seek(pos);
604         }
605         String thisName[][] = new String[names.size()][];
606         for (int k = 0; k < names.size(); ++k)
607             thisName[k] = (String[])names.get(k);
608         return thisName;
609     }
610     
611     void checkCff() {
612         int table_location[];
613         table_location = (int[])tables.get("CFF ");
614         if (table_location != null) {
615             cff = true;
616             cffOffset = table_location[0];
617             cffLength = table_location[1];
618         }
619     }
620
621     /** Reads the font data.
622      * @param ttfAfm the font as a <CODE>byte</CODE> array, possibly <CODE>null</CODE>
623      * @throws DocumentException the font is invalid
624      * @throws IOException the font file could not be read
625      * @since    2.1.5
626      */

627     void process(byte ttfAfm[], boolean preload) throws DocumentException, IOException {
628         tables = new HashMap();
629         
630         try {
631             if (ttfAfm == null)
632                 rf = new RandomAccessFileOrArray(fileName, preload, Document.plainRandomAccess);
633             else
634                 rf = new RandomAccessFileOrArray(ttfAfm);
635             if (ttcIndex.length() > 0) {
636                 int dirIdx = Integer.parseInt(ttcIndex);
637                 if (dirIdx < 0)
638                     throw new DocumentException("The font index for " + fileName + " must be positive.");
639                 String mainTag = readStandardString(4);
640                 if (!mainTag.equals("ttcf"))
641                     throw new DocumentException(fileName + " is not a valid TTC file.");
642                 rf.skipBytes(4);
643                 int dirCount = rf.readInt();
644                 if (dirIdx >= dirCount)
645                     throw new DocumentException("The font index for " + fileName + " must be between 0 and " + (dirCount - 1) + ". It was " + dirIdx + ".");
646                 rf.skipBytes(dirIdx * 4);
647                 directoryOffset = rf.readInt();
648             }
649             rf.seek(directoryOffset);
650             int ttId = rf.readInt();
651             if (ttId != 0x00010000 && ttId != 0x4F54544F)
652                 throw new DocumentException(fileName + " is not a valid TTF or OTF file.");
653             int num_tables = rf.readUnsignedShort();
654             rf.skipBytes(6);
655             for (int k = 0; k < num_tables; ++k) {
656                 String tag = readStandardString(4);
657                 rf.skipBytes(4);
658                 int table_location[] = new int[2];
659                 table_location[0] = rf.readInt();
660                 table_location[1] = rf.readInt();
661                 tables.put(tag, table_location);
662             }
663             checkCff();
664             fontName = getBaseFont();
665             fullName = getNames(4); //full name
666             familyName = getNames(1); //family name
667             allNameEntries = getAllNames();
668             if (!justNames) {
669                 fillTables();
670                 readGlyphWidths();
671                 readCMaps();
672                 readKerning();
673                 readBbox();
674                 GlyphWidths = null;
675             }
676         }
677         finally {
678             if (rf != null) {
679                 rf.close();
680                 if (!embedded)
681                     rf = null;
682             }
683         }
684     }
685     
686     /** Reads a <CODE>String</CODE> from the font file as bytes using the Cp1252
687      *  encoding.
688      * @param length the length of bytes to read
689      * @return the <CODE>String</CODE> read
690      * @throws IOException the font file could not be read
691      */

692     protected String readStandardString(int length) throws IOException {
693         byte buf[] = new byte[length];
694         rf.readFully(buf);
695         try {
696             return new String(buf, WINANSI);
697         }
698         catch (Exception e) {
699             throw new ExceptionConverter(e);
700         }
701     }
702     
703     /** Reads a Unicode <CODE>String</CODE> from the font file. Each character is
704      *  represented by two bytes.
705      * @param length the length of bytes to read. The <CODE>String</CODE> will have <CODE>length</CODE>/2
706      * characters
707      * @return the <CODE>String</CODE> read
708      * @throws IOException the font file could not be read
709      */

710     protected String readUnicodeString(int length) throws IOException {
711         StringBuffer buf = new StringBuffer();
712         length /= 2;
713         for (int k = 0; k < length; ++k) {
714             buf.append(rf.readChar());
715         }
716         return buf.toString();
717     }
718     
719     /** Reads the glyphs widths. The widths are extracted from the table 'hmtx'.
720      *  The glyphs are normalized to 1000 units.
721      * @throws DocumentException the font is invalid
722      * @throws IOException the font file could not be read
723      */

724     protected void readGlyphWidths() throws DocumentException, IOException {
725         int table_location[];
726         table_location = (int[])tables.get("hmtx");
727         if (table_location == null)
728             throw new DocumentException("Table 'hmtx' does not exist in " + fileName + style);
729         rf.seek(table_location[0]);
730         GlyphWidths = new int[hhea.numberOfHMetrics];
731         for (int k = 0; k < hhea.numberOfHMetrics; ++k) {
732             GlyphWidths[k] = (rf.readUnsignedShort() * 1000) / head.unitsPerEm;
733             rf.readUnsignedShort();
734         }
735     }
736     
737     /** Gets a glyph width.
738      * @param glyph the glyph to get the width of
739      * @return the width of the glyph in normalized 1000 units
740      */

741     protected int getGlyphWidth(int glyph) {
742         if (glyph >= GlyphWidths.length)
743             glyph = GlyphWidths.length - 1;
744         return GlyphWidths[glyph];
745     }
746     
747     private void readBbox() throws DocumentException, IOException {
748         int tableLocation[];
749         tableLocation = (int[])tables.get("head");
750         if (tableLocation == null)
751             throw new DocumentException("Table 'head' does not exist in " + fileName + style);
752         rf.seek(tableLocation[0] + TrueTypeFontSubSet.HEAD_LOCA_FORMAT_OFFSET);
753         boolean locaShortTable = (rf.readUnsignedShort() == 0);
754         tableLocation = (int[])tables.get("loca");
755         if (tableLocation == null)
756             return;
757         rf.seek(tableLocation[0]);
758         int locaTable[];
759         if (locaShortTable) {
760             int entries = tableLocation[1] / 2;
761             locaTable = new int[entries];
762             for (int k = 0; k < entries; ++k)
763                 locaTable[k] = rf.readUnsignedShort() * 2;
764         }
765         else {
766             int entries = tableLocation[1] / 4;
767             locaTable = new int[entries];
768             for (int k = 0; k < entries; ++k)
769                 locaTable[k] = rf.readInt();
770         }
771         tableLocation = (int[])tables.get("glyf");
772         if (tableLocation == null)
773             throw new DocumentException("Table 'glyf' does not exist in " + fileName + style);
774         int tableGlyphOffset = tableLocation[0];
775         bboxes = new int[locaTable.length - 1][];
776         for (int glyph = 0; glyph < locaTable.length - 1; ++glyph) {
777             int start = locaTable[glyph];
778             if (start != locaTable[glyph + 1]) {
779                 rf.seek(tableGlyphOffset + start + 2);
780                 bboxes[glyph] = new int[]{
781                     (rf.readShort() * 1000) / head.unitsPerEm,
782                     (rf.readShort() * 1000) / head.unitsPerEm,
783                     (rf.readShort() * 1000) / head.unitsPerEm,
784                     (rf.readShort() * 1000) / head.unitsPerEm};
785             }
786         }
787     }
788     
789     /** Reads the several maps from the table 'cmap'. The maps of interest are 1.0 for symbolic
790      *  fonts and 3.1 for all others. A symbolic font is defined as having the map 3.0.
791      * @throws DocumentException the font is invalid
792      * @throws IOException the font file could not be read
793      */

794     void readCMaps() throws DocumentException, IOException {
795         int table_location[];
796         table_location = (int[])tables.get("cmap");
797         if (table_location == null)
798             throw new DocumentException("Table 'cmap' does not exist in " + fileName + style);
799         rf.seek(table_location[0]);
800         rf.skipBytes(2);
801         int num_tables = rf.readUnsignedShort();
802         fontSpecific = false;
803         int map10 = 0;
804         int map31 = 0;
805         int map30 = 0;
806         int mapExt = 0;
807         for (int k = 0; k < num_tables; ++k) {
808             int platId = rf.readUnsignedShort();
809             int platSpecId = rf.readUnsignedShort();
810             int offset = rf.readInt();
811             if (platId == 3 && platSpecId == 0) {
812                 fontSpecific = true;
813                 map30 = offset;
814             }
815             else if (platId == 3 && platSpecId == 1) {
816                 map31 = offset;
817             }
818             else if (platId == 3 && platSpecId == 10) {
819                 mapExt = offset;
820             }
821             if (platId == 1 && platSpecId == 0) {
822                 map10 = offset;
823             }
824         }
825         if (map10 > 0) {
826             rf.seek(table_location[0] + map10);
827             int format = rf.readUnsignedShort();
828             switch (format) {
829                 case 0:
830                     cmap10 = readFormat0();
831                     break;
832                 case 4:
833                     cmap10 = readFormat4();
834                     break;
835                 case 6:
836                     cmap10 = readFormat6();
837                     break;
838             }
839         }
840         if (map31 > 0) {
841             rf.seek(table_location[0] + map31);
842             int format = rf.readUnsignedShort();
843             if (format == 4) {
844                 cmap31 = readFormat4();
845             }
846         }
847         if (map30 > 0) {
848             rf.seek(table_location[0] + map30);
849             int format = rf.readUnsignedShort();
850             if (format == 4) {
851                 cmap10 = readFormat4();
852             }
853         }
854         if (mapExt > 0) {
855             rf.seek(table_location[0] + mapExt);
856             int format = rf.readUnsignedShort();
857             switch (format) {
858                 case 0:
859                     cmapExt = readFormat0();
860                     break;
861                 case 4:
862                     cmapExt = readFormat4();
863                     break;
864                 case 6:
865                     cmapExt = readFormat6();
866                     break;
867                 case 12:
868                     cmapExt = readFormat12();
869                     break;
870             }
871         }
872     }
873
874     HashMap readFormat12() throws IOException {
875         HashMap h = new HashMap();
876         rf.skipBytes(2);
877         int table_lenght = rf.readInt();
878         rf.skipBytes(4);
879         int nGroups = rf.readInt();
880         for (int k = 0; k < nGroups; k++) {
881             int startCharCode = rf.readInt();
882             int endCharCode = rf.readInt();
883             int startGlyphID = rf.readInt();
884             for (int i = startCharCode; i <= endCharCode; i++) {
885                 int[] r = new int[2];
886                 r[0] = startGlyphID;
887                 r[1] = getGlyphWidth(r[0]);
888                 h.put(new Integer(i), r);
889                 startGlyphID++;
890             }
891         }
892         return h;
893     }
894     
895     /** The information in the maps of the table 'cmap' is coded in several formats.
896      *  Format 0 is the Apple standard character to glyph index mapping table.
897      * @return a <CODE>HashMap</CODE> representing this map
898      * @throws IOException the font file could not be read
899      */

900     HashMap readFormat0() throws IOException {
901         HashMap h = new HashMap();
902         rf.skipBytes(4);
903         for (int k = 0; k < 256; ++k) {
904             int r[] = new int[2];
905             r[0] = rf.readUnsignedByte();
906             r[1] = getGlyphWidth(r[0]);
907             h.put(new Integer(k), r);
908         }
909         return h;
910     }
911     
912     /** The information in the maps of the table 'cmap' is coded in several formats.
913      *  Format 4 is the Microsoft standard character to glyph index mapping table.
914      * @return a <CODE>HashMap</CODE> representing this map
915      * @throws IOException the font file could not be read
916      */

917     HashMap readFormat4() throws IOException {
918         HashMap h = new HashMap();
919         int table_lenght = rf.readUnsignedShort();
920         rf.skipBytes(2);
921         int segCount = rf.readUnsignedShort() / 2;
922         rf.skipBytes(6);
923         int endCount[] = new int[segCount];
924         for (int k = 0; k < segCount; ++k) {
925             endCount[k] = rf.readUnsignedShort();
926         }
927         rf.skipBytes(2);
928         int startCount[] = new int[segCount];
929         for (int k = 0; k < segCount; ++k) {
930             startCount[k] = rf.readUnsignedShort();
931         }
932         int idDelta[] = new int[segCount];
933         for (int k = 0; k < segCount; ++k) {
934             idDelta[k] = rf.readUnsignedShort();
935         }
936         int idRO[] = new int[segCount];
937         for (int k = 0; k < segCount; ++k) {
938             idRO[k] = rf.readUnsignedShort();
939         }
940         int glyphId[] = new int[table_lenght / 2 - 8 - segCount * 4];
941         for (int k = 0; k < glyphId.length; ++k) {
942             glyphId[k] = rf.readUnsignedShort();
943         }
944         for (int k = 0; k < segCount; ++k) {
945             int glyph;
946             for (int j = startCount[k]; j <= endCount[k] && j != 0xFFFF; ++j) {
947                 if (idRO[k] == 0) {
948                     glyph = (j + idDelta[k]) & 0xFFFF;
949                 }
950                 else {
951                     int idx = k + idRO[k] / 2 - segCount + j - startCount[k];
952                     if (idx >= glyphId.length)
953                         continue;
954                     glyph = (glyphId[idx] + idDelta[k]) & 0xFFFF;
955                 }
956                 int r[] = new int[2];
957                 r[0] = glyph;
958                 r[1] = getGlyphWidth(r[0]);
959                 h.put(new Integer(fontSpecific ? ((j & 0xff00) == 0xf000 ? j & 0xff : j) : j), r);
960             }
961         }
962         return h;
963     }
964     
965     /** The information in the maps of the table 'cmap' is coded in several formats.
966      *  Format 6 is a trimmed table mapping. It is similar to format 0 but can have
967      *  less than 256 entries.
968      * @return a <CODE>HashMap</CODE> representing this map
969      * @throws IOException the font file could not be read
970      */

971     HashMap readFormat6() throws IOException {
972         HashMap h = new HashMap();
973         rf.skipBytes(4);
974         int start_code = rf.readUnsignedShort();
975         int code_count = rf.readUnsignedShort();
976         for (int k = 0; k < code_count; ++k) {
977             int r[] = new int[2];
978             r[0] = rf.readUnsignedShort();
979             r[1] = getGlyphWidth(r[0]);
980             h.put(new Integer(k + start_code), r);
981         }
982         return h;
983     }
984     
985     /** Reads the kerning information from the 'kern' table.
986      * @throws IOException the font file could not be read
987      */

988     void readKerning() throws IOException {
989         int table_location[];
990         table_location = (int[])tables.get("kern");
991         if (table_location == null)
992             return;
993         rf.seek(table_location[0] + 2);
994         int nTables = rf.readUnsignedShort();
995         int checkpoint = table_location[0] + 4;
996         int length = 0;
997         for (int k = 0; k < nTables; ++k) {
998             checkpoint += length;
999             rf.seek(checkpoint);
1000             rf.skipBytes(2);
1001             length = rf.readUnsignedShort();
1002             int coverage = rf.readUnsignedShort();
1003             if ((coverage & 0xfff7) == 0x0001) {
1004                 int nPairs = rf.readUnsignedShort();
1005                 rf.skipBytes(6);
1006                 for (int j = 0; j < nPairs; ++j) {
1007                     int pair = rf.readInt();
1008                     int value = rf.readShort() * 1000 / head.unitsPerEm;
1009                     kerning.put(pair, value);
1010                 }
1011             }
1012         }
1013     }
1014     
1015     /** Gets the kerning between two Unicode chars.
1016      * @param char1 the first char
1017      * @param char2 the second char
1018      * @return the kerning to be applied
1019      */

1020     public int getKerning(int char1, int char2) {
1021         int metrics[] = getMetricsTT(char1);
1022         if (metrics == null)
1023             return 0;
1024         int c1 = metrics[0];
1025         metrics = getMetricsTT(char2);
1026         if (metrics == null)
1027             return 0;
1028         int c2 = metrics[0];
1029         return kerning.get((c1 << 16) + c2);
1030     }
1031     
1032     /** Gets the width from the font according to the unicode char <CODE>c</CODE>.
1033      * If the <CODE>name</CODE> is null it's a symbolic font.
1034      * @param c the unicode char
1035      * @param name the glyph name
1036      * @return the width of the char
1037      */

1038     int getRawWidth(int c, String name) {
1039         int[] metric = getMetricsTT(c);
1040         if (metric == null)
1041             return 0;
1042         return metric[1];
1043     }
1044     
1045     /** Generates the font descriptor for this font.
1046      * @return the PdfDictionary containing the font descriptor or <CODE>null</CODE>
1047      * @param subsetPrefix the subset prefix
1048      * @param fontStream the indirect reference to a PdfStream containing the font or <CODE>null</CODE>
1049      */

1050     protected PdfDictionary getFontDescriptor(PdfIndirectReference fontStream, String subsetPrefix, PdfIndirectReference cidset) {
1051         PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR);
1052         dic.put(PdfName.ASCENT, new PdfNumber(os_2.sTypoAscender * 1000 / head.unitsPerEm));
1053         dic.put(PdfName.CAPHEIGHT, new PdfNumber(os_2.sCapHeight * 1000 / head.unitsPerEm));
1054         dic.put(PdfName.DESCENT, new PdfNumber(os_2.sTypoDescender * 1000 / head.unitsPerEm));
1055         dic.put(PdfName.FONTBBOX, new PdfRectangle(
1056         head.xMin * 1000 / head.unitsPerEm,
1057         head.yMin * 1000 / head.unitsPerEm,
1058         head.xMax * 1000 / head.unitsPerEm,
1059         head.yMax * 1000 / head.unitsPerEm));
1060         if (cidset != null)
1061             dic.put(PdfName.CIDSET, cidset);
1062         if (cff) {
1063             if (encoding.startsWith("Identity-"))
1064                 dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName+"-"+encoding));
1065             else
1066                 dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style));
1067         }
1068         else
1069             dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style));
1070         dic.put(PdfName.ITALICANGLE, new PdfNumber(italicAngle));
1071         dic.put(PdfName.STEMV, new PdfNumber(80));
1072         if (fontStream != null) {
1073             if (cff)
1074                 dic.put(PdfName.FONTFILE3, fontStream);
1075             else
1076                 dic.put(PdfName.FONTFILE2, fontStream);
1077         }
1078         int flags = 0;
1079         if (isFixedPitch)
1080             flags |= 1;
1081         flags |= fontSpecific ? 4 : 32;
1082         if ((head.macStyle & 2) != 0)
1083             flags |= 64;
1084         if ((head.macStyle & 1) != 0)
1085             flags |= 262144;
1086         dic.put(PdfName.FLAGS, new PdfNumber(flags));
1087         
1088         return dic;
1089     }
1090     
1091     /** Generates the font dictionary for this font.
1092      * @return the PdfDictionary containing the font dictionary
1093      * @param subsetPrefix the subset prefix
1094      * @param firstChar the first valid character
1095      * @param lastChar the last valid character
1096      * @param shortTag a 256 bytes long <CODE>byte</CODE> array where each unused byte is represented by 0
1097      * @param fontDescriptor the indirect reference to a PdfDictionary containing the font descriptor or <CODE>null</CODE>
1098      */

1099     protected PdfDictionary getFontBaseType(PdfIndirectReference fontDescriptor, String subsetPrefix, int firstChar, int lastChar, byte shortTag[]) {
1100         PdfDictionary dic = new PdfDictionary(PdfName.FONT);
1101         if (cff) {
1102             dic.put(PdfName.SUBTYPE, PdfName.TYPE1);
1103             dic.put(PdfName.BASEFONT, new PdfName(fontName + style));
1104         }
1105         else {
1106             dic.put(PdfName.SUBTYPE, PdfName.TRUETYPE);
1107             dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style));
1108         }
1109         dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style));
1110         if (!fontSpecific) {
1111             for (int k = firstChar; k <= lastChar; ++k) {
1112                 if (!differences[k].equals(notdef)) {
1113                     firstChar = k;
1114                     break;
1115                 }
1116             }
1117         if (encoding.equals("Cp1252") || encoding.equals("MacRoman"))
1118                 dic.put(PdfName.ENCODING, encoding.equals("Cp1252") ? PdfName.WIN_ANSI_ENCODING : PdfName.MAC_ROMAN_ENCODING);
1119             else {
1120                 PdfDictionary enc = new PdfDictionary(PdfName.ENCODING);
1121                 PdfArray dif = new PdfArray();
1122                 boolean gap = true;                
1123                 for (int k = firstChar; k <= lastChar; ++k) {
1124                     if (shortTag[k] != 0) {
1125                         if (gap) {
1126                             dif.add(new PdfNumber(k));
1127                             gap = false;
1128                         }
1129                         dif.add(new PdfName(differences[k]));
1130                     }
1131                     else
1132                         gap = true;
1133                 }
1134                 enc.put(PdfName.DIFFERENCES, dif);
1135                 dic.put(PdfName.ENCODING, enc);
1136             }
1137         }
1138         dic.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar));
1139         dic.put(PdfName.LASTCHAR, new PdfNumber(lastChar));
1140         PdfArray wd = new PdfArray();
1141         for (int k = firstChar; k <= lastChar; ++k) {
1142             if (shortTag[k] == 0)
1143                 wd.add(new PdfNumber(0));
1144             else
1145                 wd.add(new PdfNumber(widths[k]));
1146         }
1147         dic.put(PdfName.WIDTHS, wd);
1148         if (fontDescriptor != null)
1149             dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor);
1150         return dic;
1151     }
1152     
1153     protected byte[] getFullFont() throws IOException {
1154         RandomAccessFileOrArray rf2 = null;
1155         try {
1156             rf2 = new RandomAccessFileOrArray(rf);
1157             rf2.reOpen();
1158             byte b[] = new byte[rf2.length()];
1159             rf2.readFully(b);
1160             return b;
1161         } 
1162         finally {
1163             try {if (rf2 != null) {rf2.close();}} catch (Exception e) {}
1164         }
1165     }
1166     
1167     protected static int[] compactRanges(ArrayList ranges) {
1168         ArrayList simp = new ArrayList();
1169         for (int k = 0; k < ranges.size(); ++k) {
1170             int[] r = (int[])ranges.get(k);
1171             for (int j = 0; j < r.length; j += 2) {
1172                 simp.add(new int[]{Math.max(0, Math.min(r[j], r[j + 1])), Math.min(0xffff, Math.max(r[j], r[j + 1]))});
1173             }
1174         }
1175         for (int k1 = 0; k1 < simp.size() - 1; ++k1) {
1176             for (int k2 = k1 + 1; k2 < simp.size(); ++k2) {
1177                 int[] r1 = (int[])simp.get(k1);
1178                 int[] r2 = (int[])simp.get(k2);
1179                 if ((r1[0] >= r2[0] && r1[0] <= r2[1]) || (r1[1] >= r2[0] && r1[0] <= r2[1])) {
1180                     r1[0] = Math.min(r1[0], r2[0]);
1181                     r1[1] = Math.max(r1[1], r2[1]);
1182                     simp.remove(k2);
1183                     --k2;
1184                 }
1185             }
1186         }
1187         int[] s = new int[simp.size() * 2];
1188         for (int k = 0; k < simp.size(); ++k) {
1189             int[] r = (int[])simp.get(k);
1190             s[k * 2] = r[0];
1191             s[k * 2 + 1] = r[1];
1192         }
1193         return s;
1194     }
1195     
1196     protected void addRangeUni(HashMap longTag, boolean includeMetrics, boolean subsetp) {
1197         if (!subsetp && (subsetRanges != null || directoryOffset > 0)) {
1198             int[] rg = (subsetRanges == null && directoryOffset > 0) ? new int[]{0, 0xffff} : compactRanges(subsetRanges);
1199             HashMap usemap;
1200             if (!fontSpecific && cmap31 != null
1201                 usemap = cmap31;
1202             else if (fontSpecific && cmap10 != null
1203                 usemap = cmap10;
1204             else if (cmap31 != null
1205                 usemap = cmap31;
1206             else 
1207                 usemap = cmap10;
1208             for (Iterator it = usemap.entrySet().iterator(); it.hasNext();) {
1209                 Map.Entry e = (Map.Entry)it.next();
1210                 int[] v = (int[])e.getValue();
1211                 Integer gi = new Integer(v[0]);
1212                 if (longTag.containsKey(gi))
1213                     continue;
1214                 int c = ((Integer)e.getKey()).intValue();
1215                 boolean skip = true;
1216                 for (int k = 0; k < rg.length; k += 2) {
1217                     if (c >= rg[k] && c <= rg[k + 1]) {
1218                         skip = false;
1219                         break;
1220                     }
1221                 }
1222                 if (!skip)
1223                     longTag.put(gi, includeMetrics ? new int[]{v[0], v[1], c} : null);
1224             }
1225         }
1226     }
1227     
1228     /** Outputs to the writer the font dictionaries and streams.
1229      * @param writer the writer for this document
1230      * @param ref the font indirect reference
1231      * @param params several parameters that depend on the font type
1232      * @throws IOException on error
1233      * @throws DocumentException error in generating the object
1234      */

1235     void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException {
1236         int firstChar = ((Integer)params[0]).intValue();
1237         int lastChar = ((Integer)params[1]).intValue();
1238         byte shortTag[] = (byte[])params[2];
1239         boolean subsetp = ((Boolean)params[3]).booleanValue() && subset;
1240         
1241         if (!subsetp) {
1242             firstChar = 0;
1243             lastChar = shortTag.length - 1;
1244             for (int k = 0; k < shortTag.length; ++k)
1245                 shortTag[k] = 1;
1246         }
1247         PdfIndirectReference ind_font = null;
1248         PdfObject pobj = null;
1249         PdfIndirectObject obj = null;
1250         String subsetPrefix = "";
1251         if (embedded) {
1252             if (cff) {
1253                 pobj = new StreamFont(readCffFont(), "Type1C", compressionLevel);
1254                 obj = writer.addToBody(pobj);
1255                 ind_font = obj.getIndirectReference();
1256             }
1257             else {
1258                 if (subsetp)
1259                     subsetPrefix = createSubsetPrefix();
1260                 HashMap glyphs = new HashMap();
1261                 for (int k = firstChar; k <= lastChar; ++k) {
1262                     if (shortTag[k] != 0) {
1263                         int[] metrics = null;
1264                         if (specialMap != null) {
1265                             int[] cd = GlyphList.nameToUnicode(differences[k]);
1266                             if (cd != null)
1267                                 metrics = getMetricsTT(cd[0]);
1268                         }
1269                         else {
1270                             if (fontSpecific)
1271                                 metrics = getMetricsTT(k);
1272                             else
1273                                 metrics = getMetricsTT(unicodeDifferences[k]);
1274                         }
1275                         if (metrics != null)
1276                             glyphs.put(new Integer(metrics[0]), null);
1277                     }
1278                 }
1279                 addRangeUni(glyphs, false, subsetp);
1280                 byte[] b = null;
1281                 if (subsetp || directoryOffset != 0 || subsetRanges != null) {
1282                     TrueTypeFontSubSet sb = new TrueTypeFontSubSet(fileName, new RandomAccessFileOrArray(rf), glyphs, directoryOffset, true, !subsetp);
1283                     b = sb.process();
1284                 }
1285                 else {
1286                     b = getFullFont();
1287                 }
1288                 int lengths[] = new int[]{b.length};
1289                 pobj = new StreamFont(b, lengths, compressionLevel);
1290                 obj = writer.addToBody(pobj);
1291                 ind_font = obj.getIndirectReference();
1292             }
1293         }
1294         pobj = getFontDescriptor(ind_font, subsetPrefix, null);
1295         if (pobj != null){
1296             obj = writer.addToBody(pobj);
1297             ind_font = obj.getIndirectReference();
1298         }
1299         pobj = getFontBaseType(ind_font, subsetPrefix, firstChar, lastChar, shortTag);
1300         writer.addToBody(pobj, ref);
1301     }
1302     
1303     /**
1304      * If this font file is using the Compact Font File Format, then this method
1305      * will return the raw bytes needed for the font stream. If this method is
1306      * ever made public: make sure to add a test if (cff == true).
1307      * @return    a byte array
1308      * @since    2.1.3
1309      */

1310     protected byte[] readCffFont() throws IOException {
1311         RandomAccessFileOrArray rf2 = new RandomAccessFileOrArray(rf);
1312         byte b[] = new byte[cffLength];
1313         try {
1314             rf2.reOpen();
1315             rf2.seek(cffOffset);
1316             rf2.readFully(b);
1317         }
1318         finally {
1319             try {
1320                 rf2.close();
1321             }
1322             catch (Exception e) {
1323                 // empty on purpose
1324             }
1325         }
1326         return b;
1327     }
1328
1329     /**
1330      * Returns a PdfStream object with the full font program.
1331      * @return    a PdfStream with the font program
1332      * @since    2.1.3
1333      */

1334     public PdfStream getFullFontStream() throws IOException, DocumentException {
1335         if (cff) {
1336             return new StreamFont(readCffFont(), "Type1C", compressionLevel);
1337         }
1338         else {
1339             byte[] b = getFullFont();
1340             int lengths[] = new int[]{b.length};
1341             return new StreamFont(b, lengths, compressionLevel);
1342         }
1343     }
1344     
1345     /** Gets the font parameter identified by <CODE>key</CODE>. Valid values
1346      * for <CODE>key</CODE> are <CODE>ASCENT</CODE>, <CODE>CAPHEIGHT</CODE>, <CODE>DESCENT</CODE>
1347      * and <CODE>ITALICANGLE</CODE>.
1348      * @param key the parameter to be extracted
1349      * @param fontSize the font size in points
1350      * @return the parameter in points
1351      */
    
1352     public float getFontDescriptor(int key, float fontSize) {
1353         switch (key) {
1354             case ASCENT:
1355                 return os_2.sTypoAscender * fontSize / head.unitsPerEm;
1356             case CAPHEIGHT:
1357                 return os_2.sCapHeight * fontSize / head.unitsPerEm;
1358             case DESCENT:
1359                 return os_2.sTypoDescender * fontSize / head.unitsPerEm;
1360             case ITALICANGLE:
1361                 return (float)italicAngle;
1362             case BBOXLLX:
1363                 return fontSize * head.xMin / head.unitsPerEm;
1364             case BBOXLLY:
1365                 return fontSize * head.yMin / head.unitsPerEm;
1366             case BBOXURX:
1367                 return fontSize * head.xMax / head.unitsPerEm;
1368             case BBOXURY:
1369                 return fontSize * head.yMax / head.unitsPerEm;
1370             case AWT_ASCENT:
1371                 return fontSize * hhea.Ascender / head.unitsPerEm;
1372             case AWT_DESCENT:
1373                 return fontSize * hhea.Descender / head.unitsPerEm;
1374             case AWT_LEADING:
1375                 return fontSize * hhea.LineGap / head.unitsPerEm;
1376             case AWT_MAXADVANCE:
1377                 return fontSize * hhea.advanceWidthMax / head.unitsPerEm;
1378             case UNDERLINE_POSITION:
1379                 return (underlinePosition - underlineThickness / 2) * fontSize / head.unitsPerEm;
1380             case UNDERLINE_THICKNESS:
1381                 return underlineThickness * fontSize / head.unitsPerEm;
1382             case STRIKETHROUGH_POSITION:
1383                 return os_2.yStrikeoutPosition * fontSize / head.unitsPerEm;
1384             case STRIKETHROUGH_THICKNESS:
1385                 return os_2.yStrikeoutSize * fontSize / head.unitsPerEm;
1386             case SUBSCRIPT_SIZE:
1387                 return os_2.ySubscriptYSize * fontSize / head.unitsPerEm;
1388             case SUBSCRIPT_OFFSET:
1389                 return -os_2.ySubscriptYOffset * fontSize / head.unitsPerEm;
1390             case SUPERSCRIPT_SIZE:
1391                 return os_2.ySuperscriptYSize * fontSize / head.unitsPerEm;
1392             case SUPERSCRIPT_OFFSET:
1393                 return os_2.ySuperscriptYOffset * fontSize / head.unitsPerEm;
1394         }
1395         return 0;
1396     }
1397     
1398     /** Gets the glyph index and metrics for a character.
1399      * @param c the character
1400      * @return an <CODE>int</CODE> array with {glyph index, width}
1401      */
    
1402     public int[] getMetricsTT(int c) {
1403         if (cmapExt != null)
1404             return (int[])cmapExt.get(new Integer(c));
1405         if (!fontSpecific && cmap31 != null
1406             return (int[])cmap31.get(new Integer(c));
1407         if (fontSpecific && cmap10 != null
1408             return (int[])cmap10.get(new Integer(c));
1409         if (cmap31 != null
1410             return (int[])cmap31.get(new Integer(c));
1411         if (cmap10 != null
1412             return (int[])cmap10.get(new Integer(c));
1413         return null;
1414     }
1415
1416     /** Gets the postscript font name.
1417      * @return the postscript font name
1418      */

1419     public String getPostscriptFontName() {
1420         return fontName;
1421     }
1422
1423     /** Gets the code pages supported by the font.
1424      * @return the code pages supported by the font
1425      */

1426     public String[] getCodePagesSupported() {
1427         long cp = (((long)os_2.ulCodePageRange2) << 32) + (os_2.ulCodePageRange1 & 0xffffffffL);
1428         int count = 0;
1429         long bit = 1;
1430         for (int k = 0; k < 64; ++k) {
1431             if ((cp & bit) != 0 && codePages[k] != null)
1432                 ++count;
1433             bit <<= 1;
1434         }
1435         String ret[] = new String[count];
1436         count = 0;
1437         bit = 1;
1438         for (int k = 0; k < 64; ++k) {
1439             if ((cp & bit) != 0 && codePages[k] != null)
1440                 ret[count++] = codePages[k];
1441             bit <<= 1;
1442         }
1443         return ret;
1444     }
1445     
1446     /** Gets the full name of the font. If it is a True Type font
1447      * each array element will have {Platform ID, Platform Encoding ID,
1448      * Language ID, font name}. The interpretation of this values can be
1449      * found in the Open Type specification, chapter 2, in the 'name' table.<br>
1450      * For the other fonts the array has a single element with {"""""",
1451      * font name}.
1452      * @return the full name of the font
1453      */

1454     public String[][] getFullFontName() {
1455         return fullName;
1456     }
1457     
1458     /** Gets all the entries of the Names-Table. If it is a True Type font
1459      * each array element will have {Name ID, Platform ID, Platform Encoding ID,
1460      * Language ID, font name}. The interpretation of this values can be
1461      * found in the Open Type specification, chapter 2, in the 'name' table.<br>
1462      * For the other fonts the array has a single element with {"""""",
1463      * font name}.
1464      * @return the full name of the font
1465      */

1466     public String[][] getAllNameEntries() {
1467         return allNameEntries;
1468     }
1469     
1470     /** Gets the family name of the font. If it is a True Type font
1471      * each array element will have {Platform ID, Platform Encoding ID,
1472      * Language ID, font name}. The interpretation of this values can be
1473      * found in the Open Type specification, chapter 2, in the 'name' table.<br>
1474      * For the other fonts the array has a single element with {"""""",
1475      * font name}.
1476      * @return the family name of the font
1477      */

1478     public String[][] getFamilyFontName() {
1479         return familyName;
1480     }
1481     
1482     /** Checks if the font has any kerning pairs.
1483      * @return <CODE>true</CODE> if the font has any kerning pairs
1484      */
    
1485     public boolean hasKernPairs() {
1486         return kerning.size() > 0;
1487     }    
1488     
1489     /**
1490      * Sets the font name that will appear in the pdf font dictionary.
1491      * Use with care as it can easily make a font unreadable if not embedded.
1492      * @param name the new font name
1493      */
    
1494     public void setPostscriptFontName(String name) {
1495         fontName = name;
1496     }
1497     
1498     /**
1499      * Sets the kerning between two Unicode chars.
1500      * @param char1 the first char
1501      * @param char2 the second char
1502      * @param kern the kerning to apply in normalized 1000 units
1503      * @return <code>true</code> if the kerning was applied, <code>false</code> otherwise
1504      */

1505     public boolean setKerning(int char1, int char2, int kern) {
1506         int metrics[] = getMetricsTT(char1);
1507         if (metrics == null)
1508             return false;
1509         int c1 = metrics[0];
1510         metrics = getMetricsTT(char2);
1511         if (metrics == null)
1512             return false;
1513         int c2 = metrics[0];
1514         kerning.put((c1 << 16) + c2, kern);
1515         return true;
1516     }
1517     
1518     protected int[] getRawCharBBox(int c, String name) {
1519         HashMap map = null;
1520         if (name == null || cmap31 == null)
1521             map = cmap10;
1522         else
1523             map = cmap31;
1524         if (map == null)
1525             return null;
1526         int metric[] = (int[])map.get(new Integer(c));
1527         if (metric == null || bboxes == null)
1528             return null;
1529         return bboxes[metric[0]];
1530     }
1531 }