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 ga...@apache.org on 2012/02/26 03:29:29 UTC

svn commit: r1293736 [31/38] - in /xmlgraphics/fop/trunk: ./ src/codegen/java/org/apache/fop/tools/ src/codegen/unicode/java/org/apache/fop/complexscripts/ src/codegen/unicode/java/org/apache/fop/complexscripts/bidi/ src/documentation/content/xdocs/tru...

Added: xmlgraphics/fop/trunk/test/java/org/apache/fop/complexscripts/fonts/ttx/TTXFile.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/test/java/org/apache/fop/complexscripts/fonts/ttx/TTXFile.java?rev=1293736&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/test/java/org/apache/fop/complexscripts/fonts/ttx/TTXFile.java (added)
+++ xmlgraphics/fop/trunk/test/java/org/apache/fop/complexscripts/fonts/ttx/TTXFile.java Sun Feb 26 02:29:01 2012
@@ -0,0 +1,3450 @@
+/*
+ * 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.complexscripts.fonts.ttx;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.nio.IntBuffer;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+import java.util.TreeMap;
+import java.util.Vector;
+
+import javax.xml.parsers.FactoryConfigurationError;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.complexscripts.fonts.GlyphClassTable;
+import org.apache.fop.complexscripts.fonts.GlyphCoverageTable;
+import org.apache.fop.complexscripts.fonts.GlyphDefinitionSubtable;
+import org.apache.fop.complexscripts.fonts.GlyphDefinitionTable;
+import org.apache.fop.complexscripts.fonts.GlyphMappingTable;
+import org.apache.fop.complexscripts.fonts.GlyphPositioningSubtable;
+import org.apache.fop.complexscripts.fonts.GlyphPositioningTable;
+import org.apache.fop.complexscripts.fonts.GlyphPositioningTable.Anchor;
+import org.apache.fop.complexscripts.fonts.GlyphPositioningTable.MarkAnchor;
+import org.apache.fop.complexscripts.fonts.GlyphPositioningTable.PairValues;
+import org.apache.fop.complexscripts.fonts.GlyphPositioningTable.Value;
+import org.apache.fop.complexscripts.fonts.GlyphSubstitutionSubtable;
+import org.apache.fop.complexscripts.fonts.GlyphSubstitutionTable;
+import org.apache.fop.complexscripts.fonts.GlyphSubstitutionTable.Ligature;
+import org.apache.fop.complexscripts.fonts.GlyphSubstitutionTable.LigatureSet;
+import org.apache.fop.complexscripts.fonts.GlyphSubtable;
+import org.apache.fop.complexscripts.fonts.GlyphTable;
+import org.apache.fop.complexscripts.fonts.GlyphTable.RuleLookup;
+import org.apache.fop.complexscripts.util.GlyphSequence;
+import org.apache.fop.complexscripts.util.UTF32;
+import org.apache.fop.util.CharUtilities;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+
+// CSOFF: InnerAssignmentCheck
+// CSOFF: LineLengthCheck
+// CSOFF: NoWhitespaceAfterCheck
+
+/**
+ * This class supports a subset of the <code>TTX</code> file as produced by the Adobe FLEX
+ * SDK (AFDKO). In particular, it is used to parse a <code>TTX</code> file in order to
+ * extract character to glyph code mapping data, glyph definition data, glyph substitution
+ * data, and glyph positioning data.
+ *
+ * <code>TTX</code> files are used in FOP for testing and debugging purposes only. Such
+ * files are used to represent font data employed by complex script processing, and
+ * normally extracted directly from an opentype (or truetype) file. However, due to
+ * copyright restrictions, it is not possible to include most opentype (or truetype) font
+ * files directly in the FOP distribution. In such cases, <code>TTX</code> files are used
+ * to distribute a subset of the complex script advanced table information contained in
+ * certain font files to facilitate testing.
+ *
+ * @author Glenn Adams
+ */
+public class TTXFile {
+
+    /** logging instance */
+    private static final Log log = LogFactory.getLog(TTXFile.class);                                                    // CSOK: ConstantNameCheck
+    /** default script tag */
+    private static final String DEFAULT_SCRIPT_TAG = "dflt";
+    /** default language tag */
+    private static final String DEFAULT_LANGUAGE_TAG = "dflt";
+
+    /** ttxfile cache */
+    private static Map<String,TTXFile> cache = new HashMap<String,TTXFile>();
+
+    // transient parsing state
+    private Locator locator;                                    // current document locator
+    private Stack<String[]> elements;                           // stack of ttx elements being parsed
+    private Map<String,Integer> glyphIds;                       // map of glyph names to glyph identifiers
+    private List<int[]> cmapEntries;                            // list of <charCode,glyphCode> pairs
+    private Vector<int[]> hmtxEntries;                          // vector of <width,lsb> pairs
+    private Map<String,Integer> glyphClasses;                   // map of glyph names to glyph classes
+    private Map<String,Map<String,List<String>>> scripts;       // map of script tag to Map<language-tag,List<features-id>>>
+    private Map<String,List<String>> languages;                 // map of language tag to List<feature-id>
+    private Map<String,Object[]> features;                      // map of feature id to Object[2] : { feature-tag, List<lookup-id> }
+    private List<String> languageFeatures;                      // list of language system feature ids, where first is (possibly null) required feature id
+    private List<String> featureLookups;                        // list of lookup ids for feature being constructed
+    private List<Integer> coverageEntries;                      // list of entries for coverage table being constructed
+    private Map<String,GlyphCoverageTable> coverages;           // map of coverage table keys to coverage tables
+    private List subtableEntries;                               // list of lookup subtable entries
+    private List<GlyphSubtable> subtables;                      // list of constructed subtables
+    private List<Integer> alternates;                           // list of alternates in alternate set being constructed
+    private List<Ligature> ligatures;                           // list of ligatures in ligature set being constructed
+    private List<Integer> substitutes;                          // list of substitutes in (multiple substitution) sequence being constructed
+    private List<PairValues> pairs;                             // list of pair value records being constructed
+    private List<PairValues[]> pairSets;                        // list of pair value sets (as arrays) being constructed
+    private List<Anchor> anchors;                               // list of anchors of base|mark|component record being constructed
+    private List<Anchor[]> components;                          // list of ligature component anchors being constructed
+    private List<MarkAnchor> markAnchors;                       // list of mark anchors being constructed
+    private List<Anchor[]> baseOrMarkAnchors;                   // list of base|mark2 anchors being constructed
+    private List<Anchor[][]> ligatureAnchors;                   // list of ligature anchors being constructed
+    private List<Anchor[]> attachmentAnchors;                   // list of entry|exit attachment anchors being constructed
+    private List<RuleLookup> ruleLookups;                       // list of rule lookups being constructed
+    private int glyphIdMax;                                     // maximum glyph id
+    private int cmPlatform;                                     // plaform id of cmap being constructed
+    private int cmEncoding;                                     // plaform id of cmap being constructed
+    private int cmLanguage;                                     // plaform id of cmap being constructed
+    private int flIndex;                                        // index of feature being constructed
+    private int flSequence;                                     // feature sequence within feature list
+    private int ltIndex;                                        // index of lookup table being constructed
+    private int ltSequence;                                     // lookup sequence within table
+    private int ltFlags;                                        // flags of current lookup being constructed
+    private int stSequence;                                     // subtable sequence number within lookup
+    private int stFormat;                                       // format of current subtable being constructed
+    private int ctFormat;                                       // format of coverage table being constructed
+    private int ctIndex;                                        // index of coverage table being constructed
+    private int rlSequence;                                     // rule lookup sequence index
+    private int rlLookup;                                       // rule lookup lookup index
+    private int psIndex;                                        // pair set index
+    private int vf1;                                            // value format 1 (used with pair pos and single pos)
+    private int vf2;                                            // value format 2 (used with pair pos)
+    private int g2;                                             // glyph id 2 (used with pair pos)
+    private int xCoord;                                         // x coordinate of anchor being constructed
+    private int yCoord;                                         // y coordinate of anchor being constructed
+    private int markClass;                                      // mark class of mark anchor being constructed
+    private String defaultScriptTag;                            // tag of default script
+    private String scriptTag;                                   // tag of script being constructed
+    private String defaultLanguageTag;                          // tag of default language system
+    private String languageTag;                                 // tag of language system being constructed
+    private String featureTag;                                  // tag of feature being constructed
+    private Value v1;                                           // positioining value 1
+    private Value v2;                                           // positioining value 2
+
+    // resultant state
+    private int upem;                                           // units per em
+    private Map<Integer,Integer> cmap;                          // constructed character map
+    private Map<Integer,Integer> gmap;                          // constructed glyph map
+    private int[][] hmtx;                                       // constructed horizontal metrics - array of design { width, lsb } pairs, indexed by glyph code
+    private int[] widths;                                       // pdf normalized widths (millipoints)
+    private GlyphDefinitionTable gdef;                          // constructed glyph definition table
+    private GlyphSubstitutionTable gsub;                        // constructed glyph substitution table
+    private GlyphPositioningTable gpos;                         // constructed glyph positioning table
+
+    public TTXFile() {
+        elements = new Stack<String[]>();
+        glyphIds = new HashMap<String,Integer>();
+        cmapEntries = new ArrayList<int[]>();
+        hmtxEntries = new Vector<int[]>();
+        glyphClasses = new HashMap<String,Integer>();
+        scripts = new HashMap<String,Map<String,List<String>>>();
+        languages = new HashMap<String,List<String>>();
+        features = new HashMap<String,Object[]>();
+        languageFeatures = new ArrayList<String>();
+        featureLookups = new ArrayList<String>();
+        coverageEntries = new ArrayList<Integer>();
+        coverages = new HashMap<String,GlyphCoverageTable>();
+        subtableEntries = new ArrayList();
+        subtables = new ArrayList<GlyphSubtable>();
+        alternates = new ArrayList<Integer>();
+        ligatures = new ArrayList<Ligature>();
+        substitutes = new ArrayList<Integer>();
+        pairs = new ArrayList<PairValues>();
+        pairSets = new ArrayList<PairValues[]>();
+        anchors = new ArrayList<Anchor>();
+        markAnchors = new ArrayList<MarkAnchor>();
+        baseOrMarkAnchors = new ArrayList<Anchor[]>();
+        ligatureAnchors = new ArrayList<Anchor[][]>();
+        components = new ArrayList<Anchor[]>();
+        attachmentAnchors = new ArrayList<Anchor[]>();
+        ruleLookups = new ArrayList<RuleLookup>();
+        glyphIdMax = -1;
+        cmPlatform = -1;
+        cmEncoding = -1;
+        cmLanguage = -1;
+        flIndex = -1;
+        flSequence = 0;
+        ltIndex = -1;
+        ltSequence = 0;
+        ltFlags = 0;
+        stSequence = 0;
+        stFormat = 0;
+        ctFormat = -1;
+        ctIndex = -1;
+        rlSequence = -1;
+        rlLookup = -1;
+        psIndex = -1;
+        vf1 = -1;
+        vf2 = -1;
+        g2 = -1;
+        xCoord = Integer.MIN_VALUE;
+        yCoord = Integer.MIN_VALUE;
+        markClass = -1;
+        defaultScriptTag = DEFAULT_SCRIPT_TAG;
+        scriptTag = null;
+        defaultLanguageTag = DEFAULT_LANGUAGE_TAG;
+        languageTag = null;
+        featureTag = null;
+        v1 = null;
+        v2 = null;
+        upem = -1;
+    }
+    public void parse ( String filename ) {
+        parse ( new File ( filename ) );
+    }
+    public void parse ( File f ) {
+        assert f != null;
+        try {
+            SAXParserFactory spf = SAXParserFactory.newInstance();
+            SAXParser sp = spf.newSAXParser();
+            sp.parse ( f, new Handler() );
+        } catch ( FactoryConfigurationError e ) {
+            throw new RuntimeException ( e.getMessage() );
+        } catch ( ParserConfigurationException e ) {
+            throw new RuntimeException ( e.getMessage() );
+        } catch ( SAXException e ) {
+            throw new RuntimeException ( e.getMessage() );
+        } catch ( IOException e ) {
+            throw new RuntimeException ( e.getMessage() );
+        }
+    }
+    public GlyphSequence mapCharsToGlyphs ( String s ) {
+        Integer[] ca = UTF32.toUTF32 ( s, 0, true );
+        int ng = ca.length;
+        IntBuffer cb = IntBuffer.allocate ( ng );
+        IntBuffer gb = IntBuffer.allocate ( ng );
+        for ( Integer c : ca ) {
+            int g = mapCharToGlyph ( (int) c );
+            if ( g >= 0 ) {
+                cb.put ( c );
+                gb.put ( g );
+            } else {
+                throw new IllegalArgumentException ( "character " + CharUtilities.format ( c ) + " has no corresponding glyph" );
+            }
+        }
+        cb.rewind();
+        gb.rewind();
+        return new GlyphSequence ( cb, gb, null );
+    }
+    public int mapCharToGlyph ( int c ) {
+        if ( cmap != null ) {
+            Integer g = cmap.get ( Integer.valueOf ( c ) );
+            if ( g != null ) {
+                return (int) g;
+            } else {
+                return -1;
+            }
+        } else {
+            return -1;
+        }
+    }
+    public int getGlyph ( String gid ) {
+        return mapGlyphId0 ( gid );
+    }
+    public GlyphSequence getGlyphSequence ( String[] gids ) {
+        assert gids != null;
+        int ng = gids.length;
+        IntBuffer cb = IntBuffer.allocate ( ng );
+        IntBuffer gb = IntBuffer.allocate ( ng );
+        for ( String gid : gids ) {
+            int g = mapGlyphId0 ( gid );
+            if ( g >= 0 ) {
+                int c = mapGlyphIdToChar ( gid );
+                if ( c < 0 ) {
+                    c = CharUtilities.NOT_A_CHARACTER;
+                }
+                cb.put ( c );
+                gb.put ( g );
+            } else {
+                throw new IllegalArgumentException ( "unmapped glyph id \"" + gid + "\"" );
+            }
+        }
+        cb.rewind();
+        gb.rewind();
+        return new GlyphSequence ( cb, gb, null );
+    }
+    public int[] getWidths ( String[] gids ) {
+        assert gids != null;
+        int ng = gids.length;
+        int[] widths = new int [ ng ];
+        int i = 0;
+        for ( String gid : gids ) {
+            int g = mapGlyphId0 ( gid );
+            int w = 0;
+            if ( g >= 0 ) {
+                if ( ( hmtx != null ) && ( g < hmtx.length ) ) {
+                    int[] mtx = hmtx [ g ];
+                    assert mtx != null;
+                    assert mtx.length > 0;
+                    w = mtx[0];
+                }
+            }
+            widths [ i++ ] = w;
+        }
+        assert i == ng;
+        return widths;
+    }
+    public int[] getWidths() {
+        if ( this.widths == null ) {
+            if ( ( hmtx != null ) && ( upem > 0 ) ) {
+                int[] widths = new int [ hmtx.length ];
+                for ( int i = 0, n = widths.length; i < n; i++ ) {
+                    widths [ i ] = getPDFWidth ( hmtx [ i ] [ 0 ], upem );
+                }
+                this.widths = widths;
+            }
+        }
+        return this.widths;
+    }
+    public static int getPDFWidth ( int tw, int upem ) {
+        // N.B. The following is copied (with minor edits) from TTFFile to insure same results
+        int pw;
+        if ( tw < 0 ) {
+            long rest1 = tw % upem;
+            long storrest = 1000 * rest1;
+            long ledd2 = ( storrest != 0 ) ? ( rest1 / storrest ) : 0;
+            pw = - ( ( -1000 * tw ) / upem - (int) ledd2 );
+        } else {
+            pw = ( tw / upem ) * 1000 + ( ( tw % upem ) * 1000 ) / upem;
+        }
+        return pw;
+    }
+    public GlyphDefinitionTable getGDEF() {
+        return gdef;
+    }
+    public GlyphSubstitutionTable getGSUB() {
+        return gsub;
+    }
+    public GlyphPositioningTable getGPOS() {
+        return gpos;
+    }
+    public static synchronized TTXFile getFromCache ( String filename ) {
+        assert cache != null;
+        TTXFile f;
+        if ( ( f = (TTXFile) cache.get ( filename ) ) == null ) {
+            f = new TTXFile();
+            f.parse ( filename );
+            cache.put ( filename, f );
+        }
+        return f;
+    }
+    public static synchronized void clearCache() {
+        cache.clear();
+    }
+    private class Handler extends DefaultHandler {
+        private Handler() {
+        }
+        @Override
+        public void startDocument() {
+        }
+        @Override
+        public void endDocument() {
+        }
+        @Override
+        public void setDocumentLocator ( Locator locator ) {
+            TTXFile.this.locator = locator;
+        }
+        @Override
+        public void startElement ( String uri, String localName, String qName, Attributes attrs ) throws SAXException {
+            String[] en = makeExpandedName ( uri, localName, qName );
+            if ( en[0] != null ) {
+                unsupportedElement ( en );
+            } else if ( en[1].equals ( "Alternate" ) ) {
+                String[] pn = new String[] { null, "AlternateSet" };
+                if ( isParent ( pn ) ) {
+                    String glyph = attrs.getValue ( "glyph" );
+                    if ( glyph == null ) {
+                        missingRequiredAttribute ( en, "glyph" );
+                    }
+                    int gid = mapGlyphId ( glyph, en );
+                    alternates.add ( Integer.valueOf ( gid ) );
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "AlternateSet" ) ) {
+                String[] pn = new String[] { null, "AlternateSubst" };
+                if ( isParent ( pn ) ) {
+                    String glyph = attrs.getValue ( "glyph" );
+                    if ( glyph == null ) {
+                        missingRequiredAttribute ( en, "glyph" );
+                    }
+                    int gid = mapGlyphId ( glyph, en );
+                    coverageEntries.add ( Integer.valueOf ( gid ) );
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "AlternateSubst" ) ) {
+                String[] pn = new String[] { null, "Lookup" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int sf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        sf = Integer.parseInt ( format );
+                        switch ( sf ) {
+                        case 1:
+                            break;
+                        default:
+                            unsupportedFormat ( en, sf );
+                            break;
+                        }
+                    }
+                    assertCoverageClear();
+                    ctIndex = 0;
+                    ctFormat = 1;
+                    assertSubtableClear();
+                    assert sf >= 0;
+                    stFormat = sf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "BacktrackCoverage" ) ) {
+                String[] pn1 = new String[] { null, "ChainContextSubst" };
+                String[] pn2 = new String[] { null, "ChainContextPos" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( isParent ( pnx ) ) {
+                    String index = attrs.getValue ( "index" );
+                    int ci = -1;
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    } else {
+                        ci = Integer.parseInt ( index );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int cf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        cf = Integer.parseInt ( format );
+                        switch ( cf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, cf );
+                            break;
+                        }
+                    }
+                    assertCoverageClear();
+                    ctIndex = ci;
+                    ctFormat = cf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "BaseAnchor" ) ) {
+                String[] pn = new String[] { null, "BaseRecord" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    }
+                    assert xCoord == Integer.MIN_VALUE;
+                    assert yCoord == Integer.MIN_VALUE;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "BaseArray" ) ) {
+                String[] pn = new String[] { null, "MarkBasePos" };
+                if ( ! isParent ( pn ) ) {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "BaseCoverage" ) ) {
+                String[] pn = new String[] { null, "MarkBasePos" };
+                if ( isParent ( pn ) ) {
+                    String format = attrs.getValue ( "Format" );
+                    int cf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        cf = Integer.parseInt ( format );
+                        switch ( cf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, cf );
+                            break;
+                        }
+                    }
+                    assertCoverageClear();
+                    ctIndex = 0;
+                    ctFormat = cf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "BaseRecord" ) ) {
+                String[] pn = new String[] { null, "BaseArray" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "ChainContextPos" ) || en[1].equals ( "ChainContextSubst" ) ) {
+                String[] pn = new String[] { null, "Lookup" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int sf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        sf = Integer.parseInt ( format );
+                        switch ( sf ) {
+                        case 1:
+                        case 2:
+                        case 3:
+                            break;
+                        default:
+                            unsupportedFormat ( en, sf );
+                            break;
+                        }
+                    }
+                    assertSubtableClear();
+                    assert sf >= 0;
+                    stFormat = sf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Class" ) ) {
+                String[] pn = new String[] { null, "MarkRecord" };
+                if ( isParent ( pn ) ) {
+                    String value = attrs.getValue ( "value" );
+                    int v = -1;
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        v = Integer.parseInt ( value );
+                    }
+                    assert markClass == -1;
+                    markClass = v;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "ClassDef" ) ) {
+                String[] pn1 = new String[] { null, "GlyphClassDef" };
+                String[] pn2 = new String[] { null, "MarkAttachClassDef" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( isParent ( pnx ) ) {
+                    String glyph = attrs.getValue ( "glyph" );
+                    if ( glyph == null ) {
+                        missingRequiredAttribute ( en, "glyph" );
+                    }
+                    String glyphClass = attrs.getValue ( "class" );
+                    if ( glyphClass == null ) {
+                        missingRequiredAttribute ( en, "class" );
+                    }
+                    if ( ! glyphIds.containsKey ( glyph ) ) {
+                        unsupportedGlyph ( en, glyph );
+                    } else if ( isParent ( pn1 ) ) {
+                        if ( glyphClasses.containsKey ( glyph ) ) {
+                            duplicateGlyphClass ( en, glyph, glyphClass );
+                        } else {
+                            glyphClasses.put ( glyph, Integer.parseInt(glyphClass) );
+                        }
+                    } else if ( isParent ( pn2 ) ) {
+                        if ( glyphClasses.containsKey ( glyph ) ) {
+                            duplicateGlyphClass ( en, glyph, glyphClass );
+                        } else {
+                            glyphClasses.put ( glyph, Integer.parseInt(glyphClass) );
+                        }
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "ComponentRecord" ) ) {
+                String[] pn = new String[] { null, "LigatureAttach" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    assert anchors.size() == 0;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Coverage" ) ) {
+                String[] pn1 = new String[] { null, "CursivePos" };
+                String[] pn2 = new String[] { null, "LigCaretList" };
+                String[] pn3 = new String[] { null, "MultipleSubst" };
+                String[] pn4 = new String[] { null, "PairPos" };
+                String[] pn5 = new String[] { null, "SinglePos" };
+                String[][] pnx = new String[][] { pn1, pn2, pn3, pn4, pn5 };
+                if ( isParent ( pnx ) ) {
+                    String format = attrs.getValue ( "Format" );
+                    int cf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        cf = Integer.parseInt ( format );
+                        switch ( cf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, cf );
+                            break;
+                        }
+                    }
+                    assertCoverageClear();
+                    ctIndex = 0;
+                    ctFormat = cf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "CursivePos" ) ) {
+                String[] pn = new String[] { null, "Lookup" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int sf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        sf = Integer.parseInt ( format );
+                        switch ( sf ) {
+                        case 1:
+                            break;
+                        default:
+                            unsupportedFormat ( en, sf );
+                            break;
+                        }
+                    }
+                    assertSubtableClear();
+                    assert sf >= 0;
+                    stFormat = sf;
+                    assert attachmentAnchors.size() == 0;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "DefaultLangSys" ) ) {
+                String[] pn = new String[] { null, "Script" };
+                if ( ! isParent ( pn ) ) {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                } else {
+                    assertLanguageFeaturesClear();
+                    assert languageTag == null;
+                    languageTag = defaultLanguageTag;
+                }
+            } else if ( en[1].equals ( "EntryAnchor" ) ) {
+                String[] pn = new String[] { null, "EntryExitRecord" };
+                if ( isParent ( pn ) ) {
+                    String format = attrs.getValue ( "Format" );
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    }
+                    assert xCoord == Integer.MIN_VALUE;
+                    assert yCoord == Integer.MIN_VALUE;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "EntryExitRecord" ) ) {
+                String[] pn = new String[] { null, "CursivePos" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "ExitAnchor" ) ) {
+                String[] pn = new String[] { null, "EntryExitRecord" };
+                if ( isParent ( pn ) ) {
+                    String format = attrs.getValue ( "Format" );
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    }
+                    assert xCoord == Integer.MIN_VALUE;
+                    assert yCoord == Integer.MIN_VALUE;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Feature" ) ) {
+                String[] pn = new String[] { null, "FeatureRecord" };
+                if ( ! isParent ( pn ) ) {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                } else {
+                    assertFeatureLookupsClear();
+                }
+            } else if ( en[1].equals ( "FeatureIndex" ) ) {
+                String[] pn1 = new String[] { null, "DefaultLangSys" };
+                String[] pn2 = new String[] { null, "LangSys" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( isParent ( pnx ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String value = attrs.getValue ( "value" );
+                    int v = -1;
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        v = Integer.parseInt ( value );
+                    }
+                    if ( languageFeatures.size() == 0 ) {
+                        languageFeatures.add ( null );
+                    }
+                    if ( ( v >= 0 ) && ( v < 65535 ) ) {
+                        languageFeatures.add ( makeFeatureId ( v ) );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "FeatureList" ) ) {
+                String[] pn1 = new String[] { null, "GSUB" };
+                String[] pn2 = new String[] { null, "GPOS" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( ! isParent ( pnx ) ) {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "FeatureRecord" ) ) {
+                String[] pn = new String[] { null, "FeatureList" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    int fi = -1;
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    } else {
+                        fi = Integer.parseInt ( index );
+                    }
+                    assertFeatureClear();
+                    assert flIndex == -1;
+                    flIndex = fi;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "FeatureTag" ) ) {
+                String[] pn = new String[] { null, "FeatureRecord" };
+                if ( isParent ( pn ) ) {
+                    String value = attrs.getValue ( "value" );
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        assert featureTag == null;
+                        featureTag = value;
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "GDEF" ) ) {
+                String[] pn = new String[] { null, "ttFont" };
+                if ( isParent ( pn ) ) {
+                    assertSubtablesClear();
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "GPOS" ) ) {
+                String[] pn = new String[] { null, "ttFont" };
+                if ( isParent ( pn ) ) {
+                    assertCoveragesClear();
+                    assertSubtablesClear();
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "GSUB" ) ) {
+                String[] pn = new String[] { null, "ttFont" };
+                if ( isParent ( pn ) ) {
+                    assertCoveragesClear();
+                    assertSubtablesClear();
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Glyph" ) ) {
+                String[] pn1 = new String[] { null, "Coverage" };
+                String[] pn2 = new String[] { null, "InputCoverage" };
+                String[] pn3 = new String[] { null, "LookAheadCoverage" };
+                String[] pn4 = new String[] { null, "BacktrackCoverage" };
+                String[] pn5 = new String[] { null, "MarkCoverage" };
+                String[] pn6 = new String[] { null, "Mark1Coverage" };
+                String[] pn7 = new String[] { null, "Mark2Coverage" };
+                String[] pn8 = new String[] { null, "BaseCoverage" };
+                String[] pn9 = new String[] { null, "LigatureCoverage" };
+                String[][] pnx = new String[][] { pn1, pn2, pn3, pn4, pn5, pn6, pn7, pn8, pn9 };
+                if ( isParent ( pnx ) ) {
+                    String value = attrs.getValue ( "value" );
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        int gid = mapGlyphId ( value, en );
+                        coverageEntries.add ( Integer.valueOf ( gid ) );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "GlyphClassDef" ) ) {
+                String[] pn = new String[] { null, "GDEF" };
+                if ( isParent ( pn ) ) {
+                    String format = attrs.getValue ( "Format" );
+                    int sf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        sf = Integer.parseInt ( format );
+                        switch ( sf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, sf );
+                            break;
+                        }
+                    }
+                    assertSubtableClear();
+                    assert sf >= 0;
+                    // force format 1 since TTX always writes entries as non-range entries
+                    if ( sf != 1 ) {
+                        sf = 1;
+                    }
+                    stFormat = sf;
+                    assert glyphClasses.isEmpty();
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "GlyphID" ) ) {
+                String[] pn = new String[] { null, "GlyphOrder" };
+                if ( isParent ( pn ) ) {
+                    String id = attrs.getValue ( "id" );
+                    int gid = -1;
+                    if ( id == null ) {
+                        missingRequiredAttribute ( en, "id" );
+                    } else {
+                        gid = Integer.parseInt ( id );
+                    }
+                    String name = attrs.getValue ( "name" );
+                    if ( name == null ) {
+                        missingRequiredAttribute ( en, "name" );
+                    }
+                    if ( glyphIds.containsKey ( name ) ) {
+                        duplicateGlyph ( en, name, gid );
+                    } else {
+                        if ( gid > glyphIdMax ) {
+                            glyphIdMax = gid;
+                        }
+                        glyphIds.put ( name, gid );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "GlyphOrder" ) ) {
+                String[] pn = new String[] { null, "ttFont" };
+                if ( ! isParent ( pn ) ) {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "InputCoverage" ) ) {
+                String[] pn1 = new String[] { null, "ChainContextSubst" };
+                String[] pn2 = new String[] { null, "ChainContextPos" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( isParent ( pnx ) ) {
+                    String index = attrs.getValue ( "index" );
+                    int ci = -1;
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    } else {
+                        ci = Integer.parseInt ( index );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int cf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        cf = Integer.parseInt ( format );
+                        switch ( cf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, cf );
+                            break;
+                        }
+                    }
+                    assertCoverageClear();
+                    ctIndex = ci;
+                    ctFormat = cf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "LangSys" ) ) {
+                String[] pn = new String[] { null, "LangSysRecord" };
+                if ( ! isParent ( pn ) ) {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                } else {
+                    assertLanguageFeaturesClear();
+                }
+            } else if ( en[1].equals ( "LangSysRecord" ) ) {
+                String[] pn = new String[] { null, "Script" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "LangSysTag" ) ) {
+                String[] pn = new String[] { null, "LangSysRecord" };
+                if ( isParent ( pn ) ) {
+                    String value = attrs.getValue ( "value" );
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        assert languageTag == null;
+                        languageTag = value;
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "LigCaretList" ) ) {
+                String[] pn = new String[] { null, "GDEF" };
+                if ( ! isParent ( pn ) ) {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Ligature" ) ) {
+                String[] pn = new String[] { null, "LigatureSet" };
+                if ( isParent ( pn ) ) {
+                    String components = attrs.getValue ( "components" );
+                    if ( components == null ) {
+                        missingRequiredAttribute ( en, "components" );
+                    }
+                    int[] cids = mapGlyphIds ( components, en );
+                    String glyph = attrs.getValue ( "glyph" );
+                    if ( glyph == null ) {
+                        missingRequiredAttribute ( en, "glyph" );
+                    }
+                    int gid = mapGlyphId ( glyph, en );
+                    ligatures.add ( new Ligature ( gid, cids ) );
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "LigatureAnchor" ) ) {
+                String[] pn = new String[] { null, "ComponentRecord" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    }
+                    assert xCoord == Integer.MIN_VALUE;
+                    assert yCoord == Integer.MIN_VALUE;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "LigatureArray" ) ) {
+                String[] pn = new String[] { null, "MarkLigPos" };
+                if ( ! isParent ( pn ) ) {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "LigatureAttach" ) ) {
+                String[] pn = new String[] { null, "LigatureArray" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    assert components.size() == 0;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "LigatureCoverage" ) ) {
+                String[] pn = new String[] { null, "MarkLigPos" };
+                if ( isParent ( pn ) ) {
+                    String format = attrs.getValue ( "Format" );
+                    int cf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        cf = Integer.parseInt ( format );
+                        switch ( cf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, cf );
+                            break;
+                        }
+                    }
+                    assertCoverageClear();
+                    ctIndex = 0;
+                    ctFormat = cf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "LigatureSet" ) ) {
+                String[] pn = new String[] { null, "LigatureSubst" };
+                if ( isParent ( pn ) ) {
+                    String glyph = attrs.getValue ( "glyph" );
+                    if ( glyph == null ) {
+                        missingRequiredAttribute ( en, "glyph" );
+                    }
+                    int gid = mapGlyphId ( glyph, en );
+                    coverageEntries.add ( Integer.valueOf ( gid ) );
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "LigatureSubst" ) ) {
+                String[] pn = new String[] { null, "Lookup" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int sf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        sf = Integer.parseInt ( format );
+                        switch ( sf ) {
+                        case 1:
+                            break;
+                        default:
+                            unsupportedFormat ( en, sf );
+                            break;
+                        }
+                    }
+                    assertCoverageClear();
+                    ctIndex = 0;
+                    ctFormat = 1;
+                    assertSubtableClear();
+                    assert sf >= 0;
+                    stFormat = sf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "LookAheadCoverage" ) ) {
+                String[] pn1 = new String[] { null, "ChainContextSubst" };
+                String[] pn2 = new String[] { null, "ChainContextPos" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( isParent ( pnx ) ) {
+                    String index = attrs.getValue ( "index" );
+                    int ci = -1;
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    } else {
+                        ci = Integer.parseInt ( index );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int cf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        cf = Integer.parseInt ( format );
+                        switch ( cf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, cf );
+                            break;
+                        }
+                    }
+                    assertCoverageClear();
+                    ctIndex = ci;
+                    ctFormat = cf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "Lookup" ) ) {
+                String[] pn = new String[] { null, "LookupList" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    int li = -1;
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    } else {
+                        li = Integer.parseInt ( index );
+                    }
+                    assertLookupClear();
+                    assert ltIndex == -1;
+                    ltIndex = li;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "LookupFlag" ) ) {
+                String[] pn = new String[] { null, "Lookup" };
+                if ( isParent ( pn ) ) {
+                    String value = attrs.getValue ( "value" );
+                    int lf = 0;
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        lf = Integer.parseInt ( value );
+                    }
+                    assert ltFlags == 0;
+                    ltFlags = lf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "LookupList" ) ) {
+                String[] pn1 = new String[] { null, "GSUB" };
+                String[] pn2 = new String[] { null, "GPOS" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( ! isParent ( pnx ) ) {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "LookupListIndex" ) ) {
+                String[] pn1 = new String[] { null, "Feature" };
+                String[] pn2 = new String[] { null, "SubstLookupRecord" };
+                String[] pn3 = new String[] { null, "PosLookupRecord" };
+                String[][] pnx = new String[][] { pn1, pn2, pn3 };
+                if ( isParent ( pnx ) ) {
+                    String index = attrs.getValue ( "index" );
+                    String value = attrs.getValue ( "value" );
+                    int v = -1;
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        v = Integer.parseInt ( value );
+                    }
+                    String[][] pny = new String[][] { pn2, pn3 };
+                    if ( isParent ( pny ) ) {
+                        assert rlLookup == -1;
+                        assert v != -1;
+                        rlLookup = v;
+                    } else {
+                        featureLookups.add ( makeLookupId ( v ) );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "LookupType" ) ) {
+                String[] pn = new String[] { null, "Lookup" };
+                if ( isParent ( pn ) ) {
+                    String value = attrs.getValue ( "value" );
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Mark1Array" ) ) {
+                String[] pn = new String[] { null, "MarkMarkPos" };
+                if ( ! isParent ( pn ) ) {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Mark1Coverage" ) ) {
+                String[] pn = new String[] { null, "MarkMarkPos" };
+                if ( isParent ( pn ) ) {
+                    String format = attrs.getValue ( "Format" );
+                    int cf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        cf = Integer.parseInt ( format );
+                        switch ( cf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, cf );
+                            break;
+                        }
+                    }
+                    assertCoverageClear();
+                    ctIndex = 0;
+                    ctFormat = cf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Mark2Anchor" ) ) {
+                String[] pn = new String[] { null, "Mark2Record" };
+                if ( isParent ( pn ) ) {
+                    String format = attrs.getValue ( "Format" );
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    }
+                    assert xCoord == Integer.MIN_VALUE;
+                    assert yCoord == Integer.MIN_VALUE;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Mark2Array" ) ) {
+                String[] pn = new String[] { null, "MarkMarkPos" };
+                if ( ! isParent ( pn ) ) {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Mark2Coverage" ) ) {
+                String[] pn = new String[] { null, "MarkMarkPos" };
+                if ( isParent ( pn ) ) {
+                    String format = attrs.getValue ( "Format" );
+                    int cf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        cf = Integer.parseInt ( format );
+                        switch ( cf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, cf );
+                            break;
+                        }
+                    }
+                    assertCoverageClear();
+                    ctIndex = 0;
+                    ctFormat = cf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Mark2Record" ) ) {
+                String[] pn = new String[] { null, "Mark2Array" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "MarkAnchor" ) ) {
+                String[] pn = new String[] { null, "MarkRecord" };
+                if ( isParent ( pn ) ) {
+                    String format = attrs.getValue ( "Format" );
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    }
+                    assert xCoord == Integer.MIN_VALUE;
+                    assert yCoord == Integer.MIN_VALUE;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "MarkArray" ) ) {
+                String[] pn1 = new String[] { null, "MarkBasePos" };
+                String[] pn2 = new String[] { null, "MarkLigPos" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( ! isParent ( pnx ) ) {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "MarkAttachClassDef" ) ) {
+                String[] pn = new String[] { null, "GDEF" };
+                if ( isParent ( pn ) ) {
+                    String format = attrs.getValue ( "Format" );
+                    int sf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        sf = Integer.parseInt ( format );
+                        switch ( sf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, sf );
+                            break;
+                        }
+                    }
+                    assertSubtableClear();
+                    assert sf >= 0;
+                    // force format 1 since TTX always writes entries as non-range entries
+                    if ( sf != 1 ) {
+                        sf = 1;
+                    }
+                    stFormat = sf;
+                    assert glyphClasses.isEmpty();
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "MarkBasePos" ) ) {
+                String[] pn = new String[] { null, "Lookup" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int sf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        sf = Integer.parseInt ( format );
+                        switch ( sf ) {
+                        case 1:
+                            break;
+                        default:
+                            unsupportedFormat ( en, sf );
+                            break;
+                        }
+                    }
+                    assertSubtableClear();
+                    assert sf >= 0;
+                    stFormat = sf;
+                    assert markAnchors.size() == 0;
+                    assert baseOrMarkAnchors.size() == 0;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "MarkCoverage" ) ) {
+                String[] pn1 = new String[] { null, "MarkBasePos" };
+                String[] pn2 = new String[] { null, "MarkLigPos" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( isParent ( pnx ) ) {
+                    String format = attrs.getValue ( "Format" );
+                    int cf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        cf = Integer.parseInt ( format );
+                        switch ( cf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, cf );
+                            break;
+                        }
+                    }
+                    assertCoverageClear();
+                    ctIndex = 0;
+                    ctFormat = cf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "MarkLigPos" ) ) {
+                String[] pn = new String[] { null, "Lookup" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int sf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        sf = Integer.parseInt ( format );
+                        switch ( sf ) {
+                        case 1:
+                            break;
+                        default:
+                            unsupportedFormat ( en, sf );
+                            break;
+                        }
+                    }
+                    assertSubtableClear();
+                    assert sf >= 0;
+                    stFormat = sf;
+                    assert markAnchors.size() == 0;
+                    assert ligatureAnchors.size() == 0;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "MarkMarkPos" ) ) {
+                String[] pn = new String[] { null, "Lookup" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int sf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        sf = Integer.parseInt ( format );
+                        switch ( sf ) {
+                        case 1:
+                            break;
+                        default:
+                            unsupportedFormat ( en, sf );
+                            break;
+                        }
+                    }
+                    assertSubtableClear();
+                    assert sf >= 0;
+                    stFormat = sf;
+                    assert markAnchors.size() == 0;
+                    assert baseOrMarkAnchors.size() == 0;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "MarkRecord" ) ) {
+                String[] pn1 = new String[] { null, "MarkArray" };
+                String[] pn2 = new String[] { null, "Mark1Array" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( isParent ( pnx ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "MultipleSubst" ) ) {
+                String[] pn = new String[] { null, "Lookup" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int sf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        sf = Integer.parseInt ( format );
+                        switch ( sf ) {
+                        case 1:
+                            break;
+                        default:
+                            unsupportedFormat ( en, sf );
+                            break;
+                        }
+                    }
+                    assertSubtableClear();
+                    assert sf >= 0;
+                    stFormat = sf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "PairPos" ) ) {
+                String[] pn = new String[] { null, "Lookup" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int sf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        sf = Integer.parseInt ( format );
+                        switch ( sf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, sf );
+                            break;
+                        }
+                    }
+                    assertSubtableClear();
+                    assert sf >= 0;
+                    stFormat = sf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "PairSet" ) ) {
+                String[] pn = new String[] { null, "PairPos" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    int psi = -1;
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    } else {
+                        psi = Integer.parseInt ( index );
+                    }
+                    assert psIndex == -1;
+                    psIndex = psi;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "PairValueRecord" ) ) {
+                String[] pn = new String[] { null, "PairSet" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    } else {
+                        assertPairClear();
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "PosLookupRecord" ) ) {
+                String[] pn1 = new String[] { null, "ChainContextSubst" };
+                String[] pn2 = new String[] { null, "ChainContextPos" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( isParent ( pnx ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "ReqFeatureIndex" ) ) {
+                String[] pn1 = new String[] { null, "DefaultLangSys" };
+                String[] pn2 = new String[] { null, "LangSys" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( isParent ( pnx ) ) {
+                    String value = attrs.getValue ( "value" );
+                    int v = -1;
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        v = Integer.parseInt ( value );
+                    }
+                    String fid;
+                    if ( ( v >= 0 ) && ( v < 65535 ) ) {
+                        fid = makeFeatureId ( v );
+                    } else {
+                        fid = null;
+                    }
+                    assertLanguageFeaturesClear();
+                    languageFeatures.add ( fid );
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "Script" ) ) {
+                String[] pn = new String[] { null, "ScriptRecord" };
+                if ( ! isParent ( pn ) ) {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "ScriptList" ) ) {
+                String[] pn1 = new String[] { null, "GSUB" };
+                String[] pn2 = new String[] { null, "GPOS" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( ! isParent ( pnx ) ) {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "ScriptRecord" ) ) {
+                String[] pn = new String[] { null, "ScriptList" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "ScriptTag" ) ) {
+                String[] pn = new String[] { null, "ScriptRecord" };
+                if ( isParent ( pn ) ) {
+                    String value = attrs.getValue ( "value" );
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        assert scriptTag == null;
+                        scriptTag = value;
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "SecondGlyph" ) ) {
+                String[] pn = new String[] { null, "PairValueRecord" };
+                if ( isParent ( pn ) ) {
+                    String value = attrs.getValue ( "value" );
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        int gid = mapGlyphId ( value, en );
+                        assert g2 == -1;
+                        g2 = gid;
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Sequence" ) ) {
+                String[] pn = new String[] { null, "MultipleSubst" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    } else {
+                        int i = Integer.parseInt ( index );
+                        if ( i != subtableEntries.size() ) {
+                            invalidIndex ( en, i, subtableEntries.size() );
+                        }
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "SequenceIndex" ) ) {
+                String[] pn1 = new String[] { null, "PosLookupRecord" };
+                String[] pn2 = new String[] { null, "SubstLookupRecord" };
+                String[][] pnx = new String[][] { pn1, pn2 };
+                if ( isParent ( pnx ) ) {
+                    String value = attrs.getValue ( "value" );
+                    int v = -1;
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        v = Integer.parseInt ( value );
+                    }
+                    assert rlSequence == -1;
+                    assert v != -1;
+                    rlSequence = v;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "SinglePos" ) ) {
+                String[] pn = new String[] { null, "Lookup" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int sf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        sf = Integer.parseInt ( format );
+                        switch ( sf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, sf );
+                            break;
+                        }
+                    }
+                    assertSubtableClear();
+                    assert sf >= 0;
+                    stFormat = sf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "SingleSubst" ) ) {
+                String[] pn = new String[] { null, "Lookup" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                    String format = attrs.getValue ( "Format" );
+                    int sf = -1;
+                    if ( format == null ) {
+                        missingRequiredAttribute ( en, "Format" );
+                    } else {
+                        sf = Integer.parseInt ( format );
+                        switch ( sf ) {
+                        case 1:
+                        case 2:
+                            break;
+                        default:
+                            unsupportedFormat ( en, sf );
+                            break;
+                        }
+                    }
+                    assertCoverageClear();
+                    ctIndex = 0;
+                    ctFormat = 1;
+                    assertSubtableClear();
+                    assert sf >= 0;
+                    stFormat = sf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "SubstLookupRecord" ) ) {
+                String[] pn = new String[] { null, "ChainContextSubst" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Substitute" ) ) {
+                String[] pn = new String[] { null, "Sequence" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( index == null ) {
+                        missingRequiredAttribute ( en, "index" );
+                    } else {
+                        int i = Integer.parseInt ( index );
+                        if ( i != substitutes.size() ) {
+                            invalidIndex ( en, i, substitutes.size() );
+                        }
+                    }
+                    String value = attrs.getValue ( "value" );
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        int gid = mapGlyphId ( value, en );
+                        substitutes.add ( Integer.valueOf ( gid ) );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Substitution" ) ) {
+                String[] pn = new String[] { null, "SingleSubst" };
+                if ( isParent ( pn ) ) {
+                    String in = attrs.getValue ( "in" );
+                    int igid = -1;
+                    int ogid = -1;
+                    if ( in == null ) {
+                        missingRequiredAttribute ( en, "in" );
+                    } else {
+                        igid = mapGlyphId ( in, en );
+                    }
+                    String out = attrs.getValue ( "out" );
+                    if ( out == null ) {
+                        missingRequiredAttribute ( en, "out" );
+                    } else {
+                        ogid = mapGlyphId ( out, en );
+                    }
+                    coverageEntries.add ( Integer.valueOf ( igid ) );
+                    subtableEntries.add ( Integer.valueOf ( ogid ) );
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Value" ) ) {
+                String[] pn = new String[] { null, "SinglePos" };
+                if ( isParent ( pn ) ) {
+                    String index = attrs.getValue ( "index" );
+                    if ( vf1 < 0 ) {
+                        missingParameter ( en, "value format" );
+                    } else {
+                        subtableEntries.add ( parseValue ( en, attrs, vf1 ) );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Value1" ) ) {
+                String[] pn = new String[] { null, "PairValueRecord" };
+                if ( isParent ( pn ) ) {
+                    if ( vf1 < 0 ) {
+                        missingParameter ( en, "value format 1" );
+                    } else {
+                        assert v1 == null;
+                        v1 = parseValue ( en, attrs, vf1 );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Value2" ) ) {
+                String[] pn = new String[] { null, "PairValueRecord" };
+                if ( isParent ( pn ) ) {
+                    if ( vf2 < 0 ) {
+                        missingParameter ( en, "value format 2" );
+                    } else {
+                        assert v2 == null;
+                        v2 = parseValue ( en, attrs, vf2 );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "ValueFormat" ) ) {
+                String[] pn = new String[] { null, "SinglePos" };
+                if ( isParent ( pn ) ) {
+                    String value = attrs.getValue ( "value" );
+                    int vf = -1;
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        vf = Integer.parseInt ( value );
+                    }
+                    assert vf1 == -1;
+                    vf1 = vf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "ValueFormat1" ) ) {
+                String[] pn = new String[] { null, "PairPos" };
+                if ( isParent ( pn ) ) {
+                    String value = attrs.getValue ( "value" );
+                    int vf = -1;
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        vf = Integer.parseInt ( value );
+                    }
+                    assert vf1 == -1;
+                    vf1 = vf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "ValueFormat2" ) ) {
+                String[] pn = new String[] { null, "PairPos" };
+                if ( isParent ( pn ) ) {
+                    String value = attrs.getValue ( "value" );
+                    int vf = -1;
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        vf = Integer.parseInt ( value );
+                    }
+                    assert vf2 == -1;
+                    vf2 = vf;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pn );
+                }
+            } else if ( en[1].equals ( "Version" ) ) {
+                String[] pn1 = new String[] { null, "GDEF" };
+                String[] pn2 = new String[] { null, "GPOS" };
+                String[] pn3 = new String[] { null, "GSUB" };
+                String[][] pnx = new String[][] { pn1, pn2, pn3 };
+                if ( isParent ( pnx ) ) {
+                    String value = attrs.getValue ( "value" );
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    }
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "XCoordinate" ) ) {
+                String[] pn1 = new String[] { null, "BaseAnchor" };
+                String[] pn2 = new String[] { null, "EntryAnchor" };
+                String[] pn3 = new String[] { null, "ExitAnchor" };
+                String[] pn4 = new String[] { null, "LigatureAnchor" };
+                String[] pn5 = new String[] { null, "MarkAnchor" };
+                String[] pn6 = new String[] { null, "Mark2Anchor" };
+                String[][] pnx = new String[][] { pn1, pn2, pn3, pn4, pn5, pn6 };
+                if ( isParent ( pnx ) ) {
+                    String value = attrs.getValue ( "value" );
+                    int x = 0;
+                    if ( value == null ) {
+                        missingRequiredAttribute ( en, "value" );
+                    } else {
+                        x = Integer.parseInt ( value );
+                    }
+                    assert xCoord == Integer.MIN_VALUE;
+                    xCoord = x;
+                } else {
+                    notPermittedInElementContext ( en, getParent(), pnx );
+                }
+            } else if ( en[1].equals ( "YCoordinate" ) ) {
+                String[] pn1 = new String[] { null, "BaseAnchor" };
+                String[] pn2 = new String[] { null, "EntryAnchor" };
+                String[] pn3 = new String[] { null, "ExitAnchor" };
+                String[] pn4 = new String[] { null, "LigatureAnchor" };
+                String[] pn5 = new String[] { null, "MarkAnchor" };
+                String[] pn6 = new String[] { null, "Mark2Anchor" };

[... 1583 lines stripped ...]


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