Index: src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java	(revision 1716744)
+++ src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java	(revision )
@@ -1,0 +1,0 @@
Index: src/java/org/apache/fop/fonts/truetype/GlyfTable.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/java/org/apache/fop/fonts/truetype/GlyfTable.java	(revision 1716744)
+++ src/java/org/apache/fop/fonts/truetype/GlyfTable.java	(revision )
@@ -1,0 +1,0 @@
Index: src/java/org/apache/fop/fonts/truetype/OFTableName.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/java/org/apache/fop/fonts/truetype/OFTableName.java	(revision 1716744)
+++ src/java/org/apache/fop/fonts/truetype/OFTableName.java	(revision )
@@ -1,0 +1,0 @@
Index: src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java	(revision 1716744)
+++ src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java	(revision )
@@ -1,0 +1,0 @@
Index: src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java	(revision 1716744)
+++ src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java	(revision )
@@ -1,0 +1,0 @@
Index: src/java/org/apache/fop/fonts/truetype/OpenFont.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/java/org/apache/fop/fonts/truetype/OpenFont.java	(revision 1716744)
+++ src/java/org/apache/fop/fonts/truetype/OpenFont.java	(revision )
@@ -53,6 +53,9 @@
 import org.apache.fop.fonts.FontUtil;
 import org.apache.fop.fonts.MultiByteFont;
 
