Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /* NOTICE: This file has been changed by Plutext Pty Ltd for use in docx4j.
    * The package name has been changed; there may also be other changes.
    * 
    * This notice is included to meet the condition in clause 4(b) of the License. 
    */
   
    /*
    * Licensed to the Apache Software Foundation (ASF) under one or more
    * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  /* $Id: TTFFile.java 719038 2008-11-19 19:46:45Z jeremias $ */
  
  package org.docx4j.fonts.fop.fonts.truetype;
  
  import java.util.BitSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import org.slf4j.Logger;
  
  
Reads a TrueType file or a TrueType Collection. The TrueType spec can be found at the Microsoft. Typography site: http://www.microsoft.com/truetype/
  
  public class TTFFile {
  
      static final byte NTABS = 24;
      static final int NMACGLYPHS = 258;
      static final int MAX_CHAR_CODE = 255;
      static final int ENC_BUF_SIZE = 1024;

    
Set to true to get even more debug output than with level DEBUG
  
      public static final boolean TRACE_ENABLED = false;
  
      private String encoding = "WinAnsiEncoding";    // Default encoding
  
    
Array containing the Panose information.
  
  	private Panose panose;
      
      private short firstChar = 0;
      private boolean isEmbeddable = true;
      private boolean hasSerifs = true;
    
Table directory
  
      protected Map dirTabs;
      private Map kerningTab;                          // for CIDs
      private Map ansiKerningTab;                      // For winAnsiEncoding
      private List cmaps;
      private List unicodeMapping;
  
      private int upem;                                // unitsPerEm from "head" table
      private int nhmtx;                               // Number of horizontal metrics
      private int postFormat;
      private int locaFormat;
    
Offset to last loca
  
      protected long lastLoca = 0;
      private int numberOfGlyphs// Number of glyphs in font (read from "maxp" table)
      private int nmGlyphs;                            // Used in fixWidths - remove?
  
    
Contains glyph data
  
      protected TTFMtxEntry[] mtxTab;                  // Contains glyph data
      private int[] mtxEncoded = null;
  
      private String postScriptName = "";
      private String fullName = "";
      private String notice = "";
      private Set familyNames = new java.util.HashSet(); //Set<String>
      private String subFamilyName = "";
  
      private long italicAngle = 0;
      private long isFixedPitch = 0;
     private int fontBBox1 = 0;
     private int fontBBox2 = 0;
     private int fontBBox3 = 0;
     private int fontBBox4 = 0;
     private int capHeight = 0;
     private int os2CapHeight = 0;
     private int underlinePosition = 0;
     private int underlineThickness = 0;
     private int xHeight = 0;
     private int os2xHeight = 0;
     //Effective ascender/descender
     private int ascender = 0;
     private int descender = 0;
     //Ascender/descender from hhea table
     private int hheaAscender = 0;
     private int hheaDescender = 0;
     //Ascender/descender from OS/2 table
     private int os2Ascender = 0;
     private int os2Descender = 0;
     private int usWeightClass = 0;
 
     private short lastChar = 0;
 
     private int[] ansiWidth;
     private Map ansiIndex;
 
     // internal mapping of glyph indexes to unicode indexes
     // used for quick mappings in this class
     private Map glyphToUnicodeMap = new java.util.HashMap();
     private Map unicodeToGlyphMap = new java.util.HashMap();
 
     private TTFDirTabEntry currentDirTab;
 
     private boolean isCFF;

    
logging instance
 
     protected Logger log = LoggerFactory.getLogger(TTFFile.class);

    
Key-value helper class
 
     class UnicodeMapping {
 
         private int unicodeIndex;
         private int glyphIndex;
 
         UnicodeMapping(int glyphIndexint unicodeIndex) {
             this. = unicodeIndex;
             this. = glyphIndex;
             .put(new Integer(glyphIndex), new Integer(unicodeIndex));
             .put(new Integer(unicodeIndex), new Integer(glyphIndex));
         }

        
Returns the glyphIndex.

Returns:
the glyph index
 
         public int getGlyphIndex() {
             return ;
         }

        
Returns the unicodeIndex.

Returns:
the Unicode index
 
         public int getUnicodeIndex() {
             return ;
         }
     }

    
Position inputstream to position indicated in the dirtab offset + offset
 
     boolean seekTab(FontFileReader inString name,
                   long offsetthrows IOException {
         TTFDirTabEntry dt = (TTFDirTabEntry).get(name);
         if (dt == null) {
             .error("Dirtab " + name + " not found.");
             return false;
         } else {
             in.seekSet(dt.getOffset() + offset);
             this. = dt;
         }
         return true;
     }

    
Convert from truetype unit to pdf unit based on the unitsPerEm field in the "head" table

Parameters:
n truetype unit
Returns:
pdf unit
 
     public int convertTTFUnit2PDFUnit(int n) {
         int ret;
         if (n < 0) {
             long rest1 = n % ;
             long storrest = 1000 * rest1;
             long ledd2 = (storrest != 0 ? rest1 / storrest : 0);
             ret = -((-1000 * n) /  - (int)ledd2);
         } else {
             ret = (n / ) * 1000 + ((n % ) * 1000) / ;
         }
 
         return ret;
     }

    
Read the cmap table, return false if the table is not present or only unsupported tables are present. Currently only unicode cmaps are supported. Set the unicodeIndex in the TTFMtxEntries and fills in the cmaps vector.
 
     private boolean readCMAP(FontFileReader inthrows IOException {
 
          = new java.util.ArrayList();
 
         seekTab(in"cmap", 2);
         int numCMap = in.readTTFUShort();    // Number of cmap subtables
         long cmapUniOffset = 0;
         long symbolMapOffset = 0;
 
         if (.isDebugEnabled()) {
             .debug(numCMap + " cmap tables");
         }
 
         //Read offset for all tables. We are only interested in the unicode table
         for (int i = 0; i < numCMapi++) {
             int cmapPID = in.readTTFUShort();
             int cmapEID = in.readTTFUShort();
             long cmapOffset = in.readTTFULong();
 
             if (.isDebugEnabled()) {
                 .debug("Platform ID: " + cmapPID + " Encoding: " + cmapEID);
             }
 
             if (cmapPID == 3 && cmapEID == 1) {
                 cmapUniOffset = cmapOffset;
             }
             if (cmapPID == 3 && cmapEID == 0) {
                 symbolMapOffset = cmapOffset;
             }
         }
 
         if (cmapUniOffset > 0) {
             return readUnicodeCmap(incmapUniOffset, 1);
         } else if (symbolMapOffset > 0) {
             return readUnicodeCmap(insymbolMapOffset, 0);
         } else {
             .error("Unsupported TrueType font: No Unicode or Symbol cmap table"
                     + " not present. Aborting");
             return false;
         }
     }
 
     private boolean readUnicodeCmap(FontFileReader inlong cmapUniOffsetint encodingID)
             throws IOException {
         //Read CMAP table and correct mtxTab.index
         int mtxPtr = 0;
 
         // Read unicode cmap
         seekTab(in"cmap"cmapUniOffset);
         int cmapFormat = in.readTTFUShort();
         /*int cmap_length =*/ in.readTTFUShort(); //skip cmap length
 
         if (.isDebugEnabled()) {
             .debug("CMAP format: " + cmapFormat);
         }
 
         if (cmapFormat == 4) {
             in.skip(2);    // Skip version number
             int cmapSegCountX2 = in.readTTFUShort();
             int cmapSearchRange = in.readTTFUShort();
             int cmapEntrySelector = in.readTTFUShort();
             int cmapRangeShift = in.readTTFUShort();
 
             if (.isDebugEnabled()) {
                 .debug("segCountX2   : " + cmapSegCountX2);
                 .debug("searchRange  : " + cmapSearchRange);
                 .debug("entrySelector: " + cmapEntrySelector);
                 .debug("rangeShift   : " + cmapRangeShift);
             }
 
 
             int[] cmapEndCounts = new int[cmapSegCountX2 / 2];
             int[] cmapStartCounts = new int[cmapSegCountX2 / 2];
             int[] cmapDeltas = new int[cmapSegCountX2 / 2];
             int[] cmapRangeOffsets = new int[cmapSegCountX2 / 2];
 
             for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
                 cmapEndCounts[i] = in.readTTFUShort();
             }
 
             in.skip(2);    // Skip reservedPad
 
             for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
                 cmapStartCounts[i] = in.readTTFUShort();
             }
 
             for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
                 cmapDeltas[i] = in.readTTFShort();
             }
 
             //int startRangeOffset = in.getCurrentPos();
 
             for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
                 cmapRangeOffsets[i] = in.readTTFUShort();
             }
 
             int glyphIdArrayOffset = in.getCurrentPos();
 
             BitSet eightBitGlyphs = new BitSet(256);
 
             // Insert the unicode id for the glyphs in mtxTab
             // and fill in the cmaps ArrayList
 
             for (int i = 0; i < cmapStartCounts.lengthi++) {
 
                 if (.isTraceEnabled()) {
                     .trace(i + ": " + cmapStartCounts[i]
                                                          + " - " + cmapEndCounts[i]);
                 }
                 if (.isDebugEnabled()) {
                     if (isInPrivateUseArea(cmapStartCounts[i], cmapEndCounts[i])) {
                         .debug("Font contains glyphs in the Unicode private use area:"
                                 + Integer.toHexString(cmapStartCounts[i]) + " - "
                                 + Integer.toHexString(cmapEndCounts[i]));
                     }
                 }
 
                 for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) {
 
                     // Update lastChar
                     if (j < 256 && j > ) {
                          = (short)j;
                     }
 
                     if (j < 256) {
                         eightBitGlyphs.set(j);
                     }
 
                     if (mtxPtr < .) {
                         int glyphIdx;
                         // the last character 65535 = .notdef
                         // may have a range offset
                         if (cmapRangeOffsets[i] != 0 && j != 65535) {
                             int glyphOffset = glyphIdArrayOffset
                                 + ((cmapRangeOffsets[i] / 2)
                                     + (j - cmapStartCounts[i])
                                     + (i)
                                     - cmapSegCountX2 / 2) * 2;
                             in.seekSet(glyphOffset);
                             glyphIdx = (in.readTTFUShort() + cmapDeltas[i])
                                        & 0xffff;
 
                             .add(new UnicodeMapping(glyphIdxj));
                             [glyphIdx].getUnicodeIndex().add(new Integer(j));
 
                             if (encodingID == 0 && j >= 0xF020 && j <= 0xF0FF) {
                                 //Experimental: Mapping 0xF020-0xF0FF to 0x0020-0x00FF
                                 //Tested with Wingdings and Symbol TTF fonts which map their
                                 //glyphs in the region 0xF020-0xF0FF.
                                 int mapped = j - 0xF000;
                                 if (!eightBitGlyphs.get(mapped)) {
                                     //Only map if Unicode code point hasn't been mapped before
                                     .add(new UnicodeMapping(glyphIdxmapped));
                                     [glyphIdx].getUnicodeIndex().add(new Integer(mapped));
                                 }
                             }
 
                             // Also add winAnsiWidth
                             List v = (List).get(new Integer(j));
                             if (v != null) {
                                 Iterator e = v.listIterator();
                                 while (e.hasNext()) {
                                     Integer aIdx = (Integer)e.next();
                                     [aIdx.intValue()]
                                         = [glyphIdx].getWx();
 
                                     if (.isTraceEnabled()) {
                                         .trace("Added width "
                                                 + [glyphIdx].getWx()
                                                 + " uni: " + j
                                                 + " ansi: " + aIdx.intValue());
                                     }
                                 }
                             }
 
                             if (.isTraceEnabled()) {
                                 .trace("Idx: "
                                         + glyphIdx
                                         + " Delta: " + cmapDeltas[i]
                                         + " Unicode: " + j
                                         + " name: " + [glyphIdx].getName());
                             }
                         } else {
                             glyphIdx = (j + cmapDeltas[i]) & 0xffff;
 
                             if (glyphIdx < .) {
                                 [glyphIdx].getUnicodeIndex().add(new Integer(j));
                             } else {
                                 .debug("Glyph " + glyphIdx
                                                    + " out of range: "
                                                    + .);
                             }
 
                             .add(new UnicodeMapping(glyphIdxj));
                             if (glyphIdx < .) {
                                 [glyphIdx].getUnicodeIndex().add(new Integer(j));
                             } else {
                                 .debug("Glyph " + glyphIdx
                                                    + " out of range: "
                                                    + .);
                             }
 
                             // Also add winAnsiWidth
                             List v = (List).get(new Integer(j));
                             if (v != null) {
                                 Iterator e = v.listIterator();
                                 while (e.hasNext()) {
                                     Integer aIdx = (Integer)e.next();
                                     [aIdx.intValue()] = [glyphIdx].getWx();
                                 }
                             }
 
                             //getLogger().debug("IIdx: " +
                             //    mtxPtr +
                             //    " Delta: " + cmap_deltas[i] +
                             //    " Unicode: " + j +
                             //    " name: " +
                             //    mtxTab[(j+cmap_deltas[i]) & 0xffff].name);
 
                         }
                         if (glyphIdx < .) {
                             if ([glyphIdx].getUnicodeIndex().size() < 2) {
                                 mtxPtr++;
                             }
                         }
                     }
                 }
             }
         } else {
             .error("Cmap format not supported: " + cmapFormat);
             return false;
         }
         return true;
     }
 
     private boolean isInPrivateUseArea(int startint end) {
         return (isInPrivateUseArea(start) || isInPrivateUseArea(end));
     }
 
     private boolean isInPrivateUseArea(int unicode) {
         return (unicode >= 0xE000 && unicode <= 0xF8FF);
     }

    
