You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by sp...@apache.org on 2010/08/19 21:46:45 UTC

svn commit: r987282 [6/9] - in /xmlgraphics/fop/branches/Temp_ComplexScripts: ./ src/codegen/unicode/java/org/apache/fop/hyphenation/ src/codegen/unicode/java/org/apache/fop/text/bidi/ src/java/org/apache/fop/area/ src/java/org/apache/fop/area/inline/ ...

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/truetype/TTFFile.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/truetype/TTFFile.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/truetype/TTFFile.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/truetype/TTFFile.java Thu Aug 19 19:46:41 2010
@@ -31,7 +31,21 @@ import org.apache.commons.logging.LogFac
 
 import org.apache.xmlgraphics.fonts.Glyphs;
 
+
 import org.apache.fop.fonts.FontUtil;
+import org.apache.fop.fonts.GlyphCoverageTable;
+import org.apache.fop.fonts.GlyphPositioningSubtable;
+import org.apache.fop.fonts.GlyphPositioningTable;
+import org.apache.fop.fonts.GlyphSubstitutionSubtable;
+import org.apache.fop.fonts.GlyphSubstitutionTable;
+import org.apache.fop.fonts.GlyphSubtable;
+import org.apache.fop.fonts.GlyphTable;
+
+// CSOFF: AvoidNestedBlocksCheck
+// CSOFF: NoWhitespaceAfterCheck
+// CSOFF: InnerAssignmentCheck
+// CSOFF: SimplifyBooleanReturnCheck
+// CSOFF: LineLengthCheck
 
 /**
  * Reads a TrueType file or a TrueType Collection.
@@ -122,6 +136,16 @@ public class TTFFile {
 
     private boolean isCFF;
 
+    /* advanced typographic support */
+    private Map/*<String,Object[3]>*/ seScripts;
+    private Map/*<String,Object[2]>*/ seLanguages;
+    private Map/*<String,List<String>>*/ seFeatures;
+    private List seCoverage;
+    private List seEntries;
+    private List seSubtables;
+    private GlyphSubstitutionTable gsub;
+    private GlyphPositioningTable gpos;
+
     /**
      * logging instance
      */
@@ -559,6 +583,8 @@ public class TTFFile {
         // print_max_min();
 
         readKerning(in);
+        readGSUB(in);
+        readGPOS(in);
         guessVerticalMetricsFromGlyphBBox();
         return true;
     }
@@ -1491,6 +1517,1511 @@ public class TTFFile {
         }
     }
 
+    private String toString ( int[] ia ) {
+        StringBuffer sb = new StringBuffer();
+        if ( ( ia == null ) || ( ia.length == 0 ) ) {
+            sb.append ( '-' );
+        } else {
+            boolean first = true;
+            for ( int i = 0; i < ia.length; i++ ) {
+                if ( ! first ) {
+                    sb.append ( ' ' );
+                } else {
+                    first = false;
+                }
+                sb.append ( ia[i] );
+            }
+        }
+        return sb.toString();
+    }
+
+    private void readLangSysTable(FontFileReader in, String tableTag, long langSysTable, String langSysTag) throws IOException {
+        in.seekSet(langSysTable);
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " lang sys table: " + langSysTag );
+        }
+        // read lookup order (reorder) table offset
+        int lo = in.readTTFUShort();
+        // read required feature index
+        int rf = in.readTTFUShort();
+        String rfi;
+        if ( rf != 65535 ) {
+            rfi = "f" + rf;
+        } else {
+            rfi = null;
+        }
+        // read (non-required) feature count
+        int nf = in.readTTFUShort();
+        // dump info if debugging
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " lang sys table reorder table: " + lo );
+            log.debug(tableTag + " lang sys table required feature index: " + rf );
+            log.debug(tableTag + " lang sys table non-required feature count: " + nf );
+        }
+        // read (non-required) feature indices
+        int[] fia = new int[nf];
+        List fl = new java.util.ArrayList();
+        for ( int i = 0; i < nf; i++ ) {
+            int fi = in.readTTFUShort();
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " lang sys table non-required feature index: " + fi );
+            }
+            fia[i] = fi;
+            fl.add ( "f" + fi );
+        }
+        if ( seLanguages == null ) {
+            seLanguages = new java.util.LinkedHashMap();
+        }
+        seLanguages.put ( langSysTag, new Object[] { rfi, fl } );
+    }
+
+    private static String defaultTag = "dflt";
+
+    private void readScriptTable(FontFileReader in, String tableTag, long scriptTable, String scriptTag) throws IOException {
+        in.seekSet(scriptTable);
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " script table: " + scriptTag );
+        }
+        // read default language system table offset
+        int dl = in.readTTFUShort();
+        String dt = defaultTag;
+        if ( dl > 0 ) {
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " default lang sys tag: " + dt );
+                log.debug(tableTag + " default lang sys table offset: " + dl );
+            }
+        }
+        // read language system record count
+        int nl = in.readTTFUShort();
+        List ll = new java.util.ArrayList();
+        if ( nl > 0 ) {
+            String[] lta = new String[nl];
+            int[] loa = new int[nl];
+            // read language system records
+            for ( int i = 0, n = nl; i < n; i++ ) {
+                String lt = in.readTTFString(4);
+                int lo = in.readTTFUShort();
+                if (log.isDebugEnabled()) {
+                    log.debug(tableTag + " lang sys tag: " + lt );
+                    log.debug(tableTag + " lang sys table offset: " + lo );
+                }
+                lta[i] = lt;
+                loa[i] = lo;
+                if ( dl == lo ) {
+                    dl = 0;
+                    dt = lt;
+                }
+                ll.add ( lt );
+            }
+            // read non-default language system tables
+            for ( int i = 0, n = nl; i < n; i++ ) {
+                readLangSysTable ( in, tableTag, scriptTable + loa [ i ], lta [ i ] );
+            }
+        }
+        // read default language system table (if specified)
+        if ( dl > 0 ) {
+            readLangSysTable ( in, tableTag, scriptTable + dl, dt );
+        } else if ( dt != null ) {
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " lang sys default: " + dt );
+            }
+        }
+        seScripts.put ( scriptTag, new Object[] { dt, ll, seLanguages } );
+        seLanguages = null;
+    }
+
+    private void readScriptList(FontFileReader in, String tableTag, long scriptList) throws IOException {
+        in.seekSet(scriptList);
+        // read script record count
+        int ns = in.readTTFUShort();
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " script list record count: " + ns );
+        }
+        if ( ns > 0 ) {
+            String[] sta = new String[ns];
+            int[] soa = new int[ns];
+            // read script records
+            for ( int i = 0, n = ns; i < n; i++ ) {
+                String st = in.readTTFString(4);
+                int so = in.readTTFUShort();
+                if (log.isDebugEnabled()) {
+                    log.debug(tableTag + " script tag: " + st );
+                    log.debug(tableTag + " script table offset: " + so );
+                }
+                sta[i] = st;
+                soa[i] = so;
+            }
+            // read script tables
+            for ( int i = 0, n = ns; i < n; i++ ) {
+                seLanguages = null;
+                readScriptTable ( in, tableTag, scriptList + soa [ i ], sta [ i ] );
+            }
+        }
+    }
+
+    private void readFeatureTable(FontFileReader in, String tableTag, long featureTable, String featureTag, int featureIndex) throws IOException {
+        in.seekSet(featureTable);
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " feature table: " + featureTag );
+        }
+        // read feature params offset
+        int po = in.readTTFUShort();
+        // read lookup list indices count
+        int nl = in.readTTFUShort();
+        // dump info if debugging
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " feature table parameters offset: " + po );
+            log.debug(tableTag + " feature table lookup list index count: " + nl );
+        }
+        // read lookup table indices
+        int[] lia = new int[nl];
+        List lul = new java.util.ArrayList();
+        for ( int i = 0; i < nl; i++ ) {
+            int li = in.readTTFUShort();
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " feature table lookup index: " + li );
+            }
+            lia[i] = li;
+            lul.add ( "lu" + li );
+        }
+        seFeatures.put ( "f" + featureIndex, new Object[] { featureTag, lul } );
+    }
+
+    private void readFeatureList(FontFileReader in, String tableTag, long featureList) throws IOException {
+        in.seekSet(featureList);
+        // read feature record count
+        int nf = in.readTTFUShort();
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " feature list record count: " + nf );
+        }
+        if ( nf > 0 ) {
+            String[] fta = new String[nf];
+            int[] foa = new int[nf];
+            // read feature records
+            for ( int i = 0, n = nf; i < n; i++ ) {
+                String ft = in.readTTFString(4);
+                int fo = in.readTTFUShort();
+                if (log.isDebugEnabled()) {
+                    log.debug(tableTag + " feature tag: " + ft );
+                    log.debug(tableTag + " feature table offset: " + fo );
+                }
+                fta[i] = ft;
+                foa[i] = fo;
+            }
+            // read feature tables
+            for ( int i = 0, n = nf; i < n; i++ ) {
+                if (log.isDebugEnabled()) {
+                    log.debug(tableTag + " feature index: " + i );
+                }
+                readFeatureTable ( in, tableTag, featureList + foa [ i ], fta [ i ], i );
+            }
+        }
+    }
+
+    /**
+     * Determine if script extension is present.
+     * @return true if script extension is present
+     */
+    public boolean hasScriptExtension() {
+        return ( gsub != null ) || ( gpos != null );
+    }
+
+    /**
+     * Returns the GSUB table or null if none present.
+     * @return the GSUB table
+     */
+    public GlyphSubstitutionTable getGSUB() {
+        return gsub;
+    }
+
+    /**
+     * Returns the GPOS table or null if none present.
+     * @return the GPOS table
+     */
+    public GlyphPositioningTable getGPOS() {
+        return gpos;
+    }
+
+    static final class GSUBLookupType {
+        static final int SINGLE                         = 1;
+        static final int MULTIPLE                       = 2;
+        static final int ALTERNATE                      = 3;
+        static final int LIGATURE                       = 4;
+        static final int CONTEXT                        = 5;
+        static final int CHAINED_CONTEXT                = 6;
+        static final int EXTENSION                      = 7;
+        static final int REVERSE_CHAINED_SINGLE         = 8;
+        private GSUBLookupType() {
+        }
+        public static int getSubtableType ( int lt ) {
+            int st;
+            switch ( lt ) {
+            case GSUBLookupType.SINGLE:
+                st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_SINGLE;
+                break;
+            case GSUBLookupType.MULTIPLE:
+                st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_MULTIPLE;
+                break;
+            case GSUBLookupType.ALTERNATE:
+                st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_ALTERNATE;
+                break;
+            case GSUBLookupType.LIGATURE:
+                st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_LIGATURE;
+                break;
+            case GSUBLookupType.CONTEXT:
+                st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_CONTEXT;
+                break;
+            case GSUBLookupType.CHAINED_CONTEXT:
+                st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_CHAINING_CONTEXT;
+                break;
+            case GSUBLookupType.EXTENSION:
+                st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_EXTENSION_SUBSTITUTION;
+                break;
+            case GSUBLookupType.REVERSE_CHAINED_SINGLE:
+                st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE;
+                break;
+            default:
+                st = -1;
+                break;
+            }
+            return st;
+        }
+        public static String toString(int type) {
+            String s;
+            switch ( type ) {
+            case SINGLE:
+                s = "Single";
+                break;
+            case MULTIPLE:
+                s = "Multiple";
+                break;
+            case ALTERNATE:
+                s = "Alternate";
+                break;
+            case LIGATURE:
+                s = "Ligature";
+                break;
+            case CONTEXT:
+                s = "Context";
+                break;
+            case CHAINED_CONTEXT:
+                s = "ChainedContext";
+                break;
+            case EXTENSION:
+                s = "Extension";
+                break;
+            case REVERSE_CHAINED_SINGLE:
+                s = "ReverseChainedSingle";
+                break;
+            default:
+                s = "?";
+                break;
+            }
+            return s;
+        }
+    }
+
+    static final class GPOSLookupType {
+        static final int SINGLE                         = 1;
+        static final int PAIR                           = 2;
+        static final int CURSIVE                        = 3;
+        static final int MARK_TO_BASE                   = 4;
+        static final int MARK_TO_LIGATURE               = 5;
+        static final int MARK_TO_MARK                   = 6;
+        static final int CONTEXT                        = 7;
+        static final int CHAINED_CONTEXT                = 8;
+        static final int EXTENSION                      = 9;
+        private GPOSLookupType() {
+        }
+        public static String toString(int type) {
+            String s;
+            switch ( type ) {
+            case SINGLE:
+                s = "Single";
+                break;
+            case PAIR:
+                s = "Pair";
+                break;
+            case CURSIVE:
+                s = "Cursive";
+                break;
+            case MARK_TO_BASE:
+                s = "MarkToBase";
+                break;
+            case MARK_TO_LIGATURE:
+                s = "MarkToLigature";
+                break;
+            case MARK_TO_MARK:
+                s = "MarkToMark";
+                break;
+            case CONTEXT:
+                s = "Context";
+                break;
+            case CHAINED_CONTEXT:
+                s = "ChainedContext";
+                break;
+            case EXTENSION:
+                s = "Extension";
+                break;
+            default:
+                s = "?";
+                break;
+            }
+            return s;
+        }
+    }
+
+    static final class LookupFlag {
+        static final int RIGHT_TO_LEFT                  = 0x0001;
+        static final int IGNORE_BASE_GLYPHS             = 0x0002;
+        static final int IGNORE_LIGATURE                = 0x0004;
+        static final int IGNORE_MARKS                   = 0x0008;
+        static final int USE_MARK_FILTERING_SET         = 0x0010;
+        static final int MARK_ATTACHMENT_TYPE           = 0xFF00;
+        private LookupFlag() {
+        }
+        public static String toString(int flags) {
+            StringBuffer sb = new StringBuffer();
+            boolean first = true;
+            if ( ( flags & RIGHT_TO_LEFT ) != 0 ) {
+                if ( first ) {
+                    first = false;
+                } else {
+                    sb.append ( '|' );
+                }
+                sb.append ( "RightToLeft" );
+            }
+            if ( ( flags & IGNORE_BASE_GLYPHS ) != 0 ) {
+                if ( first ) {
+                    first = false;
+                } else {
+                    sb.append ( '|' );
+                }
+                sb.append ( "IgnoreBaseGlyphs" );
+            }
+            if ( ( flags & IGNORE_LIGATURE ) != 0 ) {
+                if ( first ) {
+                    first = false;
+                } else {
+                    sb.append ( '|' );
+                }
+                sb.append ( "IgnoreLigature" );
+            }
+            if ( ( flags & IGNORE_MARKS ) != 0 ) {
+                if ( first ) {
+                    first = false;
+                } else {
+                    sb.append ( '|' );
+                }
+                sb.append ( "IgnoreMarks" );
+            }
+            if ( ( flags & USE_MARK_FILTERING_SET ) != 0 ) {
+                if ( first ) {
+                    first = false;
+                } else {
+                    sb.append ( '|' );
+                }
+                sb.append ( "UseMarkFilteringSet" );
+            }
+            if ( sb.length() == 0 ) {
+                sb.append ( '-' );
+            }
+            return sb.toString();
+        }
+    }
+
+    private void readCoverageTableFormat1(FontFileReader in, String label, long tableOffset, int coverageFormat) throws IOException {
+        in.seekSet(tableOffset);
+        // skip over format (already known)
+        in.skip ( 2 );
+        // read glyph count
+        int ng = in.readTTFUShort();
+        int[] ga = new int[ng];
+        for ( int i = 0, n = ng; i < n; i++ ) {
+            int g = in.readTTFUShort();
+            ga[i] = g;
+            seCoverage.add ( Integer.valueOf(g) );
+        }
+        // dump info if debugging
+        if (log.isDebugEnabled()) {
+            log.debug(label + " glyphs: " + toString(ga) );
+        }
+    }
+
+    private void readCoverageTableFormat2(FontFileReader in, String label, long tableOffset, int coverageFormat) throws IOException {
+        in.seekSet(tableOffset);
+        // skip over format (already known)
+        in.skip ( 2 );
+        // read range record count
+        int nr = in.readTTFUShort();
+        int[] rsa = new int[nr];
+        int[] rea = new int[nr];
+        int[] rxa = new int[nr];
+        for ( int i = 0, n = nr; i < n; i++ ) {
+            // read range start
+            int s = in.readTTFUShort();
+            // read range end
+            int e = in.readTTFUShort();
+            // read range coverage index
+            int x = in.readTTFUShort();
+            // dump info if debugging
+            if (log.isDebugEnabled()) {
+                log.debug(label + " range[" + i + "]: [" + s + "," + e + "]: " + x );
+            }
+            rsa[i] = s;
+            rea[i] = e;
+            rxa[i] = x;
+            seCoverage.add ( new GlyphCoverageTable.CoverageRange ( s, e, x ) );
+        }
+    }
+
+    private void readCoverageTable(FontFileReader in, String label, long tableOffset) throws IOException {
+        long cp = in.getCurrentPos();
+        in.seekSet(tableOffset);
+        // read coverage table format
+        int cf = in.readTTFUShort();
+        if ( cf == 1 ) {
+            readCoverageTableFormat1 ( in, label, tableOffset, cf );
+        } else if ( cf == 2 ) {
+            readCoverageTableFormat2 ( in, label, tableOffset, cf );
+        }
+        in.seekSet ( cp );
+    }
+
+    /* not used yet
+    private void readClassDefTableFormat1(FontFileReader in, long tableOffset, int classFormat) throws IOException {
+        in.seekSet(tableOffset);
+        // skip over format (already known)
+        in.skip ( 2 );
+    }
+
+    private void readClassDefTableFormat2(FontFileReader in, long tableOffset, int classFormat) throws IOException {
+        in.seekSet(tableOffset);
+        // skip over format (already known)
+        in.skip ( 2 );
+    }
+
+    private void readClassDefTable(FontFileReader in, long tableOffset) throws IOException {
+        long cp = in.getCurrentPos();
+        in.seekSet(tableOffset);
+        // read class table format
+        int cf = in.readTTFUShort();
+        if ( cf == 1 ) {
+            readClassDefTableFormat1 ( in, tableOffset, cf );
+        } else if ( cf == 2 ) {
+            readClassDefTableFormat2 ( in, tableOffset, cf );
+        }
+        in.seekSet ( cp );
+    }
+    */
+
+    private void readSingleSubTableFormat1(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
+        String tableTag = "GSUB";
+        in.seekSet(subtableOffset);
+        // skip over format (already known)
+        in.skip ( 2 );
+        // read coverage offset
+        int co = in.readTTFUShort();
+        // read delta glyph
+        int dg = in.readTTFShort();
+        // dump info if debugging
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " single substitution format: " + subtableFormat + " (delta)" );
+            log.debug(tableTag + " single substitution coverage table offset: " + co );
+            log.debug(tableTag + " single substitution delta: " + dg );
+        }
+        // read coverage table
+        readCoverageTable ( in, tableTag + " single substitution coverage", subtableOffset + co );
+        seEntries.add ( Integer.valueOf ( dg ) );
+    }
+
+    private void readSingleSubTableFormat2(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
+        String tableTag = "GSUB";
+        in.seekSet(subtableOffset);
+        // skip over format (already known)
+        in.skip ( 2 );
+        // read coverage offset
+        int co = in.readTTFUShort();
+        // read glyph count
+        int ng = in.readTTFUShort();
+        // dump info if debugging
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " single substitution format: " + subtableFormat + " (mapped)" );
+            log.debug(tableTag + " single substitution coverage table offset: " + co );
+            log.debug(tableTag + " single substitution glyph count: " + ng );
+        }
+        // read coverage table
+        readCoverageTable ( in, tableTag + " single substitution coverage", subtableOffset + co );
+        // read glyph substitutions
+        int[] gsa = new int[ng];
+        for ( int i = 0, n = ng; i < n; i++ ) {
+            int gs = in.readTTFUShort();
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " single substitution glyph[" + i + "]: " + gs );
+            }
+            gsa[i] = gs;
+            seEntries.add ( Integer.valueOf ( gs ) );
+        }
+    }
+
+    private int readSingleSubTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        if ( sf == 1 ) {
+            readSingleSubTableFormat1 ( in, lookupType, lookupFlags, subtableOffset, sf );
+        } else if ( sf == 2 ) {
+            readSingleSubTableFormat2 ( in, lookupType, lookupFlags, subtableOffset, sf );
+        }
+        return sf;
+    }
+
+    private void readMultipleSubTableFormat1(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
+        String tableTag = "GSUB";
+        in.seekSet(subtableOffset);
+        // skip over format (already known)
+        in.skip ( 2 );
+        // read coverage offset
+        int co = in.readTTFUShort();
+        // read sequence count
+        int ns = in.readTTFUShort();
+        // dump info if debugging
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " multiple substitution format: " + subtableFormat + " (mapped)" );
+            log.debug(tableTag + " multiple substitution coverage table offset: " + co );
+            log.debug(tableTag + " multiple substitution sequence count: " + ns );
+        }
+        // read coverage table
+        readCoverageTable ( in, tableTag + " multiple substitution coverage", subtableOffset + co );
+        // read sequence table offsets
+        int[] soa = new int[ns];
+        for ( int i = 0, n = ns; i < n; i++ ) {
+            soa[i] = in.readTTFUShort();
+        }
+        // read sequence tables
+        for ( int i = 0, n = ns; i < n; i++ ) {
+            int so = soa[i];
+            in.seekSet(subtableOffset + so);
+            // read glyph count
+            int ng = in.readTTFUShort();
+            int[] ga = new int[ng];
+            for ( int j = 0; j < ng; j++ ) {
+                int gs = in.readTTFUShort();
+                ga[j] = gs;
+            }
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " multiple substitution sequence[" + i + "]: " + toString ( ga ) );
+            }
+        }
+    }
+
+    private int readMultipleSubTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        if ( sf == 1 ) {
+            readMultipleSubTableFormat1 ( in, lookupType, lookupFlags, subtableOffset, sf );
+        }
+        return sf;
+    }
+
+    private void readAlternateSubTableFormat1(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
+        String tableTag = "GSUB";
+        in.seekSet(subtableOffset);
+        // skip over format (already known)
+        in.skip ( 2 );
+        // read coverage offset
+        int co = in.readTTFUShort();
+        // read alternate set count
+        int ns = in.readTTFUShort();
+        // dump info if debugging
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " alternate substitution format: " + subtableFormat + " (mapped)" );
+            log.debug(tableTag + " alternate substitution coverage table offset: " + co );
+            log.debug(tableTag + " alternate substitution alternate set count: " + ns );
+        }
+        // read coverage table
+        readCoverageTable ( in, tableTag + " alternate substitution coverage", subtableOffset + co );
+        // read alternate set table offsets
+        int[] soa = new int[ns];
+        for ( int i = 0, n = ns; i < n; i++ ) {
+            soa[i] = in.readTTFUShort();
+        }
+        // read alternate set tables
+        for ( int i = 0, n = ns; i < n; i++ ) {
+            int so = soa[i];
+            in.seekSet(subtableOffset + so);
+            // read glyph count
+            int ng = in.readTTFUShort();
+            int[] ga = new int[ng];
+            for ( int j = 0; j < ng; j++ ) {
+                int gs = in.readTTFUShort();
+                ga[j] = gs;
+            }
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " alternate substitution alternate set[" + i + "]: " + toString ( ga ) );
+            }
+        }
+    }
+
+    private int readAlternateSubTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        if ( sf == 1 ) {
+            readAlternateSubTableFormat1 ( in, lookupType, lookupFlags, subtableOffset, sf );
+        }
+        return sf;
+    }
+
+    private void readLigatureSubTableFormat1(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
+        String tableTag = "GSUB";
+        in.seekSet(subtableOffset);
+        // skip over format (already known)
+        in.skip ( 2 );
+        // read coverage offset
+        int co = in.readTTFUShort();
+        // read ligature set count
+        int ns = in.readTTFUShort();
+        // dump info if debugging
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " ligature substitution format: " + subtableFormat + " (mapped)" );
+            log.debug(tableTag + " ligature substitution coverage table offset: " + co );
+            log.debug(tableTag + " ligature substitution ligature set count: " + ns );
+        }
+        // read coverage table
+        readCoverageTable ( in, tableTag + " ligature substitution coverage", subtableOffset + co );
+        // read ligature set table offsets
+        int[] soa = new int[ns];
+        for ( int i = 0, n = ns; i < n; i++ ) {
+            soa[i] = in.readTTFUShort();
+        }
+        // read ligature set tables
+        for ( int i = 0, n = ns; i < n; i++ ) {
+            int so = soa[i];
+            in.seekSet(subtableOffset + so);
+            // read ligature table count
+            int nl = in.readTTFUShort();
+            int[] loa = new int[nl];
+            for ( int j = 0; j < nl; j++ ) {
+                loa[j] = in.readTTFUShort();
+            }
+            List ligs = new java.util.ArrayList();
+            for ( int j = 0; j < nl; j++ ) {
+                int lo = loa[j];
+                in.seekSet(subtableOffset + so + lo);
+                // read ligature glyph id
+                int lg = in.readTTFUShort();
+                // read ligature (input) component count
+                int nc = in.readTTFUShort();
+                int[] ca = new int [ nc - 1 ];
+                // read ligature (input) component glyph ids
+                for ( int k = 0; k < nc - 1; k++ ) {
+                    ca[k] = in.readTTFUShort();
+                }
+                if (log.isDebugEnabled()) {
+                    log.debug(tableTag + " ligature substitution ligature set[" + i + "]: ligature(" + lg + "), components: " + toString ( ca ) );
+                }
+                ligs.add ( new GlyphSubstitutionTable.Ligature ( lg, ca ) );
+            }
+            seEntries.add ( new GlyphSubstitutionTable.LigatureSet ( ligs ) );
+        }
+    }
+
+    private int readLigatureSubTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        if ( sf == 1 ) {
+            readLigatureSubTableFormat1 ( in, lookupType, lookupFlags, subtableOffset, sf );
+        }
+        return sf;
+    }
+
+    private int readContextSubTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        // [TBD] - implement me
+        return sf;
+    }
+
+    private void readChainedContextSubTableFormat1(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
+        String tableTag = "GSUB";
+        in.seekSet(subtableOffset);
+        // skip over format (already known)
+        in.skip ( 2 );
+        // read coverage offset
+        int co = in.readTTFUShort();
+        // read subrule set count
+        int ns = in.readTTFUShort();
+        // dump info if debugging
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " chained context substitution format: " + subtableFormat + " (simple)" );
+            log.debug(tableTag + " chained context substitution coverage table offset: " + co );
+            log.debug(tableTag + " chained context substitution subrule set count: " + ns );
+        }
+        // read coverage table
+        readCoverageTable ( in, tableTag + " chained context substitution coverage", subtableOffset + co );
+        // read subrule set table offsets
+        int[] soa = new int[ns];
+        for ( int i = 0, n = ns; i < n; i++ ) {
+            soa[i] = in.readTTFUShort();
+        }
+        // read subrule set tables
+        for ( int i = 0, n = ns; i < n; i++ ) {
+            int so = soa[i];
+            in.seekSet(subtableOffset + so);
+            // read subrule table count
+            int nst = in.readTTFUShort();
+            int[] stoa = new int[nst];
+            for ( int j = 0; j < nst; j++ ) {
+                stoa[j] = in.readTTFUShort();
+            }
+            for ( int j = 0; j < nst; j++ ) {
+                int sto = stoa[j];
+                in.seekSet(subtableOffset + so + sto);
+                // read backtrack glyph count
+                int nbg = in.readTTFUShort();
+                int[] bga = new int[nbg];
+                // read backtrack glyphs
+                for ( int k = 0; k < nbg; k++ ) {
+                    bga[k] = in.readTTFUShort();
+                }
+                // read input glyph count
+                int nig = in.readTTFUShort();
+                int[] iga = new int [ nig - 1 ];
+                // read input glyphs
+                for ( int k = 0; k < nig - 1; k++ ) {
+                    iga[k] = in.readTTFUShort();
+                }
+                // read lookahead glyph count
+                int nlg = in.readTTFUShort();
+                int[] lga = new int[nlg];
+                // read lookahead glyphs
+                for ( int k = 0; k < nlg; k++ ) {
+                    lga[k] = in.readTTFUShort();
+                }
+                // read substitution lookup record count
+                int nsl = in.readTTFUShort();
+                int[] sia = new int[nsl];
+                int[] lia = new int[nsl];
+                // read substitution lookup records
+                for ( int k = 0; k < nsl; k++ ) {
+                    // read sequence index
+                    sia[k] = in.readTTFUShort();
+                    // read lookup list index
+                    lia[k] = in.readTTFUShort();
+                }
+                if (log.isDebugEnabled()) {
+                    log.debug(tableTag + " chained context substitution subrule set[" + i + "]: backtrack [" + toString(bga) + "]" );
+                    log.debug(tableTag + " chained context substitution subrule set[" + i + "]: input     [" + toString(iga) + "]" );
+                    log.debug(tableTag + " chained context substitution subrule set[" + i + "]: lookahead [" + toString(lga) + "]" );
+                    log.debug(tableTag + " chained context substitution lookup count: " + nsl );
+                    for ( int k = 0; k < nsl; k++ ) {
+                        log.debug(tableTag + " chained context substitution lookup[" + i + "]: [" + sia[k] + "," + lia[k] + "]" );
+                    }
+                }
+            }
+        }
+    }
+
+    private void readChainedContextSubTableFormat2(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
+        String tableTag = "GSUB";
+        in.seekSet(subtableOffset);
+        // skip over format (already known)
+        in.skip ( 2 );
+        // read coverage offset
+        int co = in.readTTFUShort();
+        // read backtrack classdef table offset
+        int bo = in.readTTFUShort();
+        // read input classdef table offset
+        int io = in.readTTFUShort();
+        // read lookahead classdef table offset
+        int lo = in.readTTFUShort();
+        // read subclass set count
+        int ns = in.readTTFUShort();
+        // dump info if debugging
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " chained context substitution format: " + subtableFormat + " (class based)" );
+            log.debug(tableTag + " chained context substitution coverage table offset: " + co );
+            log.debug(tableTag + " chained context substitution backtrack classdef table offset: " + bo );
+            log.debug(tableTag + " chained context substitution input classdef table offset: " + io );
+            log.debug(tableTag + " chained context substitution lookahead classdef table offset: " + lo );
+            log.debug(tableTag + " chained context substitution subclass set count: " + ns );
+        }
+        // read coverage table
+        readCoverageTable ( in, tableTag + " chained context substitution coverage", subtableOffset + co );
+        // read subclass set table offsets
+        int[] soa = new int[ns];
+        for ( int i = 0, n = ns; i < n; i++ ) {
+            soa[i] = in.readTTFUShort();
+        }
+        // read subclass set tables
+        for ( int i = 0, n = ns; i < n; i++ ) {
+            int so = soa[i];
+            if ( so == 0 ) {
+                continue;
+            }
+            in.seekSet(subtableOffset + so);
+            // read subclass rule table count
+            int nst = in.readTTFUShort();
+            int[] stoa = new int[nst];
+            for ( int j = 0; j < nst; j++ ) {
+                stoa[j] = in.readTTFUShort();
+            }
+            for ( int j = 0; j < nst; j++ ) {
+                int sto = stoa[j];
+                in.seekSet(subtableOffset + so + sto);
+                // read backtrack class count
+                int nbc = in.readTTFUShort();
+                int[] bca = new int[nbc];
+                // read backtrack classes
+                for ( int k = 0; k < nbc; k++ ) {
+                    bca[k] = in.readTTFUShort();
+                }
+                // read input class count
+                int nic = in.readTTFUShort();
+                int[] ica = new int [ nic - 1 ];
+                // read inpput classes
+                for ( int k = 0; k < nic - 1; k++ ) {
+                    ica[k] = in.readTTFUShort();
+                }
+                // read lookahead class count
+                int nlc = in.readTTFUShort();
+                int[] lca = new int[nlc];
+                // read lookahead classes
+                for ( int k = 0; k < nlc; k++ ) {
+                    lca[k] = in.readTTFUShort();
+                }
+                // read substitution lookup record count
+                int nsl = in.readTTFUShort();
+                int[] sia = new int[nsl];
+                int[] lia = new int[nsl];
+                // read substitution lookup records
+                for ( int k = 0; k < nsl; k++ ) {
+                    // read sequence index
+                    sia[k] = in.readTTFUShort();
+                    // read lookup list index
+                    lia[k] = in.readTTFUShort();
+                }
+                if (log.isDebugEnabled()) {
+                    log.debug(tableTag + " chained context substitution subclass set[" + i + "]: backtrack [" + toString(bca) + "]" );
+                    log.debug(tableTag + " chained context substitution subclass set[" + i + "]: input     [" + toString(ica) + "]" );
+                    log.debug(tableTag + " chained context substitution subclass set[" + i + "]: lookahead [" + toString(lca) + "]" );
+                    log.debug(tableTag + " chained context substitution lookup count: " + nsl );
+                    for ( int k = 0; k < nsl; k++ ) {
+                        log.debug(tableTag + " chained context substitution lookup[" + i + "]: [" + sia[k] + "," + lia[k] + "]" );
+                    }
+                }
+            }
+        }
+    }
+
+    private void readChainedContextSubTableFormat3(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
+        String tableTag = "GSUB";
+        in.seekSet(subtableOffset);
+        // skip over format (already known)
+        in.skip ( 2 );
+        // read backtrack glyph count
+        int nbg = in.readTTFUShort();
+        // read backtrack glyph coverage table offsets
+        int[] boa = new int[nbg];
+        for ( int i = 0; i < nbg; i++ ) {
+            boa[i] = in.readTTFUShort();
+        }
+        // read input glyph count
+        int nig = in.readTTFUShort();
+        // read input glyph coverage table offsets
+        int[] ioa = new int[nig];
+        for ( int i = 0; i < nig; i++ ) {
+            ioa[i] = in.readTTFUShort();
+        }
+        // read lookahead glyph count
+        int nlg = in.readTTFUShort();
+        // read lookahead glyph coverage table offsets
+        int[] loa = new int[nlg];
+        for ( int i = 0; i < nlg; i++ ) {
+            loa[i] = in.readTTFUShort();
+        }
+        // read substitution lookup record count
+        int nsl = in.readTTFUShort();
+        int[] sia = new int[nsl];
+        int[] lia = new int[nsl];
+        // read substitution lookup records
+        for ( int i = 0; i < nsl; i++ ) {
+            // read sequence index
+            sia[i] = in.readTTFUShort();
+            // read lookup list index
+            lia[i] = in.readTTFUShort();
+        }
+        // dump info if debugging
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " chained context substitution format: " + subtableFormat + " (coverage based)" );
+            log.debug(tableTag + " chained context substitution backtrack coverage table offsets: " + toString(boa) );
+            log.debug(tableTag + " chained context substitution input coverage table offsets: " + toString(ioa) );
+            log.debug(tableTag + " chained context substitution lookahead coverage table offsets: " + toString(loa) );
+            log.debug(tableTag + " chained context substitution lookup count: " + nsl );
+            for ( int i = 0; i < nsl; i++ ) {
+                log.debug(tableTag + " chained context substitution lookup[" + i + "]: [" + sia[i] + "," + lia[i] + "]" );
+            }
+        }
+        // read backtrack coverage tables
+        for ( int i = 0; i < boa.length; i++ ) {
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " chained context substitution backtrack coverage table[" + i + "]" );
+            }
+            readCoverageTable ( in, tableTag + " chained context substitution coverage", subtableOffset + boa [ i ] );
+        }
+        // read input coverage tables
+        for ( int i = 0; i < ioa.length; i++ ) {
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " chained context substitution input coverage table[" + i + "]" );
+            }
+            readCoverageTable ( in, tableTag + " chained context substitution coverage", subtableOffset + ioa [ i ] );
+        }
+        // read lookahead coverage tables
+        for ( int i = 0; i < loa.length; i++ ) {
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " chained context substitution lookahead coverage table[" + i + "]" );
+            }
+            readCoverageTable ( in, tableTag + " chained context substitution coverage", subtableOffset + loa [ i ] );
+        }
+    }
+
+    private int readChainedContextSubTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        if ( sf == 1 ) {
+            readChainedContextSubTableFormat1 ( in, lookupType, lookupFlags, subtableOffset, sf );
+        } else if ( sf == 2 ) {
+            readChainedContextSubTableFormat2 ( in, lookupType, lookupFlags, subtableOffset, sf );
+        } else if ( sf == 3 ) {
+            readChainedContextSubTableFormat3 ( in, lookupType, lookupFlags, subtableOffset, sf );
+        }
+        return sf;
+    }
+
+    private int readExtensionSubTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        // [TBD] - implement me
+        return sf;
+    }
+
+    private int readReverseChainedSingleSubTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        // [TBD] - implement me
+        return sf;
+    }
+
+    private void readGSUBSubtable(FontFileReader in, int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset) throws IOException {
+        initSESubState();
+        int subtableFormat = -1;
+        switch ( lookupType ) {
+        case GSUBLookupType.SINGLE:
+            subtableFormat = readSingleSubTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GSUBLookupType.MULTIPLE:
+            subtableFormat = readMultipleSubTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GSUBLookupType.ALTERNATE:
+            subtableFormat = readAlternateSubTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GSUBLookupType.LIGATURE:
+            subtableFormat = readLigatureSubTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GSUBLookupType.CONTEXT:
+            subtableFormat = readContextSubTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GSUBLookupType.CHAINED_CONTEXT:
+            subtableFormat = readChainedContextSubTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GSUBLookupType.REVERSE_CHAINED_SINGLE:
+            subtableFormat = readReverseChainedSingleSubTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GSUBLookupType.EXTENSION:
+            subtableFormat = readExtensionSubTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        default:
+            break;
+        }
+        extractSESubState ( GlyphTable.GLYPH_TABLE_TYPE_SUBSTITUTION, lookupType, lookupFlags, lookupSequence, subtableSequence, subtableFormat );
+        resetSESubState();
+    }
+
+    private int readSinglePosTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        // [TBD] - implement me
+        return sf;
+    }
+
+    private int readPairPosTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        // [TBD] - implement me
+        return sf;
+    }
+
+    private int readCursivePosTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        // [TBD] - implement me
+        return sf;
+    }
+
+    private int readMarkToBasePosTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        // [TBD] - implement me
+        return sf;
+    }
+
+    private int readMarkToLigaturePosTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        // [TBD] - implement me
+        return sf;
+    }
+
+    private int readMarkToMarkPosTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        // [TBD] - implement me
+        return sf;
+    }
+
+    private int readContextPosTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        // [TBD] - implement me
+        return sf;
+    }
+
+    private int readChainedContextPosTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        // [TBD] - implement me
+        return sf;
+    }
+
+    private int readExtensionPosTable(FontFileReader in, int lookupType, int lookupFlags, long subtableOffset) throws IOException {
+        in.seekSet(subtableOffset);
+        // read substitution format
+        int sf = in.readTTFUShort();
+        // [TBD] - implement me
+        return sf;
+    }
+
+    private void readGPOSSubtable(FontFileReader in, int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset) throws IOException {
+        initSESubState();
+        int subtableFormat = -1;
+        switch ( lookupType ) {
+        case GPOSLookupType.SINGLE:
+            subtableFormat = readSinglePosTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GPOSLookupType.PAIR:           
+            subtableFormat = readPairPosTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GPOSLookupType.CURSIVE:
+            subtableFormat = readCursivePosTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GPOSLookupType.MARK_TO_BASE:
+            subtableFormat = readMarkToBasePosTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GPOSLookupType.MARK_TO_LIGATURE:
+            subtableFormat = readMarkToLigaturePosTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GPOSLookupType.MARK_TO_MARK:
+            subtableFormat = readMarkToMarkPosTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GPOSLookupType.CONTEXT:
+            subtableFormat = readContextPosTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GPOSLookupType.CHAINED_CONTEXT:
+            subtableFormat = readChainedContextPosTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        case GPOSLookupType.EXTENSION:
+            subtableFormat = readExtensionPosTable ( in, lookupType, lookupFlags, subtableOffset );
+            break;
+        default:
+            break;
+        }
+        extractSESubState ( GlyphTable.GLYPH_TABLE_TYPE_POSITIONING, lookupType, lookupFlags, lookupSequence, subtableSequence, subtableFormat );
+        resetSESubState();
+    }
+
+    private void readLookupTable(FontFileReader in, String tableTag, int lookupSequence, long lookupTable) throws IOException {
+        boolean isGSUB = tableTag.equals ( "GSUB" );
+        boolean isGPOS = tableTag.equals ( "GPOS" );
+        in.seekSet(lookupTable);
+        // read lookup type
+        int lt = in.readTTFUShort();
+        // read lookup flags
+        int lf = in.readTTFUShort();
+        // read sub-table count
+        int ns = in.readTTFUShort();
+        // dump info if debugging
+        if (log.isDebugEnabled()) {
+            String lts;
+            if ( isGSUB ) {
+                lts = GSUBLookupType.toString ( lt );
+            } else if ( isGPOS ) {
+                lts = GPOSLookupType.toString ( lt );
+            } else {
+                lts = "?";
+            }
+            log.debug(tableTag + " lookup table type: " + lt + " (" + lts + ")" );
+            log.debug(tableTag + " lookup table flags: " + lf + " (" + LookupFlag.toString ( lf ) + ")" );
+            log.debug(tableTag + " lookup table subtable count: " + ns );
+        }
+        // read subtable offsets
+        int[] soa = new int[ns];
+        for ( int i = 0; i < ns; i++ ) {
+            int so = in.readTTFUShort();
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " lookup table subtable offset: " + so );
+            }
+            soa[i] = so;
+        }
+        // read mark filtering set
+        if ( ( lf & LookupFlag.USE_MARK_FILTERING_SET ) != 0 ) {
+            // read mark filtering set
+            int fs = in.readTTFUShort();
+            // dump info if debugging
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " lookup table mark filter set: " + fs );
+            }
+        }
+        // read subtables
+        for ( int i = 0; i < ns; i++ ) {
+            int so = soa[i];
+            if ( isGSUB ) {
+                readGSUBSubtable ( in, lt, lf, lookupSequence, i, lookupTable + so );
+            } else if ( isGPOS ) {
+                readGPOSSubtable ( in, lt, lf, lookupSequence, i, lookupTable + so );
+            }
+        }
+    }
+
+    private void readLookupList(FontFileReader in, String tableTag, long lookupList) throws IOException {
+        in.seekSet(lookupList);
+        // read lookup record count
+        int nl = in.readTTFUShort();
+        if (log.isDebugEnabled()) {
+            log.debug(tableTag + " lookup list record count: " + nl );
+        }
+        if ( nl > 0 ) {
+            int[] loa = new int[nl];
+            // read lookup records
+            for ( int i = 0, n = nl; i < n; i++ ) {
+                int lo = in.readTTFUShort();
+                if (log.isDebugEnabled()) {
+                    log.debug(tableTag + " lookup table offset: " + lo );
+                }
+                loa[i] = lo;
+            }
+            // read lookup tables
+            for ( int i = 0, n = nl; i < n; i++ ) {
+                if (log.isDebugEnabled()) {
+                    log.debug(tableTag + " lookup index: " + i );
+                }
+                readLookupTable ( in, tableTag, i, lookupList + loa [ i ] );
+            }
+        }
+    }
+
+    /**
+     * Read the common layout tables (used by GSUB and GPOS).
+     * @param in FontFileReader to read from
+     * @param scriptList offset to script list from beginning of font file
+     * @param featureList offset to feature list from beginning of font file
+     * @param lookupList offset to lookup list from beginning of font file
+     * @throws IOException In case of a I/O problem
+     */
+    private void readCommonLayoutTables(FontFileReader in, String tableTag, long scriptList, long featureList, long lookupList) throws IOException {
+        if ( scriptList > 0 ) {
+            readScriptList ( in, tableTag, scriptList );
+        }
+        if ( featureList > 0 ) {
+            readFeatureList ( in, tableTag, featureList );
+        }
+        if ( lookupList > 0 ) {
+            readLookupList ( in, tableTag, lookupList );
+        }
+    }
+
+    /**
+     * Read the GSUB table.
+     * @param in FontFileReader to read from
+     * @throws IOException In case of a I/O problem
+     */
+    private void readGSUB(FontFileReader in) throws IOException {
+        String tableTag = "GSUB";
+        // Initialize temporary state
+        initSEState();
+        // Read glyph substitution (GSUB) table
+        TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get(tableTag);
+        if ( gpos != null ) {
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + ": ignoring duplicate table");
+            }
+        } else if (dirTab != null) {
+            seekTab(in, tableTag, 0);
+            int version = in.readTTFLong();
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " version: " + ( version / 65536 ) + "." + ( version % 65536 ));
+            }
+            int slo = in.readTTFUShort();
+            int flo = in.readTTFUShort();
+            int llo = in.readTTFUShort();
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " script list offset: " + slo );
+                log.debug(tableTag + " feature list offset: " + flo );
+                log.debug(tableTag + " lookup list offset: " + llo );
+            }
+            long to = dirTab.getOffset();
+            readCommonLayoutTables ( in, tableTag, to + slo, to + flo, to + llo );
+            GlyphSubstitutionTable gsub;
+            if ( ( gsub = constructGSUB() ) != null ) {
+                this.gsub = gsub;
+            }
+        }
+    }
+
+    /**
+     * Read the GPOS table.
+     * @param in FontFileReader to read from
+     * @throws IOException In case of a I/O problem
+     */
+    private void readGPOS(FontFileReader in) throws IOException {
+        String tableTag = "GPOS";
+        // Initialize temporary state
+        initSEState();
+        // Read glyph positioning (GPOS) table
+        TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get(tableTag);
+        if ( gpos != null ) {
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + ": ignoring duplicate table");
+            }
+        } else if (dirTab != null) {
+            seekTab(in, tableTag, 0);
+            int version = in.readTTFLong();
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " version: " + ( version / 65536 ) + "." + ( version % 65536 ));
+            }
+            int slo = in.readTTFUShort();
+            int flo = in.readTTFUShort();
+            int llo = in.readTTFUShort();
+            if (log.isDebugEnabled()) {
+                log.debug(tableTag + " script list offset: " + slo );
+                log.debug(tableTag + " feature list offset: " + flo );
+                log.debug(tableTag + " lookup list offset: " + llo );
+            }
+            long to = dirTab.getOffset();
+            readCommonLayoutTables ( in, tableTag, to + slo, to + flo, to + llo );
+            GlyphPositioningTable gpos;
+            if ( ( gpos = constructGPOS() ) != null ) {
+                this.gpos = gpos;
+            }
+        }
+    }
+
+    /**
+     * Construct the (internal representation of the) GSUB table based on previously
+     * parsed state.
+     * @returns glyph substitution table or null if insufficient or invalid state
+     */
+    private GlyphSubstitutionTable constructGSUB() {
+        GlyphSubstitutionTable gsub = null;
+        Map lookups;
+        if ( ( lookups = constructLookups() ) != null ) {
+            List subtables;
+            if ( ( subtables = constructGSUBSubtables() ) != null ) {
+                if ( ( lookups.size() > 0 ) && ( subtables.size() > 0 ) ) {
+                    gsub = new GlyphSubstitutionTable ( lookups, subtables );
+                }
+            }
+        }
+        resetSEState();
+        return gsub;
+    }
+
+    /**
+     * Construct the (internal representation of the) GPOS table based on previously
+     * parsed state.
+     * @returns glyph positioning table or null if insufficient or invalid state
+     */
+    private GlyphPositioningTable constructGPOS() {
+        GlyphPositioningTable gpos = null;
+        Map lookups;
+        if ( ( lookups = constructLookups() ) != null ) {
+            List subtables;
+            if ( ( subtables = constructGPOSSubtables() ) != null ) {
+                if ( ( lookups.size() > 0 ) && ( subtables.size() > 0 ) ) {
+                    gpos = new GlyphPositioningTable ( lookups, subtables );
+                }
+            }
+        }
+        resetSEState();
+        return gpos;
+    }
+
+    private void constructLookupsFeature ( Map lookups, String st, String lt, String fid ) {
+        Object[] fp = (Object[]) seFeatures.get ( fid );
+        if ( fp != null ) {
+            assert fp.length == 2;
+            String ft = (String) fp[0];                 // feature tag
+            List/*<String>*/ lul = (List) fp[1];        // list of lookup table ids
+            if ( ( ft != null ) && ( lul != null ) && ( lul.size() > 0 ) ) {
+                GlyphTable.LookupSpec ls = new GlyphTable.LookupSpec ( st, lt, ft );
+                lookups.put ( ls, lul );
+            }
+        }
+    }
+
+    private void constructLookupsFeatures ( Map lookups, String st, String lt, List/*<String>*/ fids ) {
+        for ( Iterator fit = fids.iterator(); fit.hasNext();) {
+            String fid = (String) fit.next();
+            constructLookupsFeature ( lookups, st, lt, fid );
+        }
+    }
+
+    private void constructLookupsLanguage ( Map lookups, String st, String lt, Map/*<String,Object[2]>*/ languages ) {
+        Object[] lp = (Object[]) languages.get ( lt );
+        if ( lp != null ) {
+            assert lp.length == 2;
+            if ( lp[0] != null ) {                      // required feature id
+                constructLookupsFeature ( lookups, st, lt, (String) lp[0] );
+            }
+            if ( lp[1] != null ) {                      // non-required features ids
+                constructLookupsFeatures ( lookups, st, lt, (List) lp[1] );
+            }
+        }
+    }
+
+    private void constructLookupsLanguages ( Map lookups, String st, List/*<String>*/ ll, Map/*<String,Object[2]>*/ languages ) {
+        for ( Iterator lit = ll.iterator(); lit.hasNext();) {
+            String lt = (String) lit.next();
+            constructLookupsLanguage ( lookups, st, lt, languages );
+        }
+    }
+
+    private Map constructLookups() {
+        Map/*<GlyphTable.LookupSpec,List<String>>*/ lookups = new java.util.LinkedHashMap();
+        for ( Iterator sit = seScripts.keySet().iterator(); sit.hasNext();) {
+            String st = (String) sit.next();
+            Object[] sp = (Object[]) seScripts.get ( st );
+            if ( sp != null ) {
+                assert sp.length == 3;
+                Map/*<String,Object[2]>*/ languages = (Map) sp[2];
+                if ( sp[0] != null ) {                  // default language
+                    constructLookupsLanguage ( lookups, st, (String) sp[0], languages );
+                }
+                if ( sp[1] != null ) {                  // non-default languages
+                    constructLookupsLanguages ( lookups, st, (List) sp[1], languages );
+                }
+            }
+        }
+        return lookups;
+    }
+
+    private List constructGSUBSubtables() {
+        List/*<GlyphSubtable>*/ subtables = new java.util.ArrayList();
+        if ( seSubtables != null ) {
+            for ( Iterator it = seSubtables.iterator(); it.hasNext();) {
+                Object[] stp = (Object[]) it.next();
+                GlyphSubtable st;
+                if ( ( st = constructGSUBSubtable ( stp ) ) != null ) {
+                    subtables.add ( st );
+                }
+            }
+        }
+        return subtables;
+    }
+
+    private GlyphSubtable constructGSUBSubtable ( Object[] stp ) {
+        GlyphSubtable st = null;
+        assert ( stp != null ) && ( stp.length == 8 );
+        Integer tt = (Integer) stp[0];
+        Integer lt = (Integer) stp[1];
+        Integer ln = (Integer) stp[2];
+        Integer lf = (Integer) stp[3];
+        // Integer sn = (Integer) stp[4]; // not used yet
+        Integer sf = (Integer) stp[5];
+        List coverage = (List) stp[6];
+        List entries = (List) stp[7];
+        if ( tt.intValue() == GlyphTable.GLYPH_TABLE_TYPE_SUBSTITUTION ) {
+            int type = GSUBLookupType.getSubtableType ( lt.intValue() );
+            String id = "lu" + ln.intValue();
+            int sequence = ln.intValue();
+            int flags = lf.intValue();
+            int format = sf.intValue();
+            st = GlyphSubstitutionTable.createSubtable ( type, id, sequence, flags, format, coverage, entries );
+        }
+        return st;
+    }
+
+    private List constructGPOSSubtables() {
+        List/*<GlyphSubtable>*/ subtables = new java.util.ArrayList();
+        return subtables;
+    }
+
+    private void initSEState() {
+        seScripts = new java.util.LinkedHashMap();
+        seLanguages = new java.util.LinkedHashMap();
+        seFeatures = new java.util.LinkedHashMap();
+        seSubtables = new java.util.ArrayList();
+        resetSESubState();
+    }
+
+    private void resetSEState() {
+        seScripts = null;
+        seLanguages = null;
+        seFeatures = null;
+        seSubtables = null;
+        resetSESubState();
+    }
+
+    private void initSESubState() {
+        seCoverage = new java.util.ArrayList();
+        seEntries = new java.util.ArrayList();
+    }
+
+    private void extractSESubState ( int tableType, int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, int subtableFormat ) {
+        if ( ( seCoverage != null ) && ( seCoverage.size() > 0 ) ) {
+            if ( ( seEntries != null ) && ( seEntries.size() > 0 ) ) {
+                if ( seSubtables != null ) {
+                    Integer tt = Integer.valueOf ( tableType );
+                    Integer lt = Integer.valueOf ( lookupType );
+                    Integer ln = Integer.valueOf ( lookupSequence );
+                    Integer lf = Integer.valueOf ( lookupFlags );
+                    Integer sn = Integer.valueOf ( subtableSequence );
+                    Integer sf = Integer.valueOf ( subtableFormat );
+                    seSubtables.add ( new Object[] { tt, lt, ln, lf, sn, sf, seCoverage, seEntries } );
+                }
+            }
+        }
+    }
+
+    private void resetSESubState() {
+        seCoverage = null;
+        seEntries = null;
+    }
+
     /**
      * Return a List with TTFCmapEntry.
      * @return A list of TTFCmapEntry objects
@@ -1715,4 +3246,4 @@ public class TTFFile {
             ioe.printStackTrace(System.err);
         }
     }
-}
\ No newline at end of file
+}

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java Thu Aug 19 19:46:41 2010
@@ -55,7 +55,7 @@ public class TTFFontLoader extends FontL
      * @param resolver the FontResolver for font URI resolution
      */
     public TTFFontLoader(String fontFileURI, FontResolver resolver) {
-        this(fontFileURI, null, true, EncodingMode.AUTO, true, resolver);
+        this(fontFileURI, null, true, EncodingMode.AUTO, true, true, resolver);
     }
 
     /**
@@ -66,12 +66,13 @@ public class TTFFontLoader extends FontL
      * @param embedded indicates whether the font is embedded or referenced
      * @param encodingMode the requested encoding mode
      * @param useKerning true to enable loading kerning info if available, false to disable
+     * @param useAdvanced true to enable loading advanced info if available, false to disable
      * @param resolver the FontResolver for font URI resolution
      */
     public TTFFontLoader(String fontFileURI, String subFontName,
                 boolean embedded, EncodingMode encodingMode, boolean useKerning,
-                FontResolver resolver) {
-        super(fontFileURI, embedded, true, resolver);
+                boolean useAdvanced, FontResolver resolver) {
+        super(fontFileURI, embedded, useKerning, useAdvanced, resolver);
         this.subFontName = subFontName;
         this.encodingMode = encodingMode;
         if (this.encodingMode == EncodingMode.AUTO) {
@@ -169,6 +170,9 @@ public class TTFFontLoader extends FontL
         if (useKerning) {
             copyKerning(ttf, isCid);
         }
+        if (useAdvanced) {
+            copyAdvanced(ttf);
+        }
         if (this.embedded && ttf.isEmbeddable()) {
             returnFont.setEmbedFileName(this.fontFileURI);
         }
@@ -224,4 +228,16 @@ public class TTFFontLoader extends FontL
             returnFont.putKerningEntry(kpx1, h2);
         }
     }
+
+    /**
+     * Copy advanced typographic information.
+     */
+    private void copyAdvanced ( TTFFile ttf ) {
+        if ( returnFont instanceof MultiByteFont ) {
+            MultiByteFont mbf = (MultiByteFont) returnFont;
+            mbf.setGSUB ( ttf.getGSUB() );
+            mbf.setGPOS ( ttf.getGPOS() );
+        }
+    }
+
 }

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java Thu Aug 19 19:46:41 2010
@@ -52,7 +52,7 @@ public class Type1FontLoader extends Fon
      */
     public Type1FontLoader(String fontFileURI, boolean embedded, boolean useKerning,
             FontResolver resolver) throws IOException {
-        super(fontFileURI, embedded, useKerning, resolver);
+        super(fontFileURI, embedded, useKerning, true, resolver);
     }
 
     private String getPFMURI(String pfbURI) {

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java Thu Aug 19 19:46:41 2010
@@ -460,7 +460,7 @@ public abstract class AbstractLayoutMana
 
     /** {@inheritDoc} */
     public String toString() {
-        return (super.toString() + (fobj != null ? "[fobj=" + fobj.toString() + "]" : ""));
+        return (super.toString() + (fobj != null ? "{fobj = " + fobj.toString() + "}" : ""));
     }
 
     /** {@inheritDoc} */



---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-help@xmlgraphics.apache.org