+/**
+ * {@asf.todo} Add description
+ */
 public abstract class OpenFont {
 
     static final byte NTABS = 24;
@@ -164,9 +167,9 @@
 
     private Map<Integer, Map<Integer, Integer>> kerningTab; // for CIDs
     private Map<Integer, Map<Integer, Integer>> ansiKerningTab; // For winAnsiEncoding
-    private List<CMapSegment> cmaps;
-    protected List<UnicodeMapping> unicodeMappings;
 
+    private OFCMap cmap;
+
     private int upem;                                // unitsPerEm from "head" table
     protected int nhmtx;                               // Number of horizontal metrics
     private PostScriptVersion postScriptVersion;
@@ -214,17 +217,11 @@
     private int os2Descender;
     private int usWeightClass;
 
-    private short lastChar;
-
-    private int[] ansiWidth;
+    private int[] ansiWidths;
     private Map<Integer, List<Integer>> ansiIndex;
 
-    // internal mapping of glyph indexes to unicode indexes
-    // used for quick mappings in this class
-    private final Map<Integer, Integer> glyphToUnicodeMap = new HashMap<Integer, Integer>();
-    private final Map<Integer, Integer> unicodeToGlyphMap = new HashMap<Integer, Integer>();
-
     private boolean isCFF;
+    private short lastChar;
 
     // advanced typographic table support
     protected boolean useAdvanced;
@@ -233,7 +230,7 @@
     /**
      * Version of the PostScript table (<q>post</q>) contained in this font.
      */
-    public static enum PostScriptVersion {
+    public enum PostScriptVersion {
         /** PostScript table version 1.0. */
         V1,
         /** PostScript table version 2.0. */
@@ -241,7 +238,7 @@
         /** PostScript table version 3.0. */
         V3,
         /** Unknown version of the PostScript table. */
-        UNKNOWN;
+        UNKNOWN
     }
 
     /**
@@ -261,79 +258,10 @@
     public OpenFont(boolean useKerning, boolean useAdvanced) {
         this.useKerning = useKerning;
         this.useAdvanced = useAdvanced;
+        this.cmap = new OFCMap(this);
     }
 
     /**
-     * Key-value helper class.
-     */
-    static final class UnicodeMapping implements Comparable {
-
-        private final int unicodeIndex;
-        private final int glyphIndex;
-
-        UnicodeMapping(OpenFont font, int glyphIndex, int unicodeIndex) {
-            this.unicodeIndex = unicodeIndex;
-            this.glyphIndex = glyphIndex;
-            font.glyphToUnicodeMap.put(new Integer(glyphIndex), new Integer(unicodeIndex));
-            font.unicodeToGlyphMap.put(new Integer(unicodeIndex), new Integer(glyphIndex));
-        }
-
-        /**
-         * Returns the glyphIndex.
-         * @return the glyph index
-         */
-        public int getGlyphIndex() {
-            return glyphIndex;
-        }
-
-        /**
-         * Returns the unicodeIndex.
-         * @return the Unicode index
-         */
-        public int getUnicodeIndex() {
-            return unicodeIndex;
-        }
-
-
-        /** {@inheritDoc} */
-        public int hashCode() {
-            int hc = unicodeIndex;
-            hc = 19 * hc + (hc ^ glyphIndex);
-            return hc;
-        }
-
-        /** {@inheritDoc} */
-        public boolean equals(Object o) {
-            if (o instanceof UnicodeMapping) {
-                UnicodeMapping m = (UnicodeMapping) o;
-                if (unicodeIndex != m.unicodeIndex) {
-                    return false;
-                } else {
-                    return (glyphIndex == m.glyphIndex);
-                }
-            } else {
-                return false;
-            }
-        }
-
-        /** {@inheritDoc} */
-        public int compareTo(Object o) {
-            if (o instanceof UnicodeMapping) {
-                UnicodeMapping m = (UnicodeMapping) o;
-                if (unicodeIndex > m.unicodeIndex) {
-                    return 1;
-                } else if (unicodeIndex < m.unicodeIndex) {
-                    return -1;
-                } else {
-                    return 0;
-                }
-            } else {
-                return -1;
-            }
-        }
-    }
-
-    /**
      * Obtain directory table entry.
      * @param name (tag) of entry
      * @return a directory table entry or null if none found
@@ -384,254 +312,17 @@
     }
 
     /**
-     * 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.
+     * Read the cmap table.
+     * Set the unicodeIndex in the TTFMtxEntries and fills in the cmaps vector.
+     *
+     * @return {@code false} if no cmap table is present or the cmap subtable formats are unsupported.
+     * See: {@link OFCMap#read()}
      */
     protected boolean readCMAP() throws IOException {
-
-        unicodeMappings = new ArrayList<OpenFont.UnicodeMapping>();
-
-        if (!seekTab(fontFile, OFTableName.CMAP, 2)) {
-            return true;
+        return this.cmap.read();
-        }
+    }
-        int numCMap = fontFile.readTTFUShort();    // Number of cmap subtables
-        long cmapUniOffset = 0;
-        long symbolMapOffset = 0;
 
-        if (log.isDebugEnabled()) {
-            log.debug(numCMap + " cmap tables");
-        }
-
-        //Read offset for all tables. We are only interested in the unicode table
-        for (int i = 0; i < numCMap; i++) {
-            int cmapPID = fontFile.readTTFUShort();
-            int cmapEID = fontFile.readTTFUShort();
-            long cmapOffset = fontFile.readTTFLong();
-
-            if (log.isDebugEnabled()) {
-                log.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(cmapUniOffset, 1);
-        } else if (symbolMapOffset > 0) {
-            return readUnicodeCmap(symbolMapOffset, 0);
-        } else {
-            log.fatal("Unsupported TrueType font: No Unicode or Symbol cmap table"
-                    + " not present. Aborting");
-            return false;
-        }
-    }
-
-    private boolean readUnicodeCmap(long cmapUniOffset, int encodingID)
-            throws IOException {
-        //Read CMAP table and correct mtxTab.index
-        int mtxPtr = 0;
-
-        // Read unicode cmap
-        seekTab(fontFile, OFTableName.CMAP, cmapUniOffset);
-        int cmapFormat = fontFile.readTTFUShort();
-        /*int cmap_length =*/ fontFile.readTTFUShort(); //skip cmap length
-
-        if (log.isDebugEnabled()) {
-            log.debug("CMAP format: " + cmapFormat);
-        }
-
-        if (cmapFormat == 4) {
-            fontFile.skip(2);    // Skip version number
-            int cmapSegCountX2 = fontFile.readTTFUShort();
-            int cmapSearchRange = fontFile.readTTFUShort();
-            int cmapEntrySelector = fontFile.readTTFUShort();
-            int cmapRangeShift = fontFile.readTTFUShort();
-
-            if (log.isDebugEnabled()) {
-                log.debug("segCountX2   : " + cmapSegCountX2);
-                log.debug("searchRange  : " + cmapSearchRange);
-                log.debug("entrySelector: " + cmapEntrySelector);
-                log.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] = fontFile.readTTFUShort();
-            }
-
-            fontFile.skip(2);    // Skip reservedPad
-
-            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
-                cmapStartCounts[i] = fontFile.readTTFUShort();
-            }
-
-            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
-                cmapDeltas[i] = fontFile.readTTFShort();
-            }
-
-            //int startRangeOffset = in.getCurrentPos();
-
-            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
-                cmapRangeOffsets[i] = fontFile.readTTFUShort();
-            }
-
-            int glyphIdArrayOffset = fontFile.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.length; i++) {
-
-                if (log.isTraceEnabled()) {
-                    log.trace(i + ": " + cmapStartCounts[i]
-                                                         + " - " + cmapEndCounts[i]);
-                }
-                if (log.isDebugEnabled()) {
-                    if (isInPrivateUseArea(cmapStartCounts[i], cmapEndCounts[i])) {
-                        log.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 > lastChar) {
-                        lastChar = (short)j;
-                    }
-
-                    if (j < 256) {
-                        eightBitGlyphs.set(j);
-                    }
-
-                    if (mtxPtr < mtxTab.length) {
-                        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;
-                            fontFile.seekSet(glyphOffset);
-                            glyphIdx = (fontFile.readTTFUShort() + cmapDeltas[i])
-                                       & 0xffff;
-                            //mtxTab[glyphIdx].setName(mtxTab[glyphIdx].getName() + " - "+(char)j);
-                            unicodeMappings.add(new UnicodeMapping(this, glyphIdx, j));
-                            mtxTab[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
-                                    unicodeMappings.add(new UnicodeMapping(this, glyphIdx, mapped));
-                                    mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(mapped));
-                                }
-                            }
-
-                            // Also add winAnsiWidth
-                            List<Integer> v = ansiIndex.get(new Integer(j));
-                            if (v != null) {
-                                for (Integer aIdx : v) {
-                                    ansiWidth[aIdx.intValue()]
-                                        = mtxTab[glyphIdx].getWx();
-
-                                    if (log.isTraceEnabled()) {
-                                        log.trace("Added width "
-                                                + mtxTab[glyphIdx].getWx()
-                                                + " uni: " + j
-                                                + " ansi: " + aIdx.intValue());
-                                    }
-                                }
-                            }
-
-                            if (log.isTraceEnabled()) {
-                                log.trace("Idx: "
-                                        + glyphIdx
-                                        + " Delta: " + cmapDeltas[i]
-                                        + " Unicode: " + j
-                                        + " name: " + mtxTab[glyphIdx].getName());
-                            }
-                        } else {
-                            glyphIdx = (j + cmapDeltas[i]) & 0xffff;
-
-                            if (glyphIdx < mtxTab.length) {
-                                mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
-                            } else {
-                                log.debug("Glyph " + glyphIdx
-                                                   + " out of range: "
-                                                   + mtxTab.length);
-                            }
-
-                            unicodeMappings.add(new UnicodeMapping(this, glyphIdx, j));
-                            if (glyphIdx < mtxTab.length) {
-                                mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
-                            } else {
-                                log.debug("Glyph " + glyphIdx
-                                                   + " out of range: "
-                                                   + mtxTab.length);
-                            }
-
-                            // Also add winAnsiWidth
-                            List<Integer> v = ansiIndex.get(new Integer(j));
-                            if (v != null) {
-                                for (Integer aIdx : v) {
-                                    ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx();
-                                }
-                            }
-
-                            //getLogger().debug("IIdx: " +
-                            //    mtxPtr +
-                            //    " Delta: " + cmap_deltas[i] +
-                            //    " Unicode: " + j +
-                            //    " name: " +
-                            //    mtxTab[(j+cmap_deltas[i]) & 0xffff].name);
-
-                        }
-                        if (glyphIdx < mtxTab.length) {
-                            if (mtxTab[glyphIdx].getUnicodeIndex().size() < 2) {
-                                mtxPtr++;
-                            }
-                        }
-                    }
-                }
-            }
-        } else {
-            log.error("Cmap format not supported: " + cmapFormat);
-            return false;
-        }
-        return true;
-    }
-
-    private boolean isInPrivateUseArea(int start, int end) {
-        return (isInPrivateUseArea(start) || isInPrivateUseArea(end));
-    }
-
-    private boolean isInPrivateUseArea(int unicode) {
-        return (unicode >= 0xE000 && unicode <= 0xF8FF);
-    }
-
     /**
-     *
      * @return mmtx data
      */
     public List<OFMtxEntry> getMtx() {
@@ -639,27 +330,6 @@
     }
 
     /**
-     * Print first char/last char
-     */
-    /* not used
-    private void printMaxMin() {
-        int min = 255;
-        int max = 0;
-        for (int i = 0; i < mtxTab.length; i++) {
-            if (mtxTab[i].getIndex() < min) {
-                min = mtxTab[i].getIndex();
-            }
-            if (mtxTab[i].getIndex() > max) {
-                max = mtxTab[i].getIndex();
-            }
-        }
-        log.info("Min: " + min);
-        log.info("Max: " + max);
-    }
-    */
-
-
-    /**
      * Reads the font using a FontFileReader.
      *
      * @param in The FontFileReader to use
@@ -674,9 +344,9 @@
      * and fill with the missingwidth
      */
     protected void initAnsiWidths() {
-        ansiWidth = new int[256];
+        ansiWidths = new int[256];
         for (int i = 0; i < 256; i++) {
-            ansiWidth[i] = mtxTab[0].getWx();
+            ansiWidths[i] = mtxTab[0].getWx();
         }
 
         // Create an index hash to the ansiWidth
@@ -684,8 +354,8 @@
         // same char (eg bullet) is repeated more than one place
         ansiIndex = new HashMap<Integer, List<Integer>>();
         for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
-            Integer ansi = new Integer(i);
-            Integer uni = new Integer(Glyphs.WINANSI_ENCODING[i]);
+            Integer ansi = i;
+            Integer uni = (int) Glyphs.WINANSI_ENCODING[i];
 
             List<Integer> v = ansiIndex.get(uni);
             if (v == null) {
@@ -745,8 +415,6 @@
             return false;
         }
 
-        // Create cmaps for bfentries
-        createCMaps();
         updateBBoxAndOffset();
 
         if (useKerning) {
@@ -762,8 +430,8 @@
      * Reads a font.
      *
      * @param in FontFileReader to read from
-     * @param name Name to be checked for in the font file
-     * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
+     * @param header Name to be checked for in the font file
+     * @param mbfont Map of glyphs (glyphs has old index as (Integer) key and
      * new index as (Integer) value)
      * @throws IOException in case of an I/O problem
      */
@@ -796,37 +464,6 @@
 
     }
 
-    protected void createCMaps() {
-        cmaps = new ArrayList<CMapSegment>();
-        int unicodeStart;
-        int glyphStart;
-        int unicodeEnd;
-        if (unicodeMappings.isEmpty()) {
-            return;
-        }
-        Iterator<UnicodeMapping> e = unicodeMappings.iterator();
-        UnicodeMapping um = e.next();
-        UnicodeMapping lastMapping = um;
-
-        unicodeStart = um.getUnicodeIndex();
-        glyphStart = um.getGlyphIndex();
-
-        while (e.hasNext()) {
-            um = e.next();
-            if (((lastMapping.getUnicodeIndex() + 1) != um.getUnicodeIndex())
-                    || ((lastMapping.getGlyphIndex() + 1) != um.getGlyphIndex())) {
-                unicodeEnd = lastMapping.getUnicodeIndex();
-                cmaps.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart));
-                unicodeStart = um.getUnicodeIndex();
-                glyphStart = um.getGlyphIndex();
-            }
-            lastMapping = um;
-        }
-
-        unicodeEnd = lastMapping.getUnicodeIndex();
-        cmaps.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart));
-    }
-
     /**
      * Returns the PostScript name of the font.
      * @return String The PostScript name
@@ -970,8 +607,7 @@
      * @return An array of bounding box values
      */
     public int[] getBBoxRaw() {
-        int[] bbox = {fontBBox1, fontBBox2, fontBBox3, fontBBox4};
-        return bbox;
+        return new int[]{fontBBox1, fontBBox2, fontBBox3, fontBBox4};
     }
 
     /**
@@ -999,6 +635,10 @@
         return lastChar;
     }
 
+    void setLastChar(short lastChar) {
+        this.lastChar = lastChar;
+    }
+
     /**
      * Returns the index of the first character.
      * @return short Index of the first character
@@ -1019,6 +659,18 @@
         return wx;
     }
 
+    int[] getAnsiWidths() {
+        return this.ansiWidths;
+    }
+
+    List<Integer> getAnsiIndices(int codepoint) {
+        return this.ansiIndex.get(codepoint);
+    }
+
+    OFCMap.UnicodeMapping[] getUnicodeMappings() {
+        return this.cmap.getUnicodeMappings();
+    }
+
     public Rectangle[] getBoundingBoxes() {
         Rectangle[] boundingBoxes = new Rectangle[mtxTab.length];
         for (int i = 0; i < boundingBoxes.length; i++) {
@@ -1053,7 +705,7 @@
      * @return int Standard width
      */
     public int getCharWidth(int idx) {
-        return convertTTFUnit2PDFUnit(ansiWidth[idx]);
+        return convertTTFUnit2PDFUnit(ansiWidths[idx]);
     }
 
     /**
@@ -1062,8 +714,8 @@
      * @return int Width in it's raw form stored in the font
      */
     public int getCharWidthRaw(int idx) {
-        if (ansiWidth != null) {
-            return ansiWidth[idx];
+        if (ansiWidths != null) {
+            return ansiWidths[idx];
         }
         return -1;
     }
@@ -1375,11 +1027,7 @@
             fontFile.skip(2);
 
             int fsType = fontFile.readTTFUShort();
-            if (fsType == 2) {
-                isEmbeddable = false;
-            } else {
-                isEmbeddable = true;
-            }
+            isEmbeddable = (fsType != 2);
             fontFile.skip(8 * 2);
             strikeoutThickness = fontFile.readTTFShort();
             strikeoutPosition = fontFile.readTTFShort();
@@ -1444,11 +1092,7 @@
             int serifStyle = fontFile.readTTFUByte();
             serifStyle = serifStyle >> 6;
             serifStyle = serifStyle & 3;
-            if (serifStyle == 1) {
-                hasSerifs = false;
-            } else {
-                hasSerifs = true;
-            }
+            hasSerifs = (serifStyle != 1);
             return true;
         } else {
             return false;
@@ -1503,30 +1147,30 @@
         int localXHeight = 0;
         int localAscender = 0;
         int localDescender = 0;
-        for (int i = 0; i < mtxTab.length; i++) {
-            if ("H".equals(mtxTab[i].getName())) {
-                localCapHeight = mtxTab[i].getBoundingBox()[3];
-            } else if ("x".equals(mtxTab[i].getName())) {
-                localXHeight = mtxTab[i].getBoundingBox()[3];
-            } else if ("d".equals(mtxTab[i].getName())) {
-                localAscender = mtxTab[i].getBoundingBox()[3];
-            } else if ("p".equals(mtxTab[i].getName())) {
-                localDescender = mtxTab[i].getBoundingBox()[1];
+        for (OFMtxEntry mtxEntry : mtxTab) {
+            if ("H".equals(mtxEntry.getName())) {
+                localCapHeight = mtxEntry.getBoundingBox()[3];
+            } else if ("x".equals(mtxEntry.getName())) {
+                localXHeight = mtxEntry.getBoundingBox()[3];
+            } else if ("d".equals(mtxEntry.getName())) {
+                localAscender = mtxEntry.getBoundingBox()[3];
+            } else if ("p".equals(mtxEntry.getName())) {
+                localDescender = mtxEntry.getBoundingBox()[1];
             } else {
                 // OpenType Fonts with a version 3.0 "post" table don't have glyph names.
                 // Use Unicode indices instead.
-                List unicodeIndex = mtxTab[i].getUnicodeIndex();
+                List unicodeIndex = mtxEntry.getUnicodeIndex();
                 if (unicodeIndex.size() > 0) {
                     //Only the first index is used
-                    char ch = (char)((Integer)unicodeIndex.get(0)).intValue();
+                    char ch = (char) ((Integer) unicodeIndex.get(0)).intValue();
                     if (ch == 'H') {
-                        localCapHeight = mtxTab[i].getBoundingBox()[3];
+                        localCapHeight = mtxEntry.getBoundingBox()[3];
                     } else if (ch == 'x') {
-                        localXHeight = mtxTab[i].getBoundingBox()[3];
+                        localXHeight = mtxEntry.getBoundingBox()[3];
                     } else if (ch == 'd') {
-                        localAscender = mtxTab[i].getBoundingBox()[3];
+                        localAscender = mtxEntry.getBoundingBox()[3];
                     } else if (ch == 'p') {
-                        localDescender = mtxTab[i].getBoundingBox()[1];
+                        localDescender = mtxEntry.getBoundingBox()[1];
                     }
                 }
             }
@@ -1583,7 +1227,7 @@
             for (int n = fontFile.readTTFUShort(); n > 0; n--) {
                 fontFile.skip(2 * 2);
                 int k = fontFile.readTTFUShort();
-                if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) {
+                if ((k & 1) == 0 || (k & 2) != 0 || (k & 4) != 0) {
                     return;
                 }
                 if ((k >> 8) != 0) {
@@ -1598,8 +1242,8 @@
                     int kpx = fontFile.readTTFShort();
                     if (kpx != 0) {
                         // CID kerning table entry, using unicode indexes
-                        final Integer iObj = glyphToUnicode(i);
-                        final Integer u2 = glyphToUnicode(j);
+                        final Integer iObj = cmap.glyphToUnicode(i);
+                        final Integer u2 = cmap.glyphToUnicode(j);
                         if (iObj == null) {
                             // happens for many fonts (Ubuntu font set),
                             // stray entries in the kerning table??
@@ -1613,7 +1257,7 @@
                             if (adjTab == null) {
                                 adjTab = new HashMap<Integer, Integer>();
                             }
-                            adjTab.put(u2, new Integer(convertTTFUnit2PDFUnit(kpx)));
+                            adjTab.put(u2, convertTTFUnit2PDFUnit(kpx));
                             kerningTab.put(iObj, adjTab);
                         }
                     }
@@ -1626,32 +1270,28 @@
 
             for (Map.Entry<Integer, Map<Integer, Integer>> e1 : kerningTab.entrySet()) {
                 Integer unicodeKey1 = e1.getKey();
-                Integer cidKey1 = unicodeToGlyph(unicodeKey1);
+                Integer cidKey1 = cmap.unicodeToGlyph(unicodeKey1);
                 Map<Integer, Integer> akpx = new HashMap<Integer, Integer>();
                 Map<Integer, Integer> ckpx = e1.getValue();
 
                 for (Map.Entry<Integer, Integer> e : ckpx.entrySet()) {
                     Integer unicodeKey2 = e.getKey();
-                    Integer cidKey2 = unicodeToGlyph(unicodeKey2.intValue());
+                    Integer cidKey2 = cmap.unicodeToGlyph(unicodeKey2);
                     Integer kern = e.getValue();
 
-                    Iterator uniMap = mtxTab[cidKey2.intValue()].getUnicodeIndex().listIterator();
-                    while (uniMap.hasNext()) {
-                        Integer unicodeKey = (Integer)uniMap.next();
-                        Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
-                        for (int u = 0; u < ansiKeys.length; u++) {
-                            akpx.put(ansiKeys[u], kern);
+                    for (int unicodeKey : mtxTab[cidKey2].getUnicodeIndex()) {
+                        int ansiKey = unicodeToWinAnsi(unicodeKey);
+                        if (ansiKey > 0) {
+                            akpx.put(ansiKey, kern);
                         }
                     }
                 }
 
                 if (akpx.size() > 0) {
-                    Iterator uniMap = mtxTab[cidKey1.intValue()].getUnicodeIndex().listIterator();
-                    while (uniMap.hasNext()) {
-                        Integer unicodeKey = (Integer)uniMap.next();
-                        Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
-                        for (int u = 0; u < ansiKeys.length; u++) {
-                            ansiKerningTab.put(ansiKeys[u], akpx);
+                    for (Integer unicodeKey : mtxTab[cidKey1].getUnicodeIndex()) {
+                        int ansiKey = unicodeToWinAnsi(unicodeKey);
+                        if (ansiKey > 0) {
+                            ansiKerningTab.put(ansiKey, akpx);
                         }
                     }
                 }
@@ -1686,7 +1326,7 @@
     private void streamGlyf(TTFGlyphOutputStream glyphOut, byte[] fontFile, int tableOffset,
             int tableLength) throws IOException {
         //Stream all but the last glyph
-        int glyphStart = 0;
+        int glyphStart;
         int glyphEnd = 0;
         glyphOut.startGlyphStream();
         for (int i = 0; i < mtxTab.length - 1; i++) {
@@ -1726,7 +1366,7 @@
      * @return the font's cmap
      */
     public List<CMapSegment> getCMaps() {
-        return cmaps;
+        return cmap.getSegments();
     }
 
     /**
@@ -1843,18 +1483,13 @@
         }
     }
 
-    /*
-     * Helper classes, they are not very efficient, but that really
-     * doesn't matter...
-     */
-    private Integer[] unicodeToWinAnsi(int unicode) {
-        List<Integer> ret = new ArrayList<Integer>();
-        for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
+    private int unicodeToWinAnsi(int unicode) {
+        for (int i = Glyphs.WINANSI_ENCODING.length; --i >= 32;) {
             if (unicode == Glyphs.WINANSI_ENCODING[i]) {
-                ret.add(new Integer(i));
+                return i;
             }
         }
-        return ret.toArray(new Integer[ret.size()]);
+        return -1;
     }
 
     /**
@@ -1890,32 +1525,6 @@
         return units + " -> " + convertTTFUnit2PDFUnit(units) + " internal units";
     }
 
-    /**
-     * Map a glyph index to the corresponding unicode code point
-     *
-     * @param glyphIndex
-     * @return unicode code point
-     */
-    private Integer glyphToUnicode(int glyphIndex) {
-        return glyphToUnicodeMap.get(new Integer(glyphIndex));
-    }
-
-    /**
-     * Map a unicode code point to the corresponding glyph index
-     *
-     * @param unicodeIndex unicode code point
-     * @return glyph index
-     */
-    private Integer unicodeToGlyph(int unicodeIndex) throws IOException {
-        final Integer result
-            = unicodeToGlyphMap.get(new Integer(unicodeIndex));
-        if (result == null) {
-            throw new IOException(
-                    "Glyph index not found for unicode value " + unicodeIndex);
-        }
-        return result;
-    }
-
     String getGlyphName(int glyphIndex) {
         return mtxTab[glyphIndex].getName();
     }
@@ -1925,12 +1534,8 @@
      * @return true if advanced (typographic) table is present
      */
     public boolean hasAdvancedTable() {
-        if (advancedTableReader != null) {
-            return  advancedTableReader.hasAdvancedTable();
-        } else {
-            return false;
+        return advancedTableReader != null && advancedTableReader.hasAdvancedTable();
-        }
+    }
-    }
 
     /**
      * Returns the GDEF table or null if none present.
@@ -1975,9 +1580,6 @@
     public static void main(String[] args) {
         InputStream stream = null;
         try {
-            boolean useKerning = true;
-            boolean useAdvanced = true;
-
             stream = new FileInputStream(args[0]);
             FontFileReader reader = new FontFileReader(stream);
 
@@ -1988,7 +1590,7 @@
 
             String header = OFFontLoader.readHeader(reader);
             boolean isCFF = header.equals("OTTO");
-            OpenFont otfFile = (isCFF) ? new OTFFile() : new TTFFile(useKerning, useAdvanced);
+            OpenFont otfFile = (isCFF) ? new OTFFile() : new TTFFile(true, true);
             otfFile.readFont(reader, header, name);
             otfFile.printStuff();
 
Index: src/java/org/apache/fop/fonts/truetype/OTFFile.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/java/org/apache/fop/fonts/truetype/OTFFile.java	(revision 1716744)
+++ src/java/org/apache/fop/fonts/truetype/OTFFile.java	(revision )
@@ -53,9 +53,9 @@
     protected void updateBBoxAndOffset() throws IOException {
         List<Mapping> gidMappings = getGIDMappings(fileFont);
         Map<Integer, String> sidNames = constructNameMap(gidMappings);
-        UnicodeMapping[] mappings = unicodeMappings.toArray(new UnicodeMapping[unicodeMappings.size()]);
-        for (int i = 0; i < mappings.length; i++) {
-            int glyphIdx = mappings[i].getGlyphIndex();
+        OFCMap.UnicodeMapping[] mappings = getUnicodeMappings();
+        for (OFCMap.UnicodeMapping mapping : mappings) {
+            int glyphIdx = mapping.getGlyphIndex();
             Mapping m = gidMappings.get(glyphIdx);
             String name = sidNames.get(m.getSID());
             mtxTab[glyphIdx].setName(name);
Index: src/java/org/apache/fop/fonts/truetype/OFCMap.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/java/org/apache/fop/fonts/truetype/OFCMap.java	(revision )
+++ src/java/org/apache/fop/fonts/truetype/OFCMap.java	(revision )
@@ -0,0 +1,615 @@
+/*
+ * 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$ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.fop.fonts.CMapSegment;
+
+class OFCMap {
+
+    //CMAP Platform IDs
+    static final int PLATFORM_ID_UNICODE    = 0x0000;
+    static final int PLATFORM_ID_MACINTOSH  = 0x0001;
+    static final int PLATFORM_ID_ISO        = 0x0002; //Note: deprecated in OTF spec
+    static final int PLATFORM_ID_WINDOWS    = 0x0003;
+    static final int PLATFORM_ID_CUSTOM     = 0x0004;
+
+    //CMAP Unicode Encoding IDs
+    static final int UNICODE_1_0_ENCODING                   = 0x0000;
+    static final int UNICODE_1_1_ENCODING                   = 0x0001;
+    static final int UNICODE_ISO_10646_ENCODING             = 0x0002;
+    static final int UNICODE_2_0_BMP_ENCODING               = 0x0003;
+    static final int UNICODE_2_0_FULL_ENCODING              = 0x0004;
+    static final int UNICODE_VARIATION_SEQUENCES_ENCODING   = 0x0005;
+    static final int UNICODE_FULL_ENCODING                  = 0x0006;
+
+    //CMAP Macintosh Encoding IDs
+    static final int MACINTOSH_ROMAN_ENCODING               = 0x0000;
+    static final int MACINTOSH_JAPANESE_ENCODING            = 0x0001;
+    static final int MACINTOSH_CHINESE_TRADITIONAL_ENCODING = 0x0002;
+    static final int MACINTOSH_KOREAN_ENCODING              = 0x0003;
+    static final int MACINTOSH_ARABIC_ENCODING              = 0x0004;
+    static final int MACINTOSH_HEBREW_ENCODING              = 0x0005;
+    static final int MACINTOSH_GREEK_ENCODING               = 0x0006;
+    static final int MACINTOSH_RUSSIAN_ENCODING             = 0x0007;
+    static final int MACINTOSH_RSYMBOL_ENCODING             = 0x0008;
+    static final int MACINTOSH_DEVANAGARI_ENCODING          = 0x0009;
+    static final int MACINTOSH_GURMUKHI_ENCODING            = 0x000A;
+    static final int MACINTOSH_GUJARATI_ENCODING            = 0x000B;
+    static final int MACINTOSH_ORIYA_ENCODING               = 0x000C;
+    static final int MACINTOSH_BENGALI_ENCODING             = 0x000D;
+    static final int MACINTOSH_TAMIL_ENCODING               = 0x000E;
+    static final int MACINTOSH_TELUGU_ENCODING              = 0x000F;
+    static final int MACINTOSH_KANNADA_ENCODING             = 0x0010;
+    static final int MACINTOSH_MALAYALAM_ENCODING           = 0x0011;
+    static final int MACINTOSH_SINHALESE_ENCODING           = 0x0012;
+    static final int MACINTOSH_BURMESE_ENCODING             = 0x0013;
+    static final int MACINTOSH_KHMER_ENCODING               = 0x0014;
+    static final int MACINTOSH_THAI_ENCODING                = 0x0015;
+    static final int MACINTOSH_LAOTIAN_ENCODING             = 0x0016;
+    static final int MACINTOSH_GEORGIAN_ENCODING            = 0x0017;
+    static final int MACINTOSH_ARMENIAN_ENCODING            = 0x0018;
+    static final int MACINTOSH_CHINESE_SIMPLIFIED_ENCODING  = 0x0019;
+    static final int MACINTOSH_TIBETAN_ENCODING             = 0x001A;
+    static final int MACINTOSH_MONGOLIAN_ENCODING           = 0x001B;
+    static final int MACINTOSH_GEEZ_ENCODING                = 0x001C;
+    static final int MACINTOSH_SLAVIC_ENCODING              = 0x001D;
+    static final int MACINTOSH_VIETNAMESE_ENCODING          = 0x001E;
+    static final int MACINTOSH_SINDHI_ENCODING              = 0x001F;
+    static final int MACINTOSH_UNINTERPRETED_ENCODING       = 0x0020;
+
+    //CMAP ISO Encoding IDs -- Note: Deprecated
+    static final int ISO_7BIT_ASCII_ENCODING            = 0x0000;
+    static final int ISO_10646_ENCODING                 = 0x0001;
+    static final int ISO_8859_1_ENCODING                = 0x0002;
+
+    //CMAP Windows Encoding IDs
+    static final int WINDOWS_SYMBOL_ENCODING            = 0x0000;
+    static final int WINDOWS_UNICODE_UCS2_ENCODING      = 0x0001;
+    static final int WINDOWS_SHIFTJIS_ENCODING          = 0x0002;
+    static final int WINDOWS_PRC_ENCODING               = 0x0003;
+    static final int WINDOWS_BIG5_ENCODING              = 0x0004;
+    static final int WINDOWS_WANSUNG_ENCODING           = 0x0005;
+    static final int WINDOWS_JOHAB_ENCODING             = 0x0006;
+    static final int WINDOWS_UNICODE_UCS4_ENCODING      = 0x000A;
+
+    //CMAP Subtable Format Constants
+    static final int FORMAT_BYTE_ENCODING               = 0x0000;
+    static final int FORMAT_HIGH_BYTE_TABLE             = 0x0002;
+    static final int FORMAT_SEGMENT_TO_DELTA            = 0x0004;
+    static final int FORMAT_TRIMMED_TABLE               = 0x0006;
+    static final int FORMAT_MIXED_16_32_BIT             = 0x0008;
+    static final int FORMAT_TRIMMED_ARRAY               = 0x000A;
+    static final int FORMAT_SEGMENTED_COVERAGE          = 0x000C;
+    static final int FORMAT_MANY_TO_ONE_RANGE           = 0x000D;
+    static final int FORMAT_UNICODE_VARIATION_SEQUENCES = 0x000E;
+
+    private OpenFont font;
+
+    protected Log log;
+
+    // internal mapping of glyph indexes to unicode indexes
+    // used for quick mappings in this class
+    private final Map<Integer, Integer> glyphToUnicodeMap = new HashMap<Integer, Integer>();
+    private final Map<Integer, Integer> unicodeToGlyphMap = new HashMap<Integer, Integer>();
+
+    List<UnicodeMapping> unicodeMappings;
+    private List<CMapSegment> segments;
+
+    /**
+     * Key-value helper class.
+     */
+    static final class UnicodeMapping implements Comparable {
+
+        private final int unicodeIndex;
+        private final int glyphIndex;
+
+        UnicodeMapping(OFCMap cmap, int glyphIndex, int unicodeIndex) {
+            this.unicodeIndex = unicodeIndex;
+            this.glyphIndex = glyphIndex;
+            cmap.glyphToUnicodeMap.put(glyphIndex, unicodeIndex);
+            cmap.unicodeToGlyphMap.put(unicodeIndex, glyphIndex);
+        }
+
+        /**
+         * Returns the glyphIndex.
+         * @return the glyph index
+         */
+        int getGlyphIndex() {
+            return glyphIndex;
+        }
+
+        /**
+         * Returns the unicodeIndex.
+         * @return the Unicode index
+         */
+        int getUnicodeIndex() {
+            return unicodeIndex;
+        }
+
+
+        /** {@inheritDoc} */
+        public int hashCode() {
+            int hc = unicodeIndex;
+            hc = 19 * hc + (hc ^ glyphIndex);
+            return hc;
+        }
+
+        /** {@inheritDoc} */
+        public boolean equals(Object o) {
+            if (o instanceof UnicodeMapping) {
+                UnicodeMapping m = (UnicodeMapping) o;
+                return unicodeIndex == m.unicodeIndex && (glyphIndex == m.glyphIndex);
+            } else {
+                return false;
+            }
+        }
+
+        /** {@inheritDoc} */
+        public int compareTo(Object o) {
+            if (o instanceof UnicodeMapping) {
+                UnicodeMapping m = (UnicodeMapping) o;
+                if (unicodeIndex > m.unicodeIndex) {
+                    return 1;
+                } else if (unicodeIndex < m.unicodeIndex) {
+                    return -1;
+                } else {
+                    return 0;
+                }
+            } else {
+                return -1;
+            }
+        }
+    }
+
+    /**
+     * Main constructor
+     * @param font  reference to the containing {@link OpenFont}
+     */
+    OFCMap(OpenFont font) {
+        this.font = font;
+        this.log = font.log;
+    }
+
+    /**
+     * @return {@code true} if this cmap can be read and is fully supported
+     * Currently only unicode segment-to-delta cmaps are supported.
+     *
+     * @throws IOException if there was an error while reading from the font file.
+     */
+    boolean read() throws IOException {
+
+        FontFileReader fontFile = this.font.fontFile;
+
+        if (!this.font.seekTab(fontFile, OFTableName.CMAP, 2)) {
+            return true;
+        }
+        this.unicodeMappings = new ArrayList<UnicodeMapping>();
+        int numCMap = fontFile.readTTFUShort();    // Number of cmap subtables
+        long cmapUniOffset = 0;
+        long symbolMapOffset = 0;
+
+        if (log.isDebugEnabled()) {
+            log.debug(numCMap + " cmap tables");
+        }
+
+        int platformID = -1;
+        int encodingID = -1;
+        //Read offset for all tables. We are only interested in the unicode table
+        for (int i = 0; i < numCMap; i++) {
+            platformID = fontFile.readTTFUShort();
+            encodingID = fontFile.readTTFUShort();
+            long cmapOffset = fontFile.readTTFLong();
+
+            if (log.isDebugEnabled()) {
+                log.debug("Platform ID: " + platformID + " Encoding: " + encodingID);
+            }
+
+            if ((platformID == PLATFORM_ID_WINDOWS
+                        && (encodingID == WINDOWS_UNICODE_UCS2_ENCODING || encodingID == WINDOWS_UNICODE_UCS4_ENCODING))
+                    || (platformID == PLATFORM_ID_UNICODE
+                        && (encodingID == UNICODE_2_0_BMP_ENCODING || encodingID == UNICODE_2_0_FULL_ENCODING))) {
+                cmapUniOffset = cmapOffset;
+            }
+            if (platformID == PLATFORM_ID_WINDOWS && encodingID == WINDOWS_SYMBOL_ENCODING
+                    || (platformID == PLATFORM_ID_MACINTOSH && encodingID == MACINTOSH_ROMAN_ENCODING)) {
+                symbolMapOffset = cmapOffset;
+            }
+        }
+
+        if (cmapUniOffset > 0) {
+            return readUnicodeCmap(cmapUniOffset, false);
+        } else if (symbolMapOffset > 0) {
+            return readUnicodeCmap(symbolMapOffset, true);
+        } else {
+            log.fatal("Unsupported TrueType font: No supported Unicode or Symbol cmap table"
+                    + " present. (Platform ID = " + platformID + ", Encoding ID = " + encodingID + ")");
+            return false;
+        }
+
+    }
+
+    private boolean readUnicodeCmap(long cmapUniOffset, boolean isWinSymbolEncoding)
+            throws IOException {
+
+        FontFileReader fontFile = this.font.fontFile;
+
+        // Read unicode cmap
+        this.font.seekTab(fontFile, OFTableName.CMAP, cmapUniOffset);
+        int cmapFormat = fontFile.readTTFUShort();
+
+        if (log.isDebugEnabled()) {
+            log.debug("CMAP format: " + cmapFormat);
+        }
+
+        switch (cmapFormat) {
+            case FORMAT_BYTE_ENCODING:
+                readByteEncodingSubTable(fontFile);
+                break;
+            case FORMAT_SEGMENT_TO_DELTA:
+                readSegmentToDeltaSubTable(fontFile, isWinSymbolEncoding);
+                break;
+            case FORMAT_SEGMENTED_COVERAGE:
+                readSegmentedCoverageSubTable(fontFile);
+                break;
+            default:
+                log.error("Cmap format not supported: " + cmapFormat);
+                return false;
+        }
+        return true;
+    }
+
+    private void readByteEncodingSubTable(FontFileReader fontFile) throws IOException {
+
+        fontFile.skip(2); // skip length
+        fontFile.skip(2); // skip language
+
+        OFMtxEntry[] mtxTab = this.font.mtxTab;
+        int[] ansiWidths = this.font.getAnsiWidths();
+        int glyphIdx;
+        for (int i = 0; i <= 255; i++) {
+            glyphIdx = fontFile.readTTFUByte();
+            unicodeMappings.add(new UnicodeMapping(this, glyphIdx, i));
+            mtxTab[glyphIdx].getUnicodeIndex().add(i);
+
+            // Also add winAnsiWidth
+            List<Integer> v = this.font.getAnsiIndices(i);
+            if (v != null) {
+                for (Integer aIdx : v) {
+                    ansiWidths[aIdx] = mtxTab[glyphIdx].getWx();
+
+                    if (log.isTraceEnabled()) {
+                        log.trace("Added width "
+                                + mtxTab[glyphIdx].getWx()
+                                + " uni: " + i
+                                + " ansi: " + aIdx);
+                    }
+                }
+            }
+        }
+    }
+
+    private void readSegmentToDeltaSubTable(FontFileReader fontFile, boolean isWinSymbolEncoding)
+            throws IOException {
+
+        fontFile.skip(2); // skip length
+        fontFile.skip(2); // skip language
+
+        int cmapSegCountX2 = fontFile.readTTFUShort();
+        int cmapSearchRange = fontFile.readTTFUShort();
+        int cmapEntrySelector = fontFile.readTTFUShort();
+        int cmapRangeShift = fontFile.readTTFUShort();
+
+        if (log.isDebugEnabled()) {
+            log.debug("segCountX2   : " + cmapSegCountX2);
+            log.debug("searchRange  : " + cmapSearchRange);
+            log.debug("entrySelector: " + cmapEntrySelector);
+            log.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] = fontFile.readTTFUShort();
+        }
+
+        fontFile.skip(2); // skip reservedPad
+
+        for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+            cmapStartCounts[i] = fontFile.readTTFUShort();
+        }
+
+        for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+            cmapDeltas[i] = fontFile.readTTFShort();
+        }
+
+        for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+            cmapRangeOffsets[i] = fontFile.readTTFUShort();
+        }
+
+        int glyphIdArrayOffset = fontFile.getCurrentPos();
+
+        BitSet eightBitGlyphs = new BitSet(256);
+
+        //Read CMAP table and correct mtxTab.index
+        int mtxPtr = 0;
+
+        // Insert the unicode id for the glyphs in mtxTab
+        // and fill in the segments ArrayList
+        for (int i = 0; i < cmapStartCounts.length; i++) {
+
+            if (log.isTraceEnabled()) {
+                log.trace(i + ": " + cmapStartCounts[i]
+                        + " - " + cmapEndCounts[i]);
+            }
+
+            if (log.isDebugEnabled()) {
+                if (isInPrivateUseArea(cmapStartCounts[i], cmapEndCounts[i])) {
+                    log.debug("Font contains glyphs in the Unicode private use area: "
+                            + Integer.toHexString(cmapStartCounts[i]) + " - "
+                            + Integer.toHexString(cmapEndCounts[i]));
+                }
+            }
+
+            short lastChar = this.font.getLastChar();
+            OFMtxEntry[] mtxTab = this.font.mtxTab;
+            int[] ansiWidths = this.font.getAnsiWidths();
+
+            for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) {
+
+                if (j < 256) {
+                    if (j > lastChar) {
+                        // update lastChar
+                        lastChar = (short) j;
+                    }
+                    eightBitGlyphs.set(j);
+                }
+
+                if (mtxPtr < mtxTab.length) {
+                    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;
+                        fontFile.seekSet(glyphOffset);
+                        glyphIdx = (fontFile.readTTFUShort() + cmapDeltas[i]) & 0xffff;
+                        //mtxTab[glyphIdx].setName(mtxTab[glyphIdx].getName() + " - "+(char)j);
+                        unicodeMappings.add(new UnicodeMapping(this, glyphIdx, j));
+                        mtxTab[glyphIdx].getUnicodeIndex().add(j);
+
+                        if (isWinSymbolEncoding && 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
+                                unicodeMappings.add(new UnicodeMapping(this, glyphIdx, mapped));
+                                mtxTab[glyphIdx].getUnicodeIndex().add(mapped);
+                            }
+                        }
+
+                        // Also add winAnsiWidth
+                        List<Integer> v = this.font.getAnsiIndices(j);
+                        if (v != null) {
+                            for (Integer aIdx : v) {
+                                ansiWidths[aIdx] = mtxTab[glyphIdx].getWx();
+
+                                if (log.isTraceEnabled()) {
+                                    log.trace("Added width "
+                                            + mtxTab[glyphIdx].getWx()
+                                            + " uni: " + j
+                                            + " ansi: " + aIdx);
+                                }
+                            }
+                        }
+
+                        if (log.isTraceEnabled()) {
+                            log.trace("Idx: "
+                                    + glyphIdx
+                                    + " Delta: " + cmapDeltas[i]
+                                    + " Unicode: " + j
+                                    + " name: " + mtxTab[glyphIdx].getName());
+                        }
+                    } else {
+                        glyphIdx = (j + cmapDeltas[i]) & 0xffff;
+
+                        if (glyphIdx < mtxTab.length) {
+                            mtxTab[glyphIdx].getUnicodeIndex().add(j);
+                        } else {
+                            log.debug("Glyph " + glyphIdx
+                                    + " out of range: "
+                                    + mtxTab.length);
+                        }
+
+                        unicodeMappings.add(new UnicodeMapping(this, glyphIdx, j));
+                        if (glyphIdx < mtxTab.length) {
+                            mtxTab[glyphIdx].getUnicodeIndex().add(j);
+                        } else {
+                            log.debug("Glyph " + glyphIdx
+                                    + " out of range: "
+                                    + mtxTab.length);
+                        }
+
+                        // Also add winAnsiWidth
+                        List<Integer> v = this.font.getAnsiIndices(j);
+                        if (v != null) {
+                            for (Integer aIdx : v) {
+                                ansiWidths[aIdx] = mtxTab[glyphIdx].getWx();
+                            }
+                        }
+                    }
+                    if (glyphIdx < mtxTab.length) {
+                        if (mtxTab[glyphIdx].getUnicodeIndex().size() < 2) {
+                            mtxPtr++;
+                        }
+                    }
+                }
+            }
+            // update font lastChar
+            this.font.setLastChar(lastChar);
+        }
+    }
+
+    private void readSegmentedCoverageSubTable(FontFileReader fontFile)
+            throws IOException {
+
+        fontFile.skip(2); // skip reserved (always 0)
+        fontFile.skip(4); // skip length
+        fontFile.skip(4); // skip language
+        long nGroups = fontFile.readTTFULong();
+
+        if (log.isTraceEnabled()) {
+            log.trace("Reading Segmented Coverage Subtable with " + nGroups + " groups:");
+        }
+
+        OFMtxEntry[] mtxTab = this.font.mtxTab;
+        int[] ansiWidths = this.font.getAnsiWidths();
+
+        long startCharCode;
+        long endCharCode;
+        long startGlyphIdx;
+        for (long l = 1; l <= nGroups; l++) {
+            startCharCode = fontFile.readTTFULong();
+            endCharCode = fontFile.readTTFULong();
+            startGlyphIdx = fontFile.readTTFULong();
+
+            if (log.isTraceEnabled()) {
+                log.trace("Group " + l + ": "
+                        + "\n    startCharCode = 0x" + Long.toHexString(startCharCode)
+                        + "\n    endCharCode   = 0x" + Long.toHexString(endCharCode)
+                        + "\n    startGlyphIdx = 0x" + Long.toHexString(startGlyphIdx));
+            }
+
+            // Add individual mappings for chars in group
+            int glyphIdx = (int) startGlyphIdx;
+            for (int i = (int) startCharCode; i <= (int) endCharCode; i++) {
+                unicodeMappings.add(new UnicodeMapping(this, glyphIdx, i));
+                mtxTab[glyphIdx].getUnicodeIndex().add(i);
+                // Also add winAnsiWidth
+                List<Integer> v = this.font.getAnsiIndices(i);
+                if (v != null) {
+                    for (Integer aIdx : v) {
+                        ansiWidths[aIdx] = mtxTab[glyphIdx].getWx();
+
+                        if (log.isTraceEnabled()) {
+                            log.trace("Added width "
+                                    + mtxTab[glyphIdx].getWx()
+                                    + " uni: " + i
+                                    + " ansi: " + aIdx);
+                        }
+                    }
+                }
+                glyphIdx++;
+            }
+        }
+    }
+
+    private boolean isInPrivateUseArea(int start, int end) {
+        return (isInPrivateUseArea(start) || isInPrivateUseArea(end));
+    }
+
+    private boolean isInPrivateUseArea(int unicode) {
+        return (unicode >= 0xE000 && unicode <= 0xF8FF);
+    }
+
+    protected List<CMapSegment> getSegments() {
+        if (this.segments == null) {
+            this.segments = new ArrayList<CMapSegment>();
+
+            int unicodeStart;
+            int glyphStart;
+            int unicodeEnd;
+            if (unicodeMappings.isEmpty()) {
+                return Collections.emptyList();
+            }
+            Iterator<UnicodeMapping> e = unicodeMappings.iterator();
+            UnicodeMapping um = e.next();
+            UnicodeMapping lastMapping = um;
+
+            unicodeStart = um.getUnicodeIndex();
+            glyphStart = um.getGlyphIndex();
+
+            while (e.hasNext()) {
+                um = e.next();
+                if (((lastMapping.getUnicodeIndex() + 1) != um.getUnicodeIndex())
+                        || ((lastMapping.getGlyphIndex() + 1) != um.getGlyphIndex())) {
+                    unicodeEnd = lastMapping.getUnicodeIndex();
+                    this.segments.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart));
+                    unicodeStart = um.getUnicodeIndex();
+                    glyphStart = um.getGlyphIndex();
+                }
+                lastMapping = um;
+            }
+
+            unicodeEnd = lastMapping.getUnicodeIndex();
+            this.segments.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart));
+        }
+        return this.segments;
+    }
+
+    /**
+     * Map a glyph index to the corresponding unicode code point
+     *
+     * @param glyphIndex the glyph index
+     * @return unicode code point
+     */
+    Integer glyphToUnicode(int glyphIndex) {
+        return glyphToUnicodeMap.get(glyphIndex);
+    }
+
+    /**
+     * Map a unicode code point to the corresponding glyph index
+     *
+     * @param unicodeIndex unicode code point
+     * @return glyph index
+     */
+    Integer unicodeToGlyph(int unicodeIndex) throws IOException {
+        final Integer result = unicodeToGlyphMap.get(unicodeIndex);
+        if (result == null) {
+            throw new IOException(
+                    "Glyph index not found for unicode value " + unicodeIndex);
+        }
+        return result;
+    }
+
+    /**
+     * @return an array with the unicode codepoint mappings
+     */
+    UnicodeMapping[] getUnicodeMappings() {
+        return this.unicodeMappings.toArray(new UnicodeMapping[this.unicodeMappings.size()]);
+    }
+}
Index: src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java	(revision 1716744)
+++ src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java	(revision )
@@ -1,0 +1,0 @@
Index: src/java/org/apache/fop/fonts/truetype/OFFontLoader.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/java/org/apache/fop/fonts/truetype/OFFontLoader.java	(revision 1716744)
+++ src/java/org/apache/fop/fonts/truetype/OFFontLoader.java	(revision )
@@ -1,0 +1,0 @@
Index: src/java/org/apache/fop/fonts/truetype/OFMtxEntry.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/java/org/apache/fop/fonts/truetype/OFMtxEntry.java	(revision 1716744)
+++ src/java/org/apache/fop/fonts/truetype/OFMtxEntry.java	(revision )
@@ -30,7 +30,7 @@
     private int lsb;
     private String name = "";
     private int index;
-    private List unicodeIndex = new java.util.ArrayList();
+    private List<Integer> unicodeIndex = new java.util.ArrayList<Integer>();
     private int[] boundingBox = new int[4];
     private long offset;
     private byte found;
@@ -131,7 +131,7 @@
      * Returns the unicodeIndex.
      * @return List
      */
-    public List getUnicodeIndex() {
+    public List<Integer> getUnicodeIndex() {
         return unicodeIndex;
     }
 