Print first char/last char
 
     private void printMaxMin() {
         int min = 255;
         int max = 0;
         for (int i = 0; i < .i++) {
             if ([i].getIndex() < min) {
                 min = [i].getIndex();
             }
             if ([i].getIndex() > max) {
                 max = [i].getIndex();
             }
         }
         .info("Min: " + min);
         .info("Max: " + max);
     }


    
Reads the font using a FontFileReader.

Parameters:
in The FontFileReader to use
Throws:
java.io.IOException In case of an I/O problem
 
     public void readFont(FontFileReader inthrows IOException {
         readFont(in, (String)null);
     }

    
initialize the ansiWidths array (for winAnsiEncoding) and fill with the missingwidth
 
     private void initAnsiWidths() {
          = new int[256];
         for (int i = 0; i < 256; i++) {
             [i] = [0].getWx();
         }
 
         // Create an index hash to the ansiWidth
         // Can't just index the winAnsiEncoding when inserting widths
         // same char (eg bullet) is repeated more than one place
          = new java.util.HashMap();
         for (int i = 32; i < ..i++) {
             Integer ansi = new Integer(i);
             Integer uni = new Integer((int).[i]);
 
             List v = (List).get(uni);
             if (v == null) {
                 v = new java.util.ArrayList();
                 .put(univ);
             }
             v.add(ansi);
         }
     }

    
Read the font data. If the fontfile is a TrueType Collection (.ttc file) the name of the font to read data for must be supplied, else the name is ignored.

Parameters:
in The FontFileReader to use
name The name of the font
Returns:
boolean Returns true if the font is valid
Throws:
java.io.IOException In case of an I/O problem
 
     public boolean readFont(FontFileReader inString namethrows IOException {
 
         /*
          * Check if TrueType collection, and that the name
          * exists in the collection
          */
         if (!checkTTC(inname)) {
             if (name == null) {
                 throw new IllegalArgumentException(
                     "For TrueType collection you must specify which font "
                     + "to select (-ttcname)");
             } else {
                 throw new IOException(
                     "Name does not exist in the TrueType collection: " + name);
             }
         }
 
         readDirTabs(in);
         readFontHeader(in);
         getNumGlyphs(in);
         if (.isDebugEnabled()) {
             .debug("Number of glyphs in font: " + );
         }
         readHorizontalHeader(in);
         readHorizontalMetrics(in);
         initAnsiWidths();
         readPostScript(in);
         readOS2(in);
         determineAscDesc();
         if (!) {
             readIndexToLocation(in);
             readGlyf(in);
         }
         readName(in);
         boolean pcltFound = readPCLT(in);
         // Read cmap table and fill in ansiwidths
         boolean valid = readCMAP(in);
         if (!valid) {
             return false;
         }
         // Create cmaps for bfentries
         createCMaps();
         // print_max_min();
 
         readKerning(in);
         return true;
     }
 
     private void createCMaps() {
          = new java.util.ArrayList();
         TTFCmapEntry tce = new TTFCmapEntry();
 
         Iterator e = .listIterator();
         UnicodeMapping um = (UnicodeMapping)e.next();
         UnicodeMapping lastMapping = um;
 
         tce.setUnicodeStart(um.getUnicodeIndex());
         tce.setGlyphStartIndex(um.getGlyphIndex());
 
         while (e.hasNext()) {
             um = (UnicodeMapping)e.next();
             if (((lastMapping.getUnicodeIndex() + 1) != um.getUnicodeIndex())
                     || ((lastMapping.getGlyphIndex() + 1) != um.getGlyphIndex())) {
                 tce.setUnicodeEnd(lastMapping.getUnicodeIndex());
                 .add(tce);
 
                 tce = new TTFCmapEntry();
                 tce.setUnicodeStart(um.getUnicodeIndex());
                 tce.setGlyphStartIndex(um.getGlyphIndex());
             }
             lastMapping = um;
         }
 
         tce.setUnicodeEnd(um.getUnicodeIndex());
         .add(tce);
     }

    
Returns the PostScript name of the font.

Returns:
String The PostScript name
 
     public String getPostScriptName() {
         if (.length() == 0) {
             return FontUtil.stripWhiteSpace(getFullName());
         } else {
             return ;
         }
     }

    
Returns the font family names of the font.

Returns:
Set The family names (a Set of Strings)
 
     public Set getFamilyNames() {
         return ;
     }

    
Returns the font sub family name of the font.

Returns:
String The sub family name
 
     public String getSubFamilyName() {
         return ;
     }

    
Returns the full name of the font.

Returns:
String The full name
 
     public String getFullName() {
         return ;
     }

    
Returns the name of the character set used.

Returns:
String The caracter set
 
     public String getCharSetName() {
         return ;
     }

    
Returns the CapHeight attribute of the font.

Returns:
int The CapHeight
 
     public int getCapHeight() {
         return (int)convertTTFUnit2PDFUnit();
     }

    
Returns the XHeight attribute of the font.

Returns:
int The XHeight
 
     public int getXHeight() {
         return (int)convertTTFUnit2PDFUnit();
     }

    
Returns the Flags attribute of the font.

Returns:
int The Flags
 
     public int getFlags() {
         int flags = 32;    // Use Adobe Standard charset
         if ( != 0) {
             flags = flags | 64;
         }
         if ( != 0) {
             flags = flags | 2;
         }
         if () {
             flags = flags | 1;
         }
         return flags;
     }

    
Returns the weight class of this font. Valid values are 100, 200....,800, 900.

Returns:
the weight class value (or 0 if there was no OS/2 table in the font)
 
     public int getWeightClass() {
         return this.;
     }

    
Returns the StemV attribute of the font.

Returns:
String The StemV
 
     public String getStemV() {
         return "0";
     }

    
Returns the ItalicAngle attribute of the font.

Returns:
String The ItalicAngle
 
     public String getItalicAngle() {
         String ia = Short.toString((short)( / 0x10000));
 
         // This is the correct italic angle, however only int italic
         // angles are supported at the moment so this is commented out.
         /*
          * if ((italicAngle % 0x10000) > 0 )
          * ia=ia+(comma+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000)));
          */
         return ia;
     }

    
Returns the font bounding box.

Returns:
int[] The font bbox
 
     public int[] getFontBBox() {
         final int[] fbb = new int[4];
         fbb[0] = (int)convertTTFUnit2PDFUnit();
         fbb[1] = (int)convertTTFUnit2PDFUnit();
         fbb[2] = (int)convertTTFUnit2PDFUnit();
         fbb[3] = (int)convertTTFUnit2PDFUnit();
 
         return fbb;
     }

    
Returns the LowerCaseAscent attribute of the font.

Returns:
int The LowerCaseAscent
 
     public int getLowerCaseAscent() {
         return (int)convertTTFUnit2PDFUnit();
     }

    
Returns the LowerCaseDescent attribute of the font.

Returns:
int The LowerCaseDescent
 
     public int getLowerCaseDescent() {
         return (int)convertTTFUnit2PDFUnit();
     }

    
Returns the index of the last character, but this is for WinAnsiEncoding only, so the last char is < 256.

Returns:
short Index of the last character (<256)
 
     public short getLastChar() {
         return ;
     }

    
Returns the index of the first character.

Returns:
short Index of the first character
 
     public short getFirstChar() {
         return ;
     }

    
Returns an array of character widths.

Returns:
int[] The character widths
 
     public int[] getWidths() {
         int[] wx = new int[.];
         for (int i = 0; i < wx.lengthi++) {
             wx[i] = (int)convertTTFUnit2PDFUnit([i].getWx());
         }
 
         return wx;
     }

    
Returns the width of a given character.

Parameters:
idx Index of the character
Returns:
int Standard width
 
     public int getCharWidth(int idx) {
         return (int)convertTTFUnit2PDFUnit([idx]);
     }

    
Returns the kerning table.

Returns:
Map The kerning table
 
     public Map getKerning() {
         return ;
     }

    
Returns the ANSI kerning table.

Returns:
Map The ANSI kerning table
 
     public Map getAnsiKerning() {
         return ;
     }

    
Indicates if the font may be embedded.

Returns:
boolean True if it may be embedded
 
     public boolean isEmbeddable() {
         return ;
     }

    
Indicates whether or not the font is an OpenType CFF font (rather than a TrueType font).

Returns:
true if the font is in OpenType CFF format.
 
     public boolean isCFF() {
        return this.;
     }

    
Read Table Directory from the current position in the FontFileReader and fill the global HashMap dirTabs with the table name (String) as key and a TTFDirTabEntry as value.

Parameters:
in FontFileReader to read the table directory from
Throws:
java.io.IOException in case of an I/O problem
 
     protected void readDirTabs(FontFileReader inthrows IOException {
         int sfntVersion = in.readTTFLong(); // TTF_FIXED_SIZE (4 bytes)
         switch (sfntVersion) {
         case 0x10000:
             .debug("sfnt version: OpenType 1.0");
             break;
         case 0x4F54544F: //"OTTO"
             this. = true;
             .debug("sfnt version: OpenType with CFF data");
             break;
         case 0x74727565: //"true"
             .debug("sfnt version: Apple TrueType");
             break;
         case 0x74797031: //"typ1"
             .debug("sfnt version: Apple Type 1 housed in sfnt wrapper");
             break;
         default:
             .debug("Unknown sfnt version: " + Integer.toHexString(sfntVersion));
             break;
         }
         int ntabs = in.readTTFUShort();
         in.skip(6);    // 3xTTF_USHORT_SIZE
 
          = new java.util.HashMap();
         TTFDirTabEntry[] pd = new TTFDirTabEntry[ntabs];
         .debug("Reading " + ntabs + " dir tables");
         for (int i = 0; i < ntabsi++) {
             pd[i] = new TTFDirTabEntry();
             .put(pd[i].read(in), pd[i]);
         }
         .debug("dir tables: " + .keySet());
     }

    
Read the "head" table, this reads the bounding box and sets the upem (unitsPerEM) variable

Parameters:
in FontFileReader to read the header from
Throws:
java.io.IOException in case of an I/O problem
 
     protected void readFontHeader(FontFileReader inthrows IOException {
         seekTab(in"head", 2 * 4 + 2 * 4);
         int flags = in.readTTFUShort();
         if (.isDebugEnabled()) {
             .debug("flags: " + flags + " - " + Integer.toString(flags, 2));
         }
          = in.readTTFUShort();
         if (.isDebugEnabled()) {
             .debug("unit per em: " + );
         }
 
         in.skip(16);
 
          = in.readTTFShort();
          = in.readTTFShort();
          = in.readTTFShort();
          = in.readTTFShort();
         if (.isDebugEnabled()) {
             .debug("font bbox: xMin=" + 
                     + " yMin=" + 
                     + " xMax=" + 
                     + " yMax=" + );
         }
 
         in.skip(2 + 2 + 2);
 
          = in.readTTFShort();
     }

    
Read the number of glyphs from the "maxp" table

Parameters:
in FontFileReader to read the number of glyphs from
Throws:
java.io.IOException in case of an I/O problem
 
     protected void getNumGlyphs(FontFileReader inthrows IOException {
         seekTab(in"maxp", 4);
          = in.readTTFUShort();
     }


    
Read the "hhea" table to find the ascender and descender and size of "hmtx" table, as a fixed size font might have only one width.

Parameters:
in FontFileReader to read the hhea table from
Throws:
java.io.IOException in case of an I/O problem
 
     protected void readHorizontalHeader(FontFileReader in)
             throws IOException {
         seekTab(in"hhea", 4);
          = in.readTTFShort();
          = in.readTTFShort();
 
         in.skip(2 + 2 + 3 * 2 + 8 * 2);
          = in.readTTFUShort();
 
         if (.isDebugEnabled()) {
             .debug("hhea.Ascender: " + formatUnitsForDebug());
             .debug("hhea.Descender: " + formatUnitsForDebug());
             .debug("Number of horizontal metrics: " + );
         }
     }

    
Read "hmtx" table and put the horizontal metrics in the mtxTab array. If the number of metrics is less than the number of glyphs (eg fixed size fonts), extend the mtxTab array and fill in the missing widths

Parameters:
in FontFileReader to read the hmtx table from
Throws:
java.io.IOException in case of an I/O problem
 
     protected void readHorizontalMetrics(FontFileReader in)
             throws IOException {
         seekTab(in"hmtx", 0);
 
         int mtxSize = Math.max();
          = new TTFMtxEntry[mtxSize];
 
         if () {
             .debug("*** Widths array: \n");
         }
         for (int i = 0; i < mtxSizei++) {
             [i] = new TTFMtxEntry();
         }
         for (int i = 0; i < i++) {
             [i].setWx(in.readTTFUShort());
             [i].setLsb(in.readTTFUShort());
 
             if () {
                 if (.isDebugEnabled()) {
                     .debug("   width[" + i + "] = "
                         + convertTTFUnit2PDFUnit([i].getWx()) + ";");
                 }
             }
         }
 
         if ( < mtxSize) {
             // Fill in the missing widths
             int lastWidth = [ - 1].getWx();
             for (int i = i < mtxSizei++) {
                 [i].setWx(lastWidth);
                 [i].setLsb(in.readTTFUShort());
             }
         }
     }


    
Read the "post" table containing the PostScript names of the glyphs.
 
     private final void readPostScript(FontFileReader inthrows IOException {
         seekTab(in"post", 0);
          = in.readTTFLong();
          = in.readTTFULong();
          = in.readTTFShort();
          = in.readTTFShort();
          = in.readTTFULong();
 
         //Skip memory usage values
         in.skip(4 * 4);
 
         .debug("PostScript format: 0x" + Integer.toHexString());
         switch () {
         case 0x00010000:
             .debug("PostScript format 1");
             for (int i = 0; i < ..i++) {
                 [i].setName(.[i]);
             }
             break;
         case 0x00020000:
             .debug("PostScript format 2");
             int numGlyphStrings = 0;
 
             // Read Number of Glyphs
             int l = in.readTTFUShort();
 
            // Read indexes
            for (int i = 0; i < li++) {
                [i].setIndex(in.readTTFUShort());
                if ([i].getIndex() > 257) {
                    //Index is not in the Macintosh standard set
                    numGlyphStrings++;
                }
                if (.isTraceEnabled()) {
                    .trace("PostScript index: " + [i].getIndexAsString());
                }
            }
            // firstChar=minIndex;
            String[] psGlyphsBuffer = new String[numGlyphStrings];
            if (.isDebugEnabled()) {
                .debug("Reading " + numGlyphStrings
                        + " glyphnames, that are not in the standard Macintosh"
                        + " set. Total number of glyphs=" + l);
            }
            for (int i = 0; i < psGlyphsBuffer.lengthi++) {
                psGlyphsBuffer[i] = in.readTTFString(in.readTTFUByte());
            }
            //Set glyph names
            for (int i = 0; i < li++) {
                if ([i].getIndex() < ) {
                    [i].setName(.[[i].getIndex()]);
                } else {
                    if (![i].isIndexReserved()) {
                        int k = [i].getIndex() - ;
                        if (.isTraceEnabled()) {
                            .trace(k + " i=" + i + " mtx=" + .
                                + " ps=" + psGlyphsBuffer.length);
                        }
                        [i].setName(psGlyphsBuffer[k]);
                    }
                }
            }
            break;
        case 0x00030000:
            // PostScript format 3 contains no glyph names
            .debug("PostScript format 3");
            break;
        default:
            .error("Unknown PostScript format: " + );
        }
    }


    
Read the "OS/2" table
    private void readOS2(FontFileReader inthrows IOException {
        // Check if font is embeddable
        TTFDirTabEntry os2Entry = (TTFDirTabEntry).get("OS/2");
        if (os2Entry != null) {
            seekTab(in"OS/2", 0);
            int version = in.readTTFUShort();
            if (.isDebugEnabled()) {
                .debug("OS/2 table: version=" + version
                        + ", offset=" + os2Entry.getOffset() + ", len=" + os2Entry.getLength());
            }
            in.skip(2); //xAvgCharWidth
            this. = in.readTTFUShort();
            // usWidthClass
            in.skip(2);
            int fsType = in.readTTFUShort();
            if (fsType == 2) {
                 = false;
            } else {
                 = true;
            }
            in.skip(11 * 2);
            
            //in.skip(10); //panose array            
            final byte[] panoseArray = new byte[10];
			for (int i = 0; i < panoseArray.lengthi++) {
				panoseArray[i] = in.read();
			this. = Panose.makeInstance(panoseArray);
            
            in.skip(4 * 4); // unicode ranges
            in.skip(4);
            in.skip(3 * 2);
            int v;
             = in.readTTFShort(); //sTypoAscender
             = in.readTTFShort(); //sTypoDescender
            if (.isDebugEnabled()) {
                .debug("sTypoAscender: " + 
                        + " -> internal " + convertTTFUnit2PDFUnit());
                .debug("sTypoDescender: " + 
                        + " -> internal " + convertTTFUnit2PDFUnit());
            }
            v = in.readTTFShort(); //sTypoLineGap
            if (.isDebugEnabled()) {
                .debug("sTypoLineGap: " + v);
            }
            v = in.readTTFUShort(); //usWinAscent
            if (.isDebugEnabled()) {
                .debug("usWinAscent: " + formatUnitsForDebug(v));
            }
            v = in.readTTFUShort(); //usWinDescent
            if (.isDebugEnabled()) {
                .debug("usWinDescent: " + formatUnitsForDebug(v));
            }
            //version 1 OS/2 table might end here
            if (os2Entry.getLength() >= 78 + (2 * 4) + (2 * 2)) {
                in.skip(2 * 4);
                this. = in.readTTFShort(); //sxHeight
                this. = in.readTTFShort(); //sCapHeight
                if (.isDebugEnabled()) {
                    .debug("sxHeight: " + this.);
                    .debug("sCapHeight: " + this.);
                }
            }
        } else {
             = true;
        }
    }

    
Read the "loca" table.

Parameters:
in FontFileReader to read from
Throws:
java.io.IOException In case of a I/O problem
    protected final void readIndexToLocation(FontFileReader in)
            throws IOException {
        if (!seekTab(in"loca", 0)) {
            throw new IOException("'loca' table not found, happens when the font file doesn't"
                    + " contain TrueType outlines (trying to read an OpenType CFF font maybe?)");
        }
        for (int i = 0; i < i++) {
            [i].setOffset( == 1 ? in.readTTFULong()
                                 : (in.readTTFUShort() << 1));
        }
         = ( == 1 ? in.readTTFULong()
                    : (in.readTTFUShort() << 1));
    }

    
Read the "glyf" table to find the bounding boxes.

Parameters:
in FontFileReader to read from
Throws:
java.io.IOException In case of a I/O problem
    private final void readGlyf(FontFileReader inthrows IOException {
        TTFDirTabEntry dirTab = (TTFDirTabEntry).get("glyf");
        if (dirTab == null) {
            throw new IOException("glyf table not found, cannot continue");
        }
        for (int i = 0; i < ( - 1); i++) {
            if ([i].getOffset() != [i + 1].getOffset()) {
                in.seekSet(dirTab.getOffset() + [i].getOffset());
                in.skip(2);
                final int[] bbox = {
                    in.readTTFShort(),
                    in.readTTFShort(),
                    in.readTTFShort(),
                    in.readTTFShort()};
                [i].setBoundingBox(bbox);
            } else {
                [i].setBoundingBox([0].getBoundingBox());
            }
        }
        long n = ((TTFDirTabEntry).get("glyf")).getOffset();
        for (int i = 0; i < i++) {
            if ((i + 1) >= .
                    || [i].getOffset() != [i + 1].getOffset()) {
                in.seekSet(n + [i].getOffset());
                in.skip(2);
                final int[] bbox = {
                    in.readTTFShort(),
                    in.readTTFShort(),
                    in.readTTFShort(),
                    in.readTTFShort()};
                [i].setBoundingBox(bbox);
            } else {
                

Todo:
Verify that this is correct, looks like a copy/paste bug (jm
                final int bbox0 = [0].getBoundingBox()[0];
                final int[] bbox = {bbox0bbox0bbox0bbox0};
                [i].setBoundingBox(bbox);
                /* Original code
                mtxTab[i].bbox[0] = mtxTab[0].bbox[0];
                mtxTab[i].bbox[1] = mtxTab[0].bbox[0];
                mtxTab[i].bbox[2] = mtxTab[0].bbox[0];
                mtxTab[i].bbox[3] = mtxTab[0].bbox[0]; */
            }
            if (.isTraceEnabled()) {
                .trace([i].toString(this));
            }
        }
    }

    
Read the "name" table.

Parameters:
in FontFileReader to read from
Throws:
java.io.IOException In case of a I/O problem
    private final void readName(FontFileReader inthrows IOException {
        seekTab(in"name", 2);
        int i = in.getCurrentPos();
        int n = in.readTTFUShort();
        int j = in.readTTFUShort() + i - 2;
        i += 2 * 2;
        while (n-- > 0) {
            // getLogger().debug("Iteration: " + n);
            in.seekSet(i);
            final int platformID = in.readTTFUShort();
            final int encodingID = in.readTTFUShort();
            final int languageID = in.readTTFUShort();
            int k = in.readTTFUShort();
            int l = in.readTTFUShort();
            if (((platformID == 1 || platformID == 3)
                    && (encodingID == 0 || encodingID == 1))) {
                in.seekSet(j + in.readTTFUShort());
                String txt;
                if (platformID == 3) {
                    txt = in.readTTFString(lencodingID);
                } else {
                    txt = in.readTTFString(l);
                }
                if (.isDebugEnabled()) {