001/*
002 * $Id: TrueTypeFont.java 4784 2011-03-15 08:33:00Z blowagie $
003 *
004 * This file is part of the iText (R) project.
005 * Copyright (c) 1998-2011 1T3XT BVBA
006 * Authors: Bruno Lowagie, Paulo Soares, et al.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU Affero General Public License version 3
010 * as published by the Free Software Foundation with the addition of the
011 * following permission added to Section 15 as permitted in Section 7(a):
012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
014 *
015 * This program is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017 * or FITNESS FOR A PARTICULAR PURPOSE.
018 * See the GNU Affero General Public License for more details.
019 * You should have received a copy of the GNU Affero General Public License
020 * along with this program; if not, see http://www.gnu.org/licenses or write to
021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
022 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
023 * http://itextpdf.com/terms-of-use/
024 *
025 * The interactive user interfaces in modified source and object code versions
026 * of this program must display Appropriate Legal Notices, as required under
027 * Section 5 of the GNU Affero General Public License.
028 *
029 * In accordance with Section 7(b) of the GNU Affero General Public License,
030 * a covered work must retain the producer line in every PDF that is created
031 * or manipulated using iText.
032 *
033 * You can be released from the requirements of the license by purchasing
034 * a commercial license. Buying such a license is mandatory as soon as you
035 * develop commercial activities involving the iText software without
036 * disclosing the source code of your own applications.
037 * These activities include: offering paid services to customers as an ASP,
038 * serving PDFs on the fly in a web application, shipping iText with a closed
039 * source product.
040 *
041 * For more information, please contact iText Software Corp. at this
042 * address: sales@itextpdf.com
043 */
044package com.itextpdf.text.pdf;
045
046import java.io.File;
047import java.io.IOException;
048import java.util.ArrayList;
049import java.util.HashMap;
050import java.util.HashSet;
051import java.util.Map;
052
053import com.itextpdf.text.Document;
054import com.itextpdf.text.DocumentException;
055import com.itextpdf.text.ExceptionConverter;
056import com.itextpdf.text.error_messages.MessageLocalization;
057
058/** Reads a Truetype font
059 *
060 * @author Paulo Soares
061 */
062class TrueTypeFont extends BaseFont {
063
064    /** The code pages possible for a True Type font.
065     */
066    static final String codePages[] = {
067        "1252 Latin 1",
068        "1250 Latin 2: Eastern Europe",
069        "1251 Cyrillic",
070        "1253 Greek",
071        "1254 Turkish",
072        "1255 Hebrew",
073        "1256 Arabic",
074        "1257 Windows Baltic",
075        "1258 Vietnamese",
076        null,
077        null,
078        null,
079        null,
080        null,
081        null,
082        null,
083        "874 Thai",
084        "932 JIS/Japan",
085        "936 Chinese: Simplified chars--PRC and Singapore",
086        "949 Korean Wansung",
087        "950 Chinese: Traditional chars--Taiwan and Hong Kong",
088        "1361 Korean Johab",
089        null,
090        null,
091        null,
092        null,
093        null,
094        null,
095        null,
096        "Macintosh Character Set (US Roman)",
097        "OEM Character Set",
098        "Symbol Character Set",
099        null,
100        null,
101        null,
102        null,
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        "869 IBM Greek",
116        "866 MS-DOS Russian",
117        "865 MS-DOS Nordic",
118        "864 Arabic",
119        "863 MS-DOS Canadian French",
120        "862 Hebrew",
121        "861 MS-DOS Icelandic",
122        "860 MS-DOS Portuguese",
123        "857 IBM Turkish",
124        "855 IBM Cyrillic; primarily Russian",
125        "852 Latin 2",
126        "775 MS-DOS Baltic",
127        "737 Greek; former 437 G",
128        "708 Arabic; ASMO 708",
129        "850 WE/Latin 1",
130        "437 US"};
131
132    protected boolean justNames = false;
133    /** Contains the location of the several tables. The key is the name of
134     * the table and the value is an <CODE>int[2]</CODE> where position 0
135     * is the offset from the start of the file and position 1 is the length
136     * of the table.
137     */
138    protected HashMap<String, int[]> tables;
139    /** The file in use.
140     */
141    protected RandomAccessFileOrArray rf;
142    /** The file name.
143     */
144    protected String fileName;
145
146    protected boolean cff = false;
147
148    protected int cffOffset;
149
150    protected int cffLength;
151
152    /** The offset from the start of the file to the table directory.
153     * It is 0 for TTF and may vary for TTC depending on the chosen font.
154     */
155    protected int directoryOffset;
156    /** The index for the TTC font. It is an empty <CODE>String</CODE> for a
157     * TTF file.
158     */
159    protected String ttcIndex;
160    /** The style modifier */
161    protected String style = "";
162    /** The content of table 'head'.
163     */
164    protected FontHeader head = new FontHeader();
165    /** The content of table 'hhea'.
166     */
167    protected HorizontalHeader hhea = new HorizontalHeader();
168    /** The content of table 'OS/2'.
169     */
170    protected WindowsMetrics os_2 = new WindowsMetrics();
171    /** The width of the glyphs. This is essentially the content of table
172     * 'hmtx' normalized to 1000 units.
173     */
174    protected int GlyphWidths[];
175
176    protected int bboxes[][];
177    /** The map containing the code information for the table 'cmap', encoding 1.0.
178     * The key is the code and the value is an <CODE>int[2]</CODE> where position 0
179     * is the glyph number and position 1 is the glyph width normalized to 1000
180     * units.
181     */
182    protected HashMap<Integer, int[]> cmap10;
183    /** The map containing the code information for the table 'cmap', encoding 3.1
184     * in Unicode.
185     * <P>
186     * The key is the code and the value is an <CODE>int</CODE>[2] where position 0
187     * is the glyph number and position 1 is the glyph width normalized to 1000
188     * units.
189     */
190    protected HashMap<Integer, int[]> cmap31;
191
192    protected HashMap<Integer, int[]> cmapExt;
193
194    /** The map containing the kerning information. It represents the content of
195     * table 'kern'. The key is an <CODE>Integer</CODE> where the top 16 bits
196     * are the glyph number for the first character and the lower 16 bits are the
197     * glyph number for the second character. The value is the amount of kerning in
198     * normalized 1000 units as an <CODE>Integer</CODE>. This value is usually negative.
199     */
200    protected IntHashtable kerning = new IntHashtable();
201    /**
202     * The font name.
203     * This name is usually extracted from the table 'name' with
204     * the 'Name ID' 6.
205     */
206    protected String fontName;
207
208    /** The full name of the font
209     */
210    protected String fullName[][];
211
212    /** All the names of the Names-Table
213     */
214    protected String allNameEntries[][];
215
216    /** The family name of the font
217     */
218    protected String familyName[][];
219    /** The italic angle. It is usually extracted from the 'post' table or in it's
220     * absence with the code:
221     * <P>
222     * <PRE>
223     * -Math.atan2(hhea.caretSlopeRun, hhea.caretSlopeRise) * 180 / Math.PI
224     * </PRE>
225     */
226    protected double italicAngle;
227    /** <CODE>true</CODE> if all the glyphs have the same width.
228     */
229    protected boolean isFixedPitch = false;
230
231    protected int underlinePosition;
232
233    protected int underlineThickness;
234
235    /** The components of table 'head'.
236     */
237    protected static class FontHeader {
238        /** A variable. */
239        int flags;
240        /** A variable. */
241        int unitsPerEm;
242        /** A variable. */
243        short xMin;
244        /** A variable. */
245        short yMin;
246        /** A variable. */
247        short xMax;
248        /** A variable. */
249        short yMax;
250        /** A variable. */
251        int macStyle;
252    }
253
254    /** The components of table 'hhea'.
255     */
256    protected static class HorizontalHeader {
257        /** A variable. */
258        short Ascender;
259        /** A variable. */
260        short Descender;
261        /** A variable. */
262        short LineGap;
263        /** A variable. */
264        int advanceWidthMax;
265        /** A variable. */
266        short minLeftSideBearing;
267        /** A variable. */
268        short minRightSideBearing;
269        /** A variable. */
270        short xMaxExtent;
271        /** A variable. */
272        short caretSlopeRise;
273        /** A variable. */
274        short caretSlopeRun;
275        /** A variable. */
276        int numberOfHMetrics;
277    }
278
279    /** The components of table 'OS/2'.
280     */
281    protected static class WindowsMetrics {
282        /** A variable. */
283        short xAvgCharWidth;
284        /** A variable. */
285        int usWeightClass;
286        /** A variable. */
287        int usWidthClass;
288        /** A variable. */
289        short fsType;
290        /** A variable. */
291        short ySubscriptXSize;
292        /** A variable. */
293        short ySubscriptYSize;
294        /** A variable. */
295        short ySubscriptXOffset;
296        /** A variable. */
297        short ySubscriptYOffset;
298        /** A variable. */
299        short ySuperscriptXSize;
300        /** A variable. */
301        short ySuperscriptYSize;
302        /** A variable. */
303        short ySuperscriptXOffset;
304        /** A variable. */
305        short ySuperscriptYOffset;
306        /** A variable. */
307        short yStrikeoutSize;
308        /** A variable. */
309        short yStrikeoutPosition;
310        /** A variable. */
311        short sFamilyClass;
312        /** A variable. */
313        byte panose[] = new byte[10];
314        /** A variable. */
315        byte achVendID[] = new byte[4];
316        /** A variable. */
317        int fsSelection;
318        /** A variable. */
319        int usFirstCharIndex;
320        /** A variable. */
321        int usLastCharIndex;
322        /** A variable. */
323        short sTypoAscender;
324        /** A variable. */
325        short sTypoDescender;
326        /** A variable. */
327        short sTypoLineGap;
328        /** A variable. */
329        int usWinAscent;
330        /** A variable. */
331        int usWinDescent;
332        /** A variable. */
333        int ulCodePageRange1;
334        /** A variable. */
335        int ulCodePageRange2;
336        /** A variable. */
337        int sCapHeight;
338    }
339
340    /** This constructor is present to allow extending the class.
341     */
342    protected TrueTypeFont() {
343    }
344
345    /** Creates a new TrueType font.
346     * @param ttFile the location of the font on file. The file must end in '.ttf' or
347     * '.ttc' but can have modifiers after the name
348     * @param enc the encoding to be applied to this font
349     * @param emb true if the font is to be embedded in the PDF
350     * @param ttfAfm the font as a <CODE>byte</CODE> array
351     * @throws DocumentException the font is invalid
352     * @throws IOException the font file could not be read
353     * @since   2.1.5
354     */
355    TrueTypeFont(String ttFile, String enc, boolean emb, byte ttfAfm[], boolean justNames, boolean forceRead) throws DocumentException, IOException {
356        this.justNames = justNames;
357        String nameBase = getBaseName(ttFile);
358        String ttcName = getTTCName(nameBase);
359        if (nameBase.length() < ttFile.length()) {
360            style = ttFile.substring(nameBase.length());
361        }
362        encoding = enc;
363        embedded = emb;
364        fileName = ttcName;
365        fontType = FONT_TYPE_TT;
366        ttcIndex = "";
367        if (ttcName.length() < nameBase.length())
368            ttcIndex = nameBase.substring(ttcName.length() + 1);
369        if (fileName.toLowerCase().endsWith(".ttf") || fileName.toLowerCase().endsWith(".otf") || fileName.toLowerCase().endsWith(".ttc")) {
370            process(ttfAfm, forceRead);
371            if (!justNames && embedded && os_2.fsType == 2)
372                throw new DocumentException(MessageLocalization.getComposedMessage("1.cannot.be.embedded.due.to.licensing.restrictions", fileName + style));
373        }
374        else
375            throw new DocumentException(MessageLocalization.getComposedMessage("1.is.not.a.ttf.otf.or.ttc.font.file", fileName + style));
376        if (!encoding.startsWith("#"))
377            PdfEncodings.convertToBytes(" ", enc); // check if the encoding exists
378        createEncoding();
379    }
380
381    /** Gets the name from a composed TTC file name.
382     * If I have for input "myfont.ttc,2" the return will
383     * be "myfont.ttc".
384     * @param name the full name
385     * @return the simple file name
386     */
387    protected static String getTTCName(String name) {
388        int idx = name.toLowerCase().indexOf(".ttc,");
389        if (idx < 0)
390            return name;
391        else
392            return name.substring(0, idx + 4);
393    }
394
395
396    /**
397     * Reads the tables 'head', 'hhea', 'OS/2' and 'post' filling several variables.
398     * @throws DocumentException the font is invalid
399     * @throws IOException the font file could not be read
400     */
401    void fillTables() throws DocumentException, IOException {
402        int table_location[];
403        table_location = tables.get("head");
404        if (table_location == null)
405            throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "head", fileName + style));
406        rf.seek(table_location[0] + 16);
407        head.flags = rf.readUnsignedShort();
408        head.unitsPerEm = rf.readUnsignedShort();
409        rf.skipBytes(16);
410        head.xMin = rf.readShort();
411        head.yMin = rf.readShort();
412        head.xMax = rf.readShort();
413        head.yMax = rf.readShort();
414        head.macStyle = rf.readUnsignedShort();
415
416        table_location = tables.get("hhea");
417        if (table_location == null)
418            throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "hhea", fileName + style));
419        rf.seek(table_location[0] + 4);
420        hhea.Ascender = rf.readShort();
421        hhea.Descender = rf.readShort();
422        hhea.LineGap = rf.readShort();
423        hhea.advanceWidthMax = rf.readUnsignedShort();
424        hhea.minLeftSideBearing = rf.readShort();
425        hhea.minRightSideBearing = rf.readShort();
426        hhea.xMaxExtent = rf.readShort();
427        hhea.caretSlopeRise = rf.readShort();
428        hhea.caretSlopeRun = rf.readShort();
429        rf.skipBytes(12);
430        hhea.numberOfHMetrics = rf.readUnsignedShort();
431
432        table_location = tables.get("OS/2");
433        if (table_location == null)
434            throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "OS/2", fileName + style));
435        rf.seek(table_location[0]);
436        int version = rf.readUnsignedShort();
437        os_2.xAvgCharWidth = rf.readShort();
438        os_2.usWeightClass = rf.readUnsignedShort();
439        os_2.usWidthClass = rf.readUnsignedShort();
440        os_2.fsType = rf.readShort();
441        os_2.ySubscriptXSize = rf.readShort();
442        os_2.ySubscriptYSize = rf.readShort();
443        os_2.ySubscriptXOffset = rf.readShort();
444        os_2.ySubscriptYOffset = rf.readShort();
445        os_2.ySuperscriptXSize = rf.readShort();
446        os_2.ySuperscriptYSize = rf.readShort();
447        os_2.ySuperscriptXOffset = rf.readShort();
448        os_2.ySuperscriptYOffset = rf.readShort();
449        os_2.yStrikeoutSize = rf.readShort();
450        os_2.yStrikeoutPosition = rf.readShort();
451        os_2.sFamilyClass = rf.readShort();
452        rf.readFully(os_2.panose);
453        rf.skipBytes(16);
454        rf.readFully(os_2.achVendID);
455        os_2.fsSelection = rf.readUnsignedShort();
456        os_2.usFirstCharIndex = rf.readUnsignedShort();
457        os_2.usLastCharIndex = rf.readUnsignedShort();
458        os_2.sTypoAscender = rf.readShort();
459        os_2.sTypoDescender = rf.readShort();
460        if (os_2.sTypoDescender > 0)
461            os_2.sTypoDescender = (short)-os_2.sTypoDescender;
462        os_2.sTypoLineGap = rf.readShort();
463        os_2.usWinAscent = rf.readUnsignedShort();
464        os_2.usWinDescent = rf.readUnsignedShort();
465        os_2.ulCodePageRange1 = 0;
466        os_2.ulCodePageRange2 = 0;
467        if (version > 0) {
468            os_2.ulCodePageRange1 = rf.readInt();
469            os_2.ulCodePageRange2 = rf.readInt();
470        }
471        if (version > 1) {
472            rf.skipBytes(2);
473            os_2.sCapHeight = rf.readShort();
474        }
475        else
476            os_2.sCapHeight = (int)(0.7 * head.unitsPerEm);
477
478        table_location = tables.get("post");
479        if (table_location == null) {
480            italicAngle = -Math.atan2(hhea.caretSlopeRun, hhea.caretSlopeRise) * 180 / Math.PI;
481            return;
482        }
483        rf.seek(table_location[0] + 4);
484        short mantissa = rf.readShort();
485        int fraction = rf.readUnsignedShort();
486        italicAngle = mantissa + fraction / 16384.0d;
487        underlinePosition = rf.readShort();
488        underlineThickness = rf.readShort();
489        isFixedPitch = rf.readInt() != 0;
490    }
491
492    /**
493     * Gets the Postscript font name.
494     * @throws DocumentException the font is invalid
495     * @throws IOException the font file could not be read
496     * @return the Postscript font name
497     */
498    String getBaseFont() throws DocumentException, IOException {
499        int table_location[];
500        table_location = tables.get("name");
501        if (table_location == null)
502            throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "name", fileName + style));
503        rf.seek(table_location[0] + 2);
504        int numRecords = rf.readUnsignedShort();
505        int startOfStorage = rf.readUnsignedShort();
506        for (int k = 0; k < numRecords; ++k) {
507            int platformID = rf.readUnsignedShort();
508            int platformEncodingID = rf.readUnsignedShort();
509            int languageID = rf.readUnsignedShort();
510            int nameID = rf.readUnsignedShort();
511            int length = rf.readUnsignedShort();
512            int offset = rf.readUnsignedShort();
513            if (nameID == 6) {
514                rf.seek(table_location[0] + startOfStorage + offset);
515                if (platformID == 0 || platformID == 3)
516                    return readUnicodeString(length);
517                else
518                    return readStandardString(length);
519            }
520        }
521        File file = new File(fileName);
522        return file.getName().replace(' ', '-');
523    }
524
525    /** Extracts the names of the font in all the languages available.
526     * @param id the name id to retrieve
527     * @throws DocumentException on error
528     * @throws IOException on error
529     */
530    String[][] getNames(int id) throws DocumentException, IOException {
531        int table_location[];
532        table_location = tables.get("name");
533        if (table_location == null)
534            throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "name", fileName + style));
535        rf.seek(table_location[0] + 2);
536        int numRecords = rf.readUnsignedShort();
537        int startOfStorage = rf.readUnsignedShort();
538        ArrayList<String[]> names = new ArrayList<String[]>();
539        for (int k = 0; k < numRecords; ++k) {
540            int platformID = rf.readUnsignedShort();
541            int platformEncodingID = rf.readUnsignedShort();
542            int languageID = rf.readUnsignedShort();
543            int nameID = rf.readUnsignedShort();
544            int length = rf.readUnsignedShort();
545            int offset = rf.readUnsignedShort();
546            if (nameID == id) {
547                int pos = rf.getFilePointer();
548                rf.seek(table_location[0] + startOfStorage + offset);
549                String name;
550                if (platformID == 0 || platformID == 3 || platformID == 2 && platformEncodingID == 1){
551                    name = readUnicodeString(length);
552                }
553                else {
554                    name = readStandardString(length);
555                }
556                names.add(new String[]{String.valueOf(platformID),
557                    String.valueOf(platformEncodingID), String.valueOf(languageID), name});
558                rf.seek(pos);
559            }
560        }
561        String thisName[][] = new String[names.size()][];
562        for (int k = 0; k < names.size(); ++k)
563            thisName[k] = names.get(k);
564        return thisName;
565    }
566
567    /** Extracts all the names of the names-Table
568     * @throws DocumentException on error
569     * @throws IOException on error
570     */
571    String[][] getAllNames() throws DocumentException, IOException {
572        int table_location[];
573        table_location = tables.get("name");
574        if (table_location == null)
575            throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "name", fileName + style));
576        rf.seek(table_location[0] + 2);
577        int numRecords = rf.readUnsignedShort();
578        int startOfStorage = rf.readUnsignedShort();
579        ArrayList<String[]> names = new ArrayList<String[]>();
580        for (int k = 0; k < numRecords; ++k) {
581            int platformID = rf.readUnsignedShort();
582            int platformEncodingID = rf.readUnsignedShort();
583            int languageID = rf.readUnsignedShort();
584            int nameID = rf.readUnsignedShort();
585            int length = rf.readUnsignedShort();
586            int offset = rf.readUnsignedShort();
587            int pos = rf.getFilePointer();
588            rf.seek(table_location[0] + startOfStorage + offset);
589            String name;
590            if (platformID == 0 || platformID == 3 || platformID == 2 && platformEncodingID == 1){
591                name = readUnicodeString(length);
592            }
593            else {
594                name = readStandardString(length);
595            }
596            names.add(new String[]{String.valueOf(nameID), String.valueOf(platformID),
597                    String.valueOf(platformEncodingID), String.valueOf(languageID), name});
598            rf.seek(pos);
599        }
600        String thisName[][] = new String[names.size()][];
601        for (int k = 0; k < names.size(); ++k)
602            thisName[k] = names.get(k);
603        return thisName;
604    }
605
606    void checkCff() {
607        int table_location[];
608        table_location = tables.get("CFF ");
609        if (table_location != null) {
610            cff = true;
611            cffOffset = table_location[0];
612            cffLength = table_location[1];
613        }
614    }
615
616    /** Reads the font data.
617     * @param ttfAfm the font as a <CODE>byte</CODE> array, possibly <CODE>null</CODE>
618     * @throws DocumentException the font is invalid
619     * @throws IOException the font file could not be read
620     * @since   2.1.5
621     */
622    void process(byte ttfAfm[], boolean preload) throws DocumentException, IOException {
623        tables = new HashMap<String, int[]>();
624
625        try {
626            if (ttfAfm == null)
627                rf = new RandomAccessFileOrArray(fileName, preload, Document.plainRandomAccess);
628            else
629                rf = new RandomAccessFileOrArray(ttfAfm);
630            if (ttcIndex.length() > 0) {
631                int dirIdx = Integer.parseInt(ttcIndex);
632                if (dirIdx < 0)
633                    throw new DocumentException(MessageLocalization.getComposedMessage("the.font.index.for.1.must.be.positive", fileName));
634                String mainTag = readStandardString(4);
635                if (!mainTag.equals("ttcf"))
636                    throw new DocumentException(MessageLocalization.getComposedMessage("1.is.not.a.valid.ttc.file", fileName));
637                rf.skipBytes(4);
638                int dirCount = rf.readInt();
639                if (dirIdx >= dirCount)
640                    throw new DocumentException(MessageLocalization.getComposedMessage("the.font.index.for.1.must.be.between.0.and.2.it.was.3", fileName, String.valueOf(dirCount - 1), String.valueOf(dirIdx)));
641                rf.skipBytes(dirIdx * 4);
642                directoryOffset = rf.readInt();
643            }
644            rf.seek(directoryOffset);
645            int ttId = rf.readInt();
646            if (ttId != 0x00010000 && ttId != 0x4F54544F)
647                throw new DocumentException(MessageLocalization.getComposedMessage("1.is.not.a.valid.ttf.or.otf.file", fileName));
648            int num_tables = rf.readUnsignedShort();
649            rf.skipBytes(6);
650            for (int k = 0; k < num_tables; ++k) {
651                String tag = readStandardString(4);
652                rf.skipBytes(4);
653                int table_location[] = new int[2];
654                table_location[0] = rf.readInt();
655                table_location[1] = rf.readInt();
656                tables.put(tag, table_location);
657            }
658            checkCff();
659            fontName = getBaseFont();
660            fullName = getNames(4); //full name
661            familyName = getNames(1); //family name
662            allNameEntries = getAllNames();
663            if (!justNames) {
664                fillTables();
665                readGlyphWidths();
666                readCMaps();
667                readKerning();
668                readBbox();
669                GlyphWidths = null;
670            }
671        }
672        finally {
673            if (rf != null) {
674                rf.close();
675                if (!embedded)
676                    rf = null;
677            }
678        }
679    }
680
681    /** Reads a <CODE>String</CODE> from the font file as bytes using the Cp1252
682     *  encoding.
683     * @param length the length of bytes to read
684     * @return the <CODE>String</CODE> read
685     * @throws IOException the font file could not be read
686     */
687    protected String readStandardString(int length) throws IOException {
688        byte buf[] = new byte[length];
689        rf.readFully(buf);
690        try {
691            return new String(buf, WINANSI);
692        }
693        catch (Exception e) {
694            throw new ExceptionConverter(e);
695        }
696    }
697
698    /** Reads a Unicode <CODE>String</CODE> from the font file. Each character is
699     *  represented by two bytes.
700     * @param length the length of bytes to read. The <CODE>String</CODE> will have <CODE>length</CODE>/2
701     * characters
702     * @return the <CODE>String</CODE> read
703     * @throws IOException the font file could not be read
704     */
705    protected String readUnicodeString(int length) throws IOException {
706        StringBuffer buf = new StringBuffer();
707        length /= 2;
708        for (int k = 0; k < length; ++k) {
709            buf.append(rf.readChar());
710        }
711        return buf.toString();
712    }
713
714    /** Reads the glyphs widths. The widths are extracted from the table 'hmtx'.
715     *  The glyphs are normalized to 1000 units.
716     * @throws DocumentException the font is invalid
717     * @throws IOException the font file could not be read
718     */
719    protected void readGlyphWidths() throws DocumentException, IOException {
720        int table_location[];
721        table_location = tables.get("hmtx");
722        if (table_location == null)
723            throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "hmtx", fileName + style));
724        rf.seek(table_location[0]);
725        GlyphWidths = new int[hhea.numberOfHMetrics];
726        for (int k = 0; k < hhea.numberOfHMetrics; ++k) {
727            GlyphWidths[k] = rf.readUnsignedShort() * 1000 / head.unitsPerEm;
728            rf.readUnsignedShort();
729        }
730    }
731
732    /** Gets a glyph width.
733     * @param glyph the glyph to get the width of
734     * @return the width of the glyph in normalized 1000 units
735     */
736    protected int getGlyphWidth(int glyph) {
737        if (glyph >= GlyphWidths.length)
738            glyph = GlyphWidths.length - 1;
739        return GlyphWidths[glyph];
740    }
741
742    private void readBbox() throws DocumentException, IOException {
743        int tableLocation[];
744        tableLocation = tables.get("head");
745        if (tableLocation == null)
746            throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "head", fileName + style));
747        rf.seek(tableLocation[0] + TrueTypeFontSubSet.HEAD_LOCA_FORMAT_OFFSET);
748        boolean locaShortTable = rf.readUnsignedShort() == 0;
749        tableLocation = tables.get("loca");
750        if (tableLocation == null)
751            return;
752        rf.seek(tableLocation[0]);
753        int locaTable[];
754        if (locaShortTable) {
755            int entries = tableLocation[1] / 2;
756            locaTable = new int[entries];
757            for (int k = 0; k < entries; ++k)
758                locaTable[k] = rf.readUnsignedShort() * 2;
759        }
760        else {
761            int entries = tableLocation[1] / 4;
762            locaTable = new int[entries];
763            for (int k = 0; k < entries; ++k)
764                locaTable[k] = rf.readInt();
765        }
766        tableLocation = tables.get("glyf");
767        if (tableLocation == null)
768            throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "glyf", fileName + style));
769        int tableGlyphOffset = tableLocation[0];
770        bboxes = new int[locaTable.length - 1][];
771        for (int glyph = 0; glyph < locaTable.length - 1; ++glyph) {
772            int start = locaTable[glyph];
773            if (start != locaTable[glyph + 1]) {
774                rf.seek(tableGlyphOffset + start + 2);
775                bboxes[glyph] = new int[]{
776                    rf.readShort() * 1000 / head.unitsPerEm,
777                    rf.readShort() * 1000 / head.unitsPerEm,
778                    rf.readShort() * 1000 / head.unitsPerEm,
779                    rf.readShort() * 1000 / head.unitsPerEm};
780            }
781        }
782    }
783
784    /** Reads the several maps from the table 'cmap'. The maps of interest are 1.0 for symbolic
785     *  fonts and 3.1 for all others. A symbolic font is defined as having the map 3.0.
786     * @throws DocumentException the font is invalid
787     * @throws IOException the font file could not be read
788     */
789    void readCMaps() throws DocumentException, IOException {
790        int table_location[];
791        table_location = tables.get("cmap");
792        if (table_location == null)
793            throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "cmap", fileName + style));
794        rf.seek(table_location[0]);
795        rf.skipBytes(2);
796        int num_tables = rf.readUnsignedShort();
797        fontSpecific = false;
798        int map10 = 0;
799        int map31 = 0;
800        int map30 = 0;
801        int mapExt = 0;
802        for (int k = 0; k < num_tables; ++k) {
803            int platId = rf.readUnsignedShort();
804            int platSpecId = rf.readUnsignedShort();
805            int offset = rf.readInt();
806            if (platId == 3 && platSpecId == 0) {
807                fontSpecific = true;
808                map30 = offset;
809            }
810            else if (platId == 3 && platSpecId == 1) {
811                map31 = offset;
812            }
813            else if (platId == 3 && platSpecId == 10) {
814                mapExt = offset;
815            }
816            if (platId == 1 && platSpecId == 0) {
817                map10 = offset;
818            }
819        }
820        if (map10 > 0) {
821            rf.seek(table_location[0] + map10);
822            int format = rf.readUnsignedShort();
823            switch (format) {
824                case 0:
825                    cmap10 = readFormat0();
826                    break;
827                case 4:
828                    cmap10 = readFormat4();
829                    break;
830                case 6:
831                    cmap10 = readFormat6();
832                    break;
833            }
834        }
835        if (map31 > 0) {
836            rf.seek(table_location[0] + map31);
837            int format = rf.readUnsignedShort();
838            if (format == 4) {
839                cmap31 = readFormat4();
840            }
841        }
842        if (map30 > 0) {
843            rf.seek(table_location[0] + map30);
844            int format = rf.readUnsignedShort();
845            if (format == 4) {
846                cmap10 = readFormat4();
847            }
848        }
849        if (mapExt > 0) {
850            rf.seek(table_location[0] + mapExt);
851            int format = rf.readUnsignedShort();
852            switch (format) {
853                case 0:
854                    cmapExt = readFormat0();
855                    break;
856                case 4:
857                    cmapExt = readFormat4();
858                    break;
859                case 6:
860                    cmapExt = readFormat6();
861                    break;
862                case 12:
863                    cmapExt = readFormat12();
864                    break;
865            }
866        }
867    }
868
869    HashMap<Integer, int[]> readFormat12() throws IOException {
870        HashMap<Integer, int[]> h = new HashMap<Integer, int[]>();
871        rf.skipBytes(2);
872        int table_lenght = rf.readInt();
873        rf.skipBytes(4);
874        int nGroups = rf.readInt();
875        for (int k = 0; k < nGroups; k++) {
876            int startCharCode = rf.readInt();
877            int endCharCode = rf.readInt();
878            int startGlyphID = rf.readInt();
879            for (int i = startCharCode; i <= endCharCode; i++) {
880                int[] r = new int[2];
881                r[0] = startGlyphID;
882                r[1] = getGlyphWidth(r[0]);
883                h.put(Integer.valueOf(i), r);
884                startGlyphID++;
885            }
886        }
887        return h;
888    }
889
890    /** The information in the maps of the table 'cmap' is coded in several formats.
891     *  Format 0 is the Apple standard character to glyph index mapping table.
892     * @return a <CODE>HashMap</CODE> representing this map
893     * @throws IOException the font file could not be read
894     */
895    HashMap<Integer, int[]> readFormat0() throws IOException {
896        HashMap<Integer, int[]> h = new HashMap<Integer, int[]>();
897        rf.skipBytes(4);
898        for (int k = 0; k < 256; ++k) {
899            int r[] = new int[2];
900            r[0] = rf.readUnsignedByte();
901            r[1] = getGlyphWidth(r[0]);
902            h.put(Integer.valueOf(k), r);
903        }
904        return h;
905    }
906
907    /** The information in the maps of the table 'cmap' is coded in several formats.
908     *  Format 4 is the Microsoft standard character to glyph index mapping table.
909     * @return a <CODE>HashMap</CODE> representing this map
910     * @throws IOException the font file could not be read
911     */
912    HashMap<Integer, int[]> readFormat4() throws IOException {
913        HashMap<Integer, int[]> h = new HashMap<Integer, int[]>();
914        int table_lenght = rf.readUnsignedShort();
915        rf.skipBytes(2);
916        int segCount = rf.readUnsignedShort() / 2;
917        rf.skipBytes(6);
918        int endCount[] = new int[segCount];
919        for (int k = 0; k < segCount; ++k) {
920            endCount[k] = rf.readUnsignedShort();
921        }
922        rf.skipBytes(2);
923        int startCount[] = new int[segCount];
924        for (int k = 0; k < segCount; ++k) {
925            startCount[k] = rf.readUnsignedShort();
926        }
927        int idDelta[] = new int[segCount];
928        for (int k = 0; k < segCount; ++k) {
929            idDelta[k] = rf.readUnsignedShort();
930        }
931        int idRO[] = new int[segCount];
932        for (int k = 0; k < segCount; ++k) {
933            idRO[k] = rf.readUnsignedShort();
934        }
935        int glyphId[] = new int[table_lenght / 2 - 8 - segCount * 4];
936        for (int k = 0; k < glyphId.length; ++k) {
937            glyphId[k] = rf.readUnsignedShort();
938        }
939        for (int k = 0; k < segCount; ++k) {
940            int glyph;
941            for (int j = startCount[k]; j <= endCount[k] && j != 0xFFFF; ++j) {
942                if (idRO[k] == 0) {
943                    glyph = j + idDelta[k] & 0xFFFF;
944                }
945                else {
946                    int idx = k + idRO[k] / 2 - segCount + j - startCount[k];
947                    if (idx >= glyphId.length)
948                        continue;
949                    glyph = glyphId[idx] + idDelta[k] & 0xFFFF;
950                }
951                int r[] = new int[2];
952                r[0] = glyph;
953                r[1] = getGlyphWidth(r[0]);
954                h.put(Integer.valueOf(fontSpecific ? ((j & 0xff00) == 0xf000 ? j & 0xff : j) : j), r);
955            }
956        }
957        return h;
958    }
959
960    /** The information in the maps of the table 'cmap' is coded in several formats.
961     *  Format 6 is a trimmed table mapping. It is similar to format 0 but can have
962     *  less than 256 entries.
963     * @return a <CODE>HashMap</CODE> representing this map
964     * @throws IOException the font file could not be read
965     */
966    HashMap<Integer, int[]> readFormat6() throws IOException {
967        HashMap<Integer, int[]> h = new HashMap<Integer, int[]>();
968        rf.skipBytes(4);
969        int start_code = rf.readUnsignedShort();
970        int code_count = rf.readUnsignedShort();
971        for (int k = 0; k < code_count; ++k) {
972            int r[] = new int[2];
973            r[0] = rf.readUnsignedShort();
974            r[1] = getGlyphWidth(r[0]);
975            h.put(Integer.valueOf(k + start_code), r);
976        }
977        return h;
978    }
979
980    /** Reads the kerning information from the 'kern' table.
981     * @throws IOException the font file could not be read
982     */
983    void readKerning() throws IOException {
984        int table_location[];
985        table_location = tables.get("kern");
986        if (table_location == null)
987            return;
988        rf.seek(table_location[0] + 2);
989        int nTables = rf.readUnsignedShort();
990        int checkpoint = table_location[0] + 4;
991        int length = 0;
992        for (int k = 0; k < nTables; ++k) {
993            checkpoint += length;
994            rf.seek(checkpoint);
995            rf.skipBytes(2);
996            length = rf.readUnsignedShort();
997            int coverage = rf.readUnsignedShort();
998            if ((coverage & 0xfff7) == 0x0001) {
999                int nPairs = rf.readUnsignedShort();
1000                rf.skipBytes(6);
1001                for (int j = 0; j < nPairs; ++j) {
1002                    int pair = rf.readInt();
1003                    int value = rf.readShort() * 1000 / head.unitsPerEm;
1004                    kerning.put(pair, value);
1005                }
1006            }
1007        }
1008    }
1009
1010    /** Gets the kerning between two Unicode chars.
1011     * @param char1 the first char
1012     * @param char2 the second char
1013     * @return the kerning to be applied
1014     */
1015    @Override
1016    public int getKerning(int char1, int char2) {
1017        int metrics[] = getMetricsTT(char1);
1018        if (metrics == null)
1019            return 0;
1020        int c1 = metrics[0];
1021        metrics = getMetricsTT(char2);
1022        if (metrics == null)
1023            return 0;
1024        int c2 = metrics[0];
1025        return kerning.get((c1 << 16) + c2);
1026    }
1027
1028    /** Gets the width from the font according to the unicode char <CODE>c</CODE>.
1029     * If the <CODE>name</CODE> is null it's a symbolic font.
1030     * @param c the unicode char
1031     * @param name the glyph name
1032     * @return the width of the char
1033     */
1034    @Override
1035    int getRawWidth(int c, String name) {
1036        int[] metric = getMetricsTT(c);
1037        if (metric == null)
1038            return 0;
1039        return metric[1];
1040    }
1041
1042    /** Generates the font descriptor for this font.
1043     * @return the PdfDictionary containing the font descriptor or <CODE>null</CODE>
1044     * @param subsetPrefix the subset prefix
1045     * @param fontStream the indirect reference to a PdfStream containing the font or <CODE>null</CODE>
1046     */
1047    protected PdfDictionary getFontDescriptor(PdfIndirectReference fontStream, String subsetPrefix, PdfIndirectReference cidset) {
1048        PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR);
1049        dic.put(PdfName.ASCENT, new PdfNumber(os_2.sTypoAscender * 1000 / head.unitsPerEm));
1050        dic.put(PdfName.CAPHEIGHT, new PdfNumber(os_2.sCapHeight * 1000 / head.unitsPerEm));
1051        dic.put(PdfName.DESCENT, new PdfNumber(os_2.sTypoDescender * 1000 / head.unitsPerEm));
1052        dic.put(PdfName.FONTBBOX, new PdfRectangle(
1053        head.xMin * 1000 / head.unitsPerEm,
1054        head.yMin * 1000 / head.unitsPerEm,
1055        head.xMax * 1000 / head.unitsPerEm,
1056        head.yMax * 1000 / head.unitsPerEm));
1057        if (cidset != null)
1058            dic.put(PdfName.CIDSET, cidset);
1059        if (cff) {
1060            if (encoding.startsWith("Identity-"))
1061                dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName+"-"+encoding));
1062            else
1063                dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style));
1064        }
1065        else
1066            dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style));
1067        dic.put(PdfName.ITALICANGLE, new PdfNumber(italicAngle));
1068        dic.put(PdfName.STEMV, new PdfNumber(80));
1069        if (fontStream != null) {
1070            if (cff)
1071                dic.put(PdfName.FONTFILE3, fontStream);
1072            else
1073                dic.put(PdfName.FONTFILE2, fontStream);
1074        }
1075        int flags = 0;
1076        if (isFixedPitch)
1077            flags |= 1;
1078        flags |= fontSpecific ? 4 : 32;
1079        if ((head.macStyle & 2) != 0)
1080            flags |= 64;
1081        if ((head.macStyle & 1) != 0)
1082            flags |= 262144;
1083        dic.put(PdfName.FLAGS, new PdfNumber(flags));
1084
1085        return dic;
1086    }
1087
1088    /** Generates the font dictionary for this font.
1089     * @return the PdfDictionary containing the font dictionary
1090     * @param subsetPrefix the subset prefix
1091     * @param firstChar the first valid character
1092     * @param lastChar the last valid character
1093     * @param shortTag a 256 bytes long <CODE>byte</CODE> array where each unused byte is represented by 0
1094     * @param fontDescriptor the indirect reference to a PdfDictionary containing the font descriptor or <CODE>null</CODE>
1095     */
1096    protected PdfDictionary getFontBaseType(PdfIndirectReference fontDescriptor, String subsetPrefix, int firstChar, int lastChar, byte shortTag[]) {
1097        PdfDictionary dic = new PdfDictionary(PdfName.FONT);
1098        if (cff) {
1099            dic.put(PdfName.SUBTYPE, PdfName.TYPE1);
1100            dic.put(PdfName.BASEFONT, new PdfName(fontName + style));
1101        }
1102        else {
1103            dic.put(PdfName.SUBTYPE, PdfName.TRUETYPE);
1104            dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style));
1105        }
1106        dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style));
1107        if (!fontSpecific) {
1108            for (int k = firstChar; k <= lastChar; ++k) {
1109                if (!differences[k].equals(notdef)) {
1110                    firstChar = k;
1111                    break;
1112                }
1113            }
1114        if (encoding.equals("Cp1252") || encoding.equals("MacRoman"))
1115                dic.put(PdfName.ENCODING, encoding.equals("Cp1252") ? PdfName.WIN_ANSI_ENCODING : PdfName.MAC_ROMAN_ENCODING);
1116            else {
1117                PdfDictionary enc = new PdfDictionary(PdfName.ENCODING);
1118                PdfArray dif = new PdfArray();
1119                boolean gap = true;
1120                for (int k = firstChar; k <= lastChar; ++k) {
1121                    if (shortTag[k] != 0) {
1122                        if (gap) {
1123                            dif.add(new PdfNumber(k));
1124                            gap = false;
1125                        }
1126                        dif.add(new PdfName(differences[k]));
1127                    }
1128                    else
1129                        gap = true;
1130                }
1131                enc.put(PdfName.DIFFERENCES, dif);
1132                dic.put(PdfName.ENCODING, enc);
1133            }
1134        }
1135        dic.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar));
1136        dic.put(PdfName.LASTCHAR, new PdfNumber(lastChar));
1137        PdfArray wd = new PdfArray();
1138        for (int k = firstChar; k <= lastChar; ++k) {
1139            if (shortTag[k] == 0)
1140                wd.add(new PdfNumber(0));
1141            else
1142                wd.add(new PdfNumber(widths[k]));
1143        }
1144        dic.put(PdfName.WIDTHS, wd);
1145        if (fontDescriptor != null)
1146            dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor);
1147        return dic;
1148    }
1149
1150    protected byte[] getFullFont() throws IOException {
1151        RandomAccessFileOrArray rf2 = null;
1152        try {
1153            rf2 = new RandomAccessFileOrArray(rf);
1154            rf2.reOpen();
1155            byte b[] = new byte[rf2.length()];
1156            rf2.readFully(b);
1157            return b;
1158        }
1159        finally {
1160            try {if (rf2 != null) {rf2.close();}} catch (Exception e) {}
1161        }
1162    }
1163
1164    protected static int[] compactRanges(ArrayList<int[]> ranges) {
1165        ArrayList<int[]> simp = new ArrayList<int[]>();
1166        for (int k = 0; k < ranges.size(); ++k) {
1167            int[] r = ranges.get(k);
1168            for (int j = 0; j < r.length; j += 2) {
1169                simp.add(new int[]{Math.max(0, Math.min(r[j], r[j + 1])), Math.min(0xffff, Math.max(r[j], r[j + 1]))});
1170            }
1171        }
1172        for (int k1 = 0; k1 < simp.size() - 1; ++k1) {
1173            for (int k2 = k1 + 1; k2 < simp.size(); ++k2) {
1174                int[] r1 = simp.get(k1);
1175                int[] r2 = simp.get(k2);
1176                if (r1[0] >= r2[0] && r1[0] <= r2[1] || r1[1] >= r2[0] && r1[0] <= r2[1]) {
1177                    r1[0] = Math.min(r1[0], r2[0]);
1178                    r1[1] = Math.max(r1[1], r2[1]);
1179                    simp.remove(k2);
1180                    --k2;
1181                }
1182            }
1183        }
1184        int[] s = new int[simp.size() * 2];
1185        for (int k = 0; k < simp.size(); ++k) {
1186            int[] r = simp.get(k);
1187            s[k * 2] = r[0];
1188            s[k * 2 + 1] = r[1];
1189        }
1190        return s;
1191    }
1192
1193    protected void addRangeUni(HashMap<Integer, int[]> longTag, boolean includeMetrics, boolean subsetp) {
1194        if (!subsetp && (subsetRanges != null || directoryOffset > 0)) {
1195            int[] rg = subsetRanges == null && directoryOffset > 0 ? new int[]{0, 0xffff} : compactRanges(subsetRanges);
1196            HashMap<Integer, int[]> usemap;
1197            if (!fontSpecific && cmap31 != null)
1198                usemap = cmap31;
1199            else if (fontSpecific && cmap10 != null)
1200                usemap = cmap10;
1201            else if (cmap31 != null)
1202                usemap = cmap31;
1203            else
1204                usemap = cmap10;
1205            for (Map.Entry<Integer, int[]> e: usemap.entrySet()) {
1206                int[] v = e.getValue();
1207                Integer gi = Integer.valueOf(v[0]);
1208                if (longTag.containsKey(gi))
1209                    continue;
1210                int c = e.getKey().intValue();
1211                boolean skip = true;
1212                for (int k = 0; k < rg.length; k += 2) {
1213                    if (c >= rg[k] && c <= rg[k + 1]) {
1214                        skip = false;
1215                        break;
1216                    }
1217                }
1218                if (!skip)
1219                    longTag.put(gi, includeMetrics ? new int[]{v[0], v[1], c} : null);
1220            }
1221        }
1222    }
1223
1224    protected void addRangeUni(HashSet<Integer> longTag, boolean subsetp) {
1225        if (!subsetp && (subsetRanges != null || directoryOffset > 0)) {
1226            int[] rg = subsetRanges == null && directoryOffset > 0 ? new int[]{0, 0xffff} : compactRanges(subsetRanges);
1227            HashMap<Integer, int[]> usemap;
1228            if (!fontSpecific && cmap31 != null)
1229                usemap = cmap31;
1230            else if (fontSpecific && cmap10 != null)
1231                usemap = cmap10;
1232            else if (cmap31 != null)
1233                usemap = cmap31;
1234            else
1235                usemap = cmap10;
1236            for (Map.Entry<Integer, int[]> e: usemap.entrySet()) {
1237                int[] v = e.getValue();
1238                Integer gi = Integer.valueOf(v[0]);
1239                if (longTag.contains(gi))
1240                    continue;
1241                int c = e.getKey().intValue();
1242                boolean skip = true;
1243                for (int k = 0; k < rg.length; k += 2) {
1244                    if (c >= rg[k] && c <= rg[k + 1]) {
1245                        skip = false;
1246                        break;
1247                    }
1248                }
1249                if (!skip)
1250                    longTag.add(gi);
1251            }
1252        }
1253    }
1254
1255    /** Outputs to the writer the font dictionaries and streams.
1256     * @param writer the writer for this document
1257     * @param ref the font indirect reference
1258     * @param params several parameters that depend on the font type
1259     * @throws IOException on error
1260     * @throws DocumentException error in generating the object
1261     */
1262    @Override
1263    void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException {
1264        int firstChar = ((Integer)params[0]).intValue();
1265        int lastChar = ((Integer)params[1]).intValue();
1266        byte shortTag[] = (byte[])params[2];
1267        boolean subsetp = ((Boolean)params[3]).booleanValue() && subset;
1268
1269        if (!subsetp) {
1270            firstChar = 0;
1271            lastChar = shortTag.length - 1;
1272            for (int k = 0; k < shortTag.length; ++k)
1273                shortTag[k] = 1;
1274        }
1275        PdfIndirectReference ind_font = null;
1276        PdfObject pobj = null;
1277        PdfIndirectObject obj = null;
1278        String subsetPrefix = "";
1279        if (embedded) {
1280            if (cff) {
1281                pobj = new StreamFont(readCffFont(), "Type1C", compressionLevel);
1282                obj = writer.addToBody(pobj);
1283                ind_font = obj.getIndirectReference();
1284            }
1285            else {
1286                if (subsetp)
1287                    subsetPrefix = createSubsetPrefix();
1288                HashSet<Integer> glyphs = new HashSet<Integer>();
1289                for (int k = firstChar; k <= lastChar; ++k) {
1290                    if (shortTag[k] != 0) {
1291                        int[] metrics = null;
1292                        if (specialMap != null) {
1293                            int[] cd = GlyphList.nameToUnicode(differences[k]);
1294                            if (cd != null)
1295                                metrics = getMetricsTT(cd[0]);
1296                        }
1297                        else {
1298                            if (fontSpecific)
1299                                metrics = getMetricsTT(k);
1300                            else
1301                                metrics = getMetricsTT(unicodeDifferences[k]);
1302                        }
1303                        if (metrics != null)
1304                            glyphs.add(Integer.valueOf(metrics[0]));
1305                    }
1306                }
1307                addRangeUni(glyphs, subsetp);
1308                byte[] b = null;
1309                if (subsetp || directoryOffset != 0 || subsetRanges != null) {
1310                    TrueTypeFontSubSet sb = new TrueTypeFontSubSet(fileName, new RandomAccessFileOrArray(rf), glyphs, directoryOffset, true, !subsetp);
1311                    b = sb.process();
1312                }
1313                else {
1314                    b = getFullFont();
1315                }
1316                int lengths[] = new int[]{b.length};
1317                pobj = new StreamFont(b, lengths, compressionLevel);
1318                obj = writer.addToBody(pobj);
1319                ind_font = obj.getIndirectReference();
1320            }
1321        }
1322        pobj = getFontDescriptor(ind_font, subsetPrefix, null);
1323        if (pobj != null){
1324            obj = writer.addToBody(pobj);
1325            ind_font = obj.getIndirectReference();
1326        }
1327        pobj = getFontBaseType(ind_font, subsetPrefix, firstChar, lastChar, shortTag);
1328        writer.addToBody(pobj, ref);
1329    }
1330
1331    /**
1332     * If this font file is using the Compact Font File Format, then this method
1333     * will return the raw bytes needed for the font stream. If this method is
1334     * ever made public: make sure to add a test if (cff == true).
1335     * @return  a byte array
1336     * @since   2.1.3
1337     */
1338    protected byte[] readCffFont() throws IOException {
1339        RandomAccessFileOrArray rf2 = new RandomAccessFileOrArray(rf);
1340        byte b[] = new byte[cffLength];
1341        try {
1342            rf2.reOpen();
1343            rf2.seek(cffOffset);
1344            rf2.readFully(b);
1345        }
1346        finally {
1347            try {
1348                rf2.close();
1349            }
1350            catch (Exception e) {
1351                // empty on purpose
1352            }
1353        }
1354        return b;
1355    }
1356
1357    /**
1358     * Returns a PdfStream object with the full font program.
1359     * @return  a PdfStream with the font program
1360     * @since   2.1.3
1361     */
1362    @Override
1363    public PdfStream getFullFontStream() throws IOException, DocumentException {
1364        if (cff) {
1365            return new StreamFont(readCffFont(), "Type1C", compressionLevel);
1366        }
1367        else {
1368                byte[] b = getFullFont();
1369                int lengths[] = new int[]{b.length};
1370                return new StreamFont(b, lengths, compressionLevel);
1371        }
1372    }
1373
1374    /** Gets the font parameter identified by <CODE>key</CODE>. Valid values
1375     * for <CODE>key</CODE> are <CODE>ASCENT</CODE>, <CODE>CAPHEIGHT</CODE>, <CODE>DESCENT</CODE>
1376     * and <CODE>ITALICANGLE</CODE>.
1377     * @param key the parameter to be extracted
1378     * @param fontSize the font size in points
1379     * @return the parameter in points
1380     */
1381    @Override
1382    public float getFontDescriptor(int key, float fontSize) {
1383        switch (key) {
1384            case ASCENT:
1385                return os_2.sTypoAscender * fontSize / head.unitsPerEm;
1386            case CAPHEIGHT:
1387                return os_2.sCapHeight * fontSize / head.unitsPerEm;
1388            case DESCENT:
1389                return os_2.sTypoDescender * fontSize / head.unitsPerEm;
1390            case ITALICANGLE:
1391                return (float)italicAngle;
1392            case BBOXLLX:
1393                return fontSize * head.xMin / head.unitsPerEm;
1394            case BBOXLLY:
1395                return fontSize * head.yMin / head.unitsPerEm;
1396            case BBOXURX:
1397                return fontSize * head.xMax / head.unitsPerEm;
1398            case BBOXURY:
1399                return fontSize * head.yMax / head.unitsPerEm;
1400            case AWT_ASCENT:
1401                return fontSize * hhea.Ascender / head.unitsPerEm;
1402            case AWT_DESCENT:
1403                return fontSize * hhea.Descender / head.unitsPerEm;
1404            case AWT_LEADING:
1405                return fontSize * hhea.LineGap / head.unitsPerEm;
1406            case AWT_MAXADVANCE:
1407                return fontSize * hhea.advanceWidthMax / head.unitsPerEm;
1408            case UNDERLINE_POSITION:
1409                return (underlinePosition - underlineThickness / 2) * fontSize / head.unitsPerEm;
1410            case UNDERLINE_THICKNESS:
1411                return underlineThickness * fontSize / head.unitsPerEm;
1412            case STRIKETHROUGH_POSITION:
1413                return os_2.yStrikeoutPosition * fontSize / head.unitsPerEm;
1414            case STRIKETHROUGH_THICKNESS:
1415                return os_2.yStrikeoutSize * fontSize / head.unitsPerEm;
1416            case SUBSCRIPT_SIZE:
1417                return os_2.ySubscriptYSize * fontSize / head.unitsPerEm;
1418            case SUBSCRIPT_OFFSET:
1419                return -os_2.ySubscriptYOffset * fontSize / head.unitsPerEm;
1420            case SUPERSCRIPT_SIZE:
1421                return os_2.ySuperscriptYSize * fontSize / head.unitsPerEm;
1422            case SUPERSCRIPT_OFFSET:
1423                return os_2.ySuperscriptYOffset * fontSize / head.unitsPerEm;
1424            case WEIGHT_CLASS:
1425                return os_2.usWeightClass;
1426            case WIDTH_CLASS:
1427                return os_2.usWidthClass;
1428        }
1429        return 0;
1430    }
1431
1432    /** Gets the glyph index and metrics for a character.
1433     * @param c the character
1434     * @return an <CODE>int</CODE> array with {glyph index, width}
1435     */
1436    public int[] getMetricsTT(int c) {
1437        if (cmapExt != null)
1438            return cmapExt.get(Integer.valueOf(c));
1439        if (!fontSpecific && cmap31 != null)
1440            return cmap31.get(Integer.valueOf(c));
1441        if (fontSpecific && cmap10 != null)
1442            return cmap10.get(Integer.valueOf(c));
1443        if (cmap31 != null)
1444            return cmap31.get(Integer.valueOf(c));
1445        if (cmap10 != null)
1446            return cmap10.get(Integer.valueOf(c));
1447        return null;
1448    }
1449
1450    /** Gets the postscript font name.
1451     * @return the postscript font name
1452     */
1453    @Override
1454    public String getPostscriptFontName() {
1455        return fontName;
1456    }
1457
1458    /** Gets the code pages supported by the font.
1459     * @return the code pages supported by the font
1460     */
1461    @Override
1462    public String[] getCodePagesSupported() {
1463        long cp = ((long)os_2.ulCodePageRange2 << 32) + (os_2.ulCodePageRange1 & 0xffffffffL);
1464        int count = 0;
1465        long bit = 1;
1466        for (int k = 0; k < 64; ++k) {
1467            if ((cp & bit) != 0 && codePages[k] != null)
1468                ++count;
1469            bit <<= 1;
1470        }
1471        String ret[] = new String[count];
1472        count = 0;
1473        bit = 1;
1474        for (int k = 0; k < 64; ++k) {
1475            if ((cp & bit) != 0 && codePages[k] != null)
1476                ret[count++] = codePages[k];
1477            bit <<= 1;
1478        }
1479        return ret;
1480    }
1481
1482    /** Gets the full name of the font. If it is a True Type font
1483     * each array element will have {Platform ID, Platform Encoding ID,
1484     * Language ID, font name}. The interpretation of this values can be
1485     * found in the Open Type specification, chapter 2, in the 'name' table.<br>
1486     * For the other fonts the array has a single element with {"", "", "",
1487     * font name}.
1488     * @return the full name of the font
1489     */
1490    @Override
1491    public String[][] getFullFontName() {
1492        return fullName;
1493    }
1494
1495    /** Gets all the entries of the Names-Table. If it is a True Type font
1496     * each array element will have {Name ID, Platform ID, Platform Encoding ID,
1497     * Language ID, font name}. The interpretation of this values can be
1498     * found in the Open Type specification, chapter 2, in the 'name' table.<br>
1499     * For the other fonts the array has a single element with {"", "", "",
1500     * font name}.
1501     * @return the full name of the font
1502     */
1503    @Override
1504    public String[][] getAllNameEntries() {
1505        return allNameEntries;
1506    }
1507
1508    /** Gets the family name of the font. If it is a True Type font
1509     * each array element will have {Platform ID, Platform Encoding ID,
1510     * Language ID, font name}. The interpretation of this values can be
1511     * found in the Open Type specification, chapter 2, in the 'name' table.<br>
1512     * For the other fonts the array has a single element with {"", "", "",
1513     * font name}.
1514     * @return the family name of the font
1515     */
1516    @Override
1517    public String[][] getFamilyFontName() {
1518        return familyName;
1519    }
1520
1521    /** Checks if the font has any kerning pairs.
1522     * @return <CODE>true</CODE> if the font has any kerning pairs
1523     */
1524    @Override
1525    public boolean hasKernPairs() {
1526        return kerning.size() > 0;
1527    }
1528
1529    /**
1530     * Sets the font name that will appear in the pdf font dictionary.
1531     * Use with care as it can easily make a font unreadable if not embedded.
1532     * @param name the new font name
1533     */
1534    @Override
1535    public void setPostscriptFontName(String name) {
1536        fontName = name;
1537    }
1538
1539    /**
1540     * Sets the kerning between two Unicode chars.
1541     * @param char1 the first char
1542     * @param char2 the second char
1543     * @param kern the kerning to apply in normalized 1000 units
1544     * @return <code>true</code> if the kerning was applied, <code>false</code> otherwise
1545     */
1546    @Override
1547    public boolean setKerning(int char1, int char2, int kern) {
1548        int metrics[] = getMetricsTT(char1);
1549        if (metrics == null)
1550            return false;
1551        int c1 = metrics[0];
1552        metrics = getMetricsTT(char2);
1553        if (metrics == null)
1554            return false;
1555        int c2 = metrics[0];
1556        kerning.put((c1 << 16) + c2, kern);
1557        return true;
1558    }
1559
1560    @Override
1561    protected int[] getRawCharBBox(int c, String name) {
1562        HashMap<Integer, int[]> map = null;
1563        if (name == null || cmap31 == null)
1564            map = cmap10;
1565        else
1566            map = cmap31;
1567        if (map == null)
1568            return null;
1569        int metric[] = map.get(Integer.valueOf(c));
1570        if (metric == null || bboxes == null)
1571            return null;
1572        return bboxes[metric[0]];
1573    }
1574}