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

Save This Page
Home » iText-src-2.1.3 » com.lowagie » text » pdf » [javadoc | source]