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/09/30 11:47:17 UTC

svn commit: r1002978 [4/9] - in /xmlgraphics/fop/branches/Temp_ComplexScripts: ./ src/java/org/apache/fop/area/ src/java/org/apache/fop/area/inline/ src/java/org/apache/fop/fo/ src/java/org/apache/fop/fonts/ src/java/org/apache/fop/fonts/apps/ src/java...

Added: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphProcessingState.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphProcessingState.java?rev=1002978&view=auto
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphProcessingState.java (added)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphProcessingState.java Thu Sep 30 09:47:15 2010
@@ -0,0 +1,1089 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts;
+
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+// CSOFF: LineLengthCheck
+// CSOFF: NoWhitespaceAfterCheck
+
+/**
+ * The <code>GlyphProcessingState</code> implements a common, base state object used during glyph substitution
+ * and positioning processing.
+ * @author Glenn Adams
+ */
+
+public class GlyphProcessingState {
+
+    /** governing glyph definition table */
+    protected GlyphDefinitionTable gdef;
+    /** governing script */
+    protected String script;
+    /** governing language */
+    protected String language;
+    /** governing feature */
+    protected String feature;
+    /** current input glyph sequence */
+    protected GlyphSequence igs;
+    /** current index in input sequence */
+    protected int index;
+    /** last (maximum) index of input sequence (exclusive) */
+    protected int indexLast;
+    /** consumed, updated after each successful subtable application */
+    protected int consumed;
+    /** lookup flags */
+    protected int flags;
+    /** class match set */
+    protected int classMatchSet;
+    /** script specific context tester or null */
+    protected ScriptContextTester sct;
+    /** glyph context tester or null */
+    protected GlyphContextTester gct;
+    /** ignore base glyph tester */
+    protected GlyphTester ignoreBase;
+    /** ignore ligature glyph tester */
+    protected GlyphTester ignoreLigature;
+    /** ignore mark glyph tester */
+    protected GlyphTester ignoreMark;
+    /** default ignore glyph tester */
+    protected GlyphTester ignoreDefault;
+
+    /**
+     * Construct glyph processing state.
+     * @param gs input glyph sequence
+     * @param script script identifier
+     * @param language language identifier
+     * @param feature feature identifier
+     * @param sct script context tester (or null)
+     */
+    protected GlyphProcessingState ( GlyphSequence gs, String script, String language, String feature, ScriptContextTester sct ) {
+        this.script = script;
+        this.language = language;
+        this.feature = feature;
+        this.igs = gs;
+        this.indexLast = gs.getGlyphCount();
+        this.sct = sct;
+        this.gct = ( sct != null ) ? sct.getTester ( feature ) : null;
+        this.ignoreBase = new GlyphTester() { public boolean test(int gi) { return isBase(gi); } };
+        this.ignoreLigature = new GlyphTester() { public boolean test(int gi) { return isLigature(gi); } };
+        this.ignoreMark = new GlyphTester() { public boolean test(int gi) { return isMark(gi); } };
+    }
+
+    /**
+     * Construct glyph processing state using an existing state object using shallow copy
+     * except as follows: input glyph sequence is copied deep except for its characters array.
+     * @param s existing processing state to copy from
+     */
+    protected GlyphProcessingState ( GlyphProcessingState s ) {
+        this ( new GlyphSequence ( s.igs ), s.script, s.language, s.feature, s.sct );
+        setPosition ( s.index );
+    }
+
+    /**
+     * Set governing glyph definition table.
+     * @param gdef glyph definition table (or null, to unset)
+     */
+    public void setGDEF ( GlyphDefinitionTable gdef ) {
+        if ( this.gdef == null ) {
+            this.gdef = gdef;
+        } else if ( gdef == null ) {
+            this.gdef = null;
+        }
+    }
+
+    /**
+     * Obtain governing glyph definition table.
+     * @return glyph definition table (or null, to not set)
+     */
+    public GlyphDefinitionTable getGDEF() {
+        return gdef;
+    }
+
+    /**
+     * Set governing lookup flags
+     * @param flags lookup flags (or zero, to unset)
+     */
+    public void setFlags ( int flags ) {
+        if ( this.flags == 0 ) {
+            this.flags = flags;
+        } else if ( flags == 0 ) {
+            this.flags = 0;
+        }
+    }
+
+    /**
+     * Obtain governing lookup  flags.
+     * @return lookup flags (zero may indicate unset or no flags)
+     */
+    public int getFlags() {
+        return flags;
+    }
+
+    /**
+     * Obtain governing class match set.
+     * @param gi glyph index that may be used to determine which match set applies
+     * @return class match set (zero may indicate unset or no set)
+     */
+    public int getClassMatchSet ( int gi ) {
+        return 0;
+    }
+
+    /**
+     * Set default ignore tester.
+     * @param ignoreDefault glyph tester (or null, to unset)
+     */
+    public void setIgnoreDefault ( GlyphTester ignoreDefault ) {
+        if ( this.ignoreDefault == null ) {
+            this.ignoreDefault = ignoreDefault;
+        } else if ( ignoreDefault == null ) {
+            this.ignoreDefault = null;
+        }
+    }
+
+    /**
+     * Obtain governing default ignores tester.
+     * @return default ignores tester
+     */
+    public GlyphTester getIgnoreDefault() {
+        return ignoreDefault;
+    }
+
+    /**
+     * Update glyph subtable specific state. Each time a
+     * different glyph subtable is to be applied, it is used
+     * to update this state prior to application, after which
+     * this state is to be reset.
+     * @param st glyph subtable to use for update
+     */
+    public void updateSubtableState ( GlyphSubtable st ) {
+        setGDEF ( st.getGDEF() );
+        setFlags ( st.getFlags() );
+        setIgnoreDefault ( getIgnoreTester ( flags ) );
+    }
+
+    /**
+     * Reset glyph subtable specific state.
+     */
+    public void resetSubtableState() {
+        setGDEF ( null );
+        setFlags ( 0 );
+        setIgnoreDefault ( null );
+    }
+
+    /**
+     * Obtain current position index in input glyph sequence.
+     * @return current index
+     */
+    public int getPosition() {
+        return index;
+    }
+
+    /**
+     * Set (seek to) position index in input glyph sequence.
+     * @param index to seek to
+     * @throws IndexOutOfBoundsException if index is less than zero
+     * or exceeds last valid position
+     */
+    public void setPosition ( int index ) throws IndexOutOfBoundsException {
+        if ( ( index >= 0 ) && ( index <= indexLast ) ) {
+            this.index =  index;
+        } else {
+            throw new IndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Obtain last valid position index in input glyph sequence.
+     * @return current last index
+     */
+    public int getLastPosition() {
+        return indexLast;
+    }
+
+    /**
+     * Determine if at least one glyph remains in
+     * input sequence.
+     * @return true if one or more glyph remains
+     */
+    public boolean hasNext() {
+        return hasNext ( 1 );
+    }
+
+    /**
+     * Determine if at least <code>count</code> glyphs remain in
+     * input sequence.
+     * @param count of glyphs to test
+     * @return true if at least <code>count</code> glyphs are available
+     */
+    public boolean hasNext ( int count ) {
+        return ( index + count ) <= indexLast;
+    }
+
+    /**
+     * Update the current position index based upon previously consumed
+     * glyphs, i.e., add the consuemd count to the current position index.
+     * If no glyphs were previously consumed, then forces exactly one
+     * glyph to be consumed.
+     * @return the new (updated) position index
+     */
+    public int next() {
+        if ( index < indexLast ) {
+            // force consumption of at least one input glyph
+            if ( consumed == 0 ) {
+                consumed = 1;
+            }
+            index += consumed; consumed = 0;
+            if ( index > indexLast ) {
+                index = indexLast;
+            }
+        }
+        return index;
+    }
+
+    /**
+     * Determine if at least one backtrack (previous) glyph is present
+     * in input sequence.
+     * @return true if one or more glyph remains
+     */
+    public boolean hasPrev() {
+        return hasPrev ( 1 );
+    }
+
+    /**
+     * Determine if at least <code>count</code> backtrack (previous) glyphs
+     * are present in input sequence.
+     * @param count of glyphs to test
+     * @return true if at least <code>count</code> glyphs are available
+     */
+    public boolean hasPrev ( int count ) {
+        return ( index - count ) >= 0;
+    }
+
+    /**
+     * Update the current position index based upon previously consumed
+     * glyphs, i.e., subtract the consuemd count from the current position index.
+     * If no glyphs were previously consumed, then forces exactly one
+     * glyph to be consumed. This method is used to traverse an input
+     * glyph sequence in reverse order.
+     * @return the new (updated) position index
+     */
+    public int prev() {
+        if ( index > 0 ) {
+            // force consumption of at least one input glyph
+            if ( consumed == 0 ) {
+                consumed = 1;
+            }
+            index -= consumed; consumed = 0;
+            if ( index < 0 ) {
+                index = 0;
+            }
+        }
+        return index;
+    }
+
+    /**
+     * Record the consumption of <code>count</code> glyphs such that
+     * this consumption never exceeds the number of glyphs in the input glyph
+     * sequence.
+     * @param count of glyphs to consume
+     * @return newly adjusted consumption count
+     * @throws IndexOutOfBoundsException if count would cause consumption
+     * to exceed count of glyphs in input glyph sequence
+     */
+    public int consume ( int count ) throws IndexOutOfBoundsException {
+        if ( ( consumed + count ) <= indexLast ) {
+            consumed += count;
+            return consumed;
+        } else {
+            throw new IndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Determine if any consumption has occurred.
+     * @return true if consumption count is greater than zero
+     */
+    public boolean didConsume() {
+        return consumed > 0;
+    }
+
+    /**
+     * Obtain reference to input glyph sequence, which must not be modified.
+     * @return input glyph sequence
+     */
+    public GlyphSequence getInput() {
+        return igs;
+    }
+
+    /**
+     * Obtain glyph at specified offset from current position.
+     * @param offset from current position
+     * @return glyph at specified offset from current position
+     * @throws IndexOutOfBoundsException if no glyph available at offset
+     */
+    public int getGlyph ( int offset ) throws IndexOutOfBoundsException {
+        int i = index + offset;
+        if ( ( i >= 0 ) && ( i < indexLast ) ) {
+            return igs.getGlyph ( i );
+        } else {
+            throw new IndexOutOfBoundsException ( "attempting index at " + i );
+        }
+    }
+
+    /**
+     * Obtain glyph at current position.
+     * @return glyph at current position
+     * @throws IndexOutOfBoundsException if no glyph available
+     */
+    public int getGlyph() throws IndexOutOfBoundsException {
+        return getGlyph ( 0 );
+    }
+
+    /**
+     * Set (replace) glyph at specified offset from current position.
+     * @param offset from current position
+     * @param glyph to set at specified offset from current position
+     * @throws IndexOutOfBoundsException if specified offset is not valid position
+     */
+    public void setGlyph ( int offset, int glyph ) throws IndexOutOfBoundsException {
+        int i = index + offset;
+        if ( ( i >= 0 ) && ( i < indexLast ) ) {
+            igs.setGlyph ( i, glyph );
+        } else {
+            throw new IndexOutOfBoundsException ( "attempting index at " + i );
+        }
+    }
+
+    /**
+     * Obtain character association of glyph at specified offset from current position.
+     * @param offset from current position
+     * @return character association of glyph at current position
+     * @throws IndexOutOfBoundsException if offset results in an invalid index into input glyph sequence
+     */
+    public GlyphSequence.CharAssociation getAssociation ( int offset ) throws IndexOutOfBoundsException {
+        int i = index + offset;
+        if ( ( i >= 0 ) && ( i < indexLast ) ) {
+            return igs.getAssociation ( i );
+        } else {
+            throw new IndexOutOfBoundsException ( "attempting index at " + i );
+        }
+    }
+
+    /**
+     * Obtain character association of glyph at current position.
+     * @return character association of glyph at current position
+     * @throws IndexOutOfBoundsException if no glyph available
+     */
+    public GlyphSequence.CharAssociation getAssociation() throws IndexOutOfBoundsException {
+        return getAssociation ( 0 );
+    }
+
+    /**
+     * Obtain <code>count</code> glyphs starting at specified offset from current position. If
+     * <code>reverseOrder</code> is true, then glyphs are returned in reverse order starting at specified offset
+     * and going in reverse towards beginning of input glyph sequence.
+     * @param offset from current position
+     * @param count number of glyphs to obtain
+     * @param reverseOrder true if to obtain in reverse order
+     * @param ignoreTester glyph tester to use to determine which glyphs are ignored (or null, in which case none are ignored)
+     * @param glyphs array to use to fetch glyphs
+     * @param counts int[2] array to receive fetched glyph counts, where counts[0] will
+     * receive the number of glyphs obtained, and counts[1] will receive the number of glyphs
+     * ignored
+     * @return array of glyphs
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public int[] getGlyphs ( int offset, int count, boolean reverseOrder, GlyphTester ignoreTester, int[] glyphs, int[] counts ) throws IndexOutOfBoundsException {
+        if ( count < 0 ) {
+            count = getGlyphsAvailable ( offset, reverseOrder, ignoreTester ) [ 0 ];
+        }
+        int start = index + offset;
+        if ( start < 0 ) {
+            throw new IndexOutOfBoundsException ( "will attempt index at " + start );
+        } else if ( ! reverseOrder && ( ( start + count ) > indexLast ) ) {
+            throw new IndexOutOfBoundsException ( "will attempt index at " + ( start + count ) );
+        } else if ( reverseOrder && ( ( start + 1 ) < count ) ) {
+            throw new IndexOutOfBoundsException ( "will attempt index at " + ( start - count ) );
+        }
+        if ( glyphs == null ) {
+            glyphs = new int [ count ];
+        } else if ( glyphs.length != count ) {
+            throw new IllegalArgumentException ( "glyphs array is non-null, but its length (" + glyphs.length + "), is not equal to count (" + count + ")" );
+        }
+        if ( ! reverseOrder ) {
+            return getGlyphsForward ( start, count, ignoreTester, glyphs, counts );
+        } else {
+            return getGlyphsReverse ( start, count, ignoreTester, glyphs, counts );
+        }
+    }
+
+    private int[] getGlyphsForward ( int start, int count, GlyphTester ignoreTester, int[] glyphs, int[] counts ) throws IndexOutOfBoundsException {
+        int counted = 0;
+        int ignored = 0;
+        for ( int i = start, n = indexLast, k = 0; i < n; i++ ) {
+            int gi = getGlyph ( i - index );
+            if ( gi == 65535 ) {
+                ignored++;
+            } else {
+                if ( ( ignoreTester == null ) || ! ignoreTester.test ( gi ) ) {
+                    if ( k < count ) {
+                        glyphs [ k++ ] = gi; counted++;
+                    } else {
+                        break;
+                    }
+                } else {
+                    ignored++;
+                }
+            }
+        }
+        if ( ( counts != null ) && ( counts.length > 1 ) ) {
+            counts[0] = counted;
+            counts[1] = ignored;
+        }
+        return glyphs;
+    }
+
+    private int[] getGlyphsReverse ( int start, int count, GlyphTester ignoreTester, int[] glyphs, int[] counts ) throws IndexOutOfBoundsException {
+        int counted = 0;
+        int ignored = 0;
+        for ( int i = start, k = 0; i >= 0; i-- ) {
+            int gi = getGlyph ( i - index );
+            if ( gi == 65535 ) {
+                ignored++;
+            } else {
+                if ( ( ignoreTester == null ) || ! ignoreTester.test ( gi ) ) {
+                    if ( k < count ) {
+                        glyphs [ k++ ] = gi; counted++;
+                    } else {
+                        break;
+                    }
+                } else {
+                    ignored++;
+                }
+            }
+        }
+        if ( ( counts != null ) && ( counts.length > 1 ) ) {
+            counts[0] = counted;
+            counts[1] = ignored;
+        }
+        return glyphs;
+    }
+
+    /**
+     * Obtain <code>count</code> glyphs starting at specified offset from current position. If
+     * offset is negative, then glyphs are returned in reverse order starting at specified offset
+     * and going in reverse towards beginning of input glyph sequence.
+     * @param offset from current position
+     * @param count number of glyphs to obtain
+     * @param glyphs array to use to fetch glyphs
+     * @param counts int[2] array to receive fetched glyph counts, where counts[0] will
+     * receive the number of glyphs obtained, and counts[1] will receive the number of glyphs
+     * ignored
+     * @return array of glyphs
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public int[] getGlyphs ( int offset, int count, int[] glyphs, int[] counts ) throws IndexOutOfBoundsException {
+        return getGlyphs ( offset, count, offset < 0, ignoreDefault, glyphs, counts );
+    }
+
+    /**
+     * Obtain all glyphs starting from current position to end of input glyph sequence.
+     * @return array of available glyphs
+     * @throws IndexOutOfBoundsException if no glyph available
+     */
+    public int[] getGlyphs() throws IndexOutOfBoundsException {
+        return getGlyphs ( 0, indexLast - index, false, null, null, null );
+    }
+
+    /**
+     * Obtain <code>count</code> ignored glyphs starting at specified offset from current position. If
+     * <code>reverseOrder</code> is true, then glyphs are returned in reverse order starting at specified offset
+     * and going in reverse towards beginning of input glyph sequence.
+     * @param offset from current position
+     * @param count number of glyphs to obtain
+     * @param reverseOrder true if to obtain in reverse order
+     * @param ignoreTester glyph tester to use to determine which glyphs are ignored (or null, in which case none are ignored)
+     * @param glyphs array to use to fetch glyphs
+     * @param counts int[2] array to receive fetched glyph counts, where counts[0] will
+     * receive the number of glyphs obtained, and counts[1] will receive the number of glyphs
+     * ignored
+     * @return array of glyphs
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public int[] getIgnoredGlyphs ( int offset, int count, boolean reverseOrder, GlyphTester ignoreTester, int[] glyphs, int[] counts ) throws IndexOutOfBoundsException {
+        return getGlyphs ( offset, count, reverseOrder, new NotGlyphTester ( ignoreTester ), glyphs, counts );
+    }
+
+    /**
+     * Obtain <code>count</code> ignored glyphs starting at specified offset from current position. If <code>offset</code> is
+     * negative, then fetch in reverse order.
+     * @param offset from current position
+     * @param count number of glyphs to obtain
+     * @return array of glyphs
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public int[] getIgnoredGlyphs ( int offset, int count ) throws IndexOutOfBoundsException {
+        return getIgnoredGlyphs ( offset, count, offset < 0, ignoreDefault, null, null );
+    }
+
+    /**
+     * Determine number of glyphs available starting at specified offset from current position. If
+     * <code>reverseOrder</code> is true, then search backwards in input glyph sequence.
+     * @param offset from current position
+     * @param reverseOrder true if to obtain in reverse order
+     * @param ignoreTester glyph tester to use to determine which glyphs to count (or null, in which case none are ignored)
+     * @return an int[2] array where counts[0] is the number of glyphs available, and counts[1] is the number of glyphs ignored
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public int[] getGlyphsAvailable ( int offset, boolean reverseOrder, GlyphTester ignoreTester ) throws IndexOutOfBoundsException {
+        int start = index + offset;
+        if ( ( start < 0 ) || ( start > indexLast ) ) {
+            return new int[] { 0, 0 };
+        } else if ( ! reverseOrder ) {
+            return getGlyphsAvailableForward ( start, ignoreTester );
+        } else {
+            return getGlyphsAvailableReverse ( start, ignoreTester );
+        }
+    }
+
+    private int[] getGlyphsAvailableForward ( int start, GlyphTester ignoreTester ) throws IndexOutOfBoundsException {
+        int counted = 0;
+        int ignored = 0;
+        if ( ignoreTester == null ) {
+            counted = indexLast - start;
+        } else {
+            for ( int i = start, n = indexLast; i < n; i++ ) {
+                int gi = getGlyph ( i - index );
+                if ( gi == 65535 ) {
+                    ignored++;
+                } else {
+                    if ( ignoreTester.test ( gi ) ) {
+                        ignored++;
+                    } else {
+                        counted++;
+                    }
+                }
+            }
+        }
+        return new int[] { counted, ignored };
+    }
+
+    private int[] getGlyphsAvailableReverse ( int start, GlyphTester ignoreTester ) throws IndexOutOfBoundsException {
+        int counted = 0;
+        int ignored = 0;
+        if ( ignoreTester == null ) {
+            counted = start + 1;
+        } else {
+            for ( int i = start; i >= 0; i-- ) {
+                int gi = getGlyph ( i - index );
+                if ( gi == 65535 ) {
+                    ignored++;
+                } else {
+                    if ( ignoreTester.test ( gi ) ) {
+                        ignored++;
+                    } else {
+                        counted++;
+                    }
+                }
+            }
+        }
+        return new int[] { counted, ignored };
+    }
+
+    /**
+     * Determine number of glyphs available starting at specified offset from current position. If
+     * <code>reverseOrder</code> is true, then search backwards in input glyph sequence. Uses the
+     * default ignores tester.
+     * @param offset from current position
+     * @param reverseOrder true if to obtain in reverse order
+     * @return an int[2] array where counts[0] is the number of glyphs available, and counts[1] is the number of glyphs ignored
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public int[] getGlyphsAvailable ( int offset, boolean reverseOrder ) throws IndexOutOfBoundsException {
+        return getGlyphsAvailable ( offset, reverseOrder, ignoreDefault );
+    }
+
+    /**
+     * Determine number of glyphs available starting at specified offset from current position. If
+     * offset is negative, then search backwards in input glyph sequence. Uses the
+     * default ignores tester.
+     * @param offset from current position
+     * @return an int[2] array where counts[0] is the number of glyphs available, and counts[1] is the number of glyphs ignored
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public int[] getGlyphsAvailable ( int offset ) throws IndexOutOfBoundsException {
+        return getGlyphsAvailable ( offset, offset < 0 );
+    }
+
+    /**
+     * Obtain <code>count</code> character associations of glyphs starting at specified offset from current position. If
+     * <code>reverseOrder</code> is true, then associations are returned in reverse order starting at specified offset
+     * and going in reverse towards beginning of input glyph sequence.
+     * @param offset from current position
+     * @param count number of associations to obtain
+     * @param reverseOrder true if to obtain in reverse order
+     * @param ignoreTester glyph tester to use to determine which glyphs are ignored (or null, in which case none are ignored)
+     * @param associations array to use to fetch associations
+     * @param counts int[2] array to receive fetched association counts, where counts[0] will
+     * receive the number of associations obtained, and counts[1] will receive the number of glyphs whose
+     * associations were ignored
+     * @return array of associations
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public GlyphSequence.CharAssociation[] getAssociations ( int offset, int count, boolean reverseOrder, GlyphTester ignoreTester, GlyphSequence.CharAssociation[] associations, int[] counts )
+        throws IndexOutOfBoundsException {
+        if ( count < 0 ) {
+            count = getGlyphsAvailable ( offset, reverseOrder, ignoreTester ) [ 0 ];
+        }
+        int start = index + offset;
+        if ( start < 0 ) {
+            throw new IndexOutOfBoundsException ( "will attempt index at " + start );
+        } else if ( ! reverseOrder && ( ( start + count ) > indexLast ) ) {
+            throw new IndexOutOfBoundsException ( "will attempt index at " + ( start + count ) );
+        } else if ( reverseOrder && ( ( start + 1 ) < count ) ) {
+            throw new IndexOutOfBoundsException ( "will attempt index at " + ( start - count ) );
+        }
+        if ( associations == null ) {
+            associations = new GlyphSequence.CharAssociation [ count ];
+        } else if ( associations.length != count ) {
+            throw new IllegalArgumentException ( "associations array is non-null, but its length (" + associations.length + "), is not equal to count (" + count + ")" );
+        }
+        if ( ! reverseOrder ) {
+            return getAssociationsForward ( start, count, ignoreTester, associations, counts );
+        } else {
+            return getAssociationsReverse ( start, count, ignoreTester, associations, counts );
+        }
+    }
+
+    private GlyphSequence.CharAssociation[] getAssociationsForward ( int start, int count, GlyphTester ignoreTester, GlyphSequence.CharAssociation[] associations, int[] counts )
+        throws IndexOutOfBoundsException {
+        int counted = 0;
+        int ignored = 0;
+        for ( int i = start, n = indexLast, k = 0; i < n; i++ ) {
+            int gi = getGlyph ( i - index );
+            if ( gi == 65535 ) {
+                ignored++;
+            } else {
+                if ( ( ignoreTester == null ) || ! ignoreTester.test ( gi ) ) {
+                    if ( k < count ) {
+                        associations [ k++ ] = getAssociation ( i - index ); counted++;
+                    } else {
+                        break;
+                    }
+                } else {
+                    ignored++;
+                }
+            }
+        }
+        if ( ( counts != null ) && ( counts.length > 1 ) ) {
+            counts[0] = counted;
+            counts[1] = ignored;
+        }
+        return associations;
+    }
+
+    private GlyphSequence.CharAssociation[] getAssociationsReverse ( int start, int count, GlyphTester ignoreTester, GlyphSequence.CharAssociation[] associations, int[] counts )
+        throws IndexOutOfBoundsException {
+        int counted = 0;
+        int ignored = 0;
+        for ( int i = start, k = 0; i >= 0; i-- ) {
+            int gi = getGlyph ( i - index );
+            if ( gi == 65535 ) {
+                ignored++;
+            } else {
+                if ( ( ignoreTester == null ) || ! ignoreTester.test ( gi ) ) {
+                    if ( k < count ) {
+                        associations [ k++ ] = getAssociation ( i - index ); counted++;
+                    } else {
+                        break;
+                    }
+                } else {
+                    ignored++;
+                }
+            }
+        }
+        if ( ( counts != null ) && ( counts.length > 1 ) ) {
+            counts[0] = counted;
+            counts[1] = ignored;
+        }
+        return associations;
+    }
+
+    /**
+     * Obtain <code>count</code> character associations of glyphs starting at specified offset from current position. If
+     * offset is negative, then search backwards in input glyph sequence. Uses the
+     * default ignores tester.
+     * @param offset from current position
+     * @param count number of associations to obtain
+     * @return array of associations
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public GlyphSequence.CharAssociation[] getAssociations ( int offset, int count ) throws IndexOutOfBoundsException {
+        return getAssociations ( offset, count, offset < 0, ignoreDefault, null, null );
+    }
+
+    /**
+     * Obtain <code>count</code> character associations of ignored glyphs starting at specified offset from current position. If
+     * <code>reverseOrder</code> is true, then glyphs are returned in reverse order starting at specified offset
+     * and going in reverse towards beginning of input glyph sequence.
+     * @param offset from current position
+     * @param count number of character associations to obtain
+     * @param reverseOrder true if to obtain in reverse order
+     * @param ignoreTester glyph tester to use to determine which glyphs are ignored (or null, in which case none are ignored)
+     * @param associations array to use to fetch associations
+     * @param counts int[2] array to receive fetched association counts, where counts[0] will
+     * receive the number of associations obtained, and counts[1] will receive the number of glyphs whose
+     * associations were ignored
+     * @return array of associations
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public GlyphSequence.CharAssociation[] getIgnoredAssociations ( int offset, int count, boolean reverseOrder, GlyphTester ignoreTester, GlyphSequence.CharAssociation[] associations, int[] counts )
+        throws IndexOutOfBoundsException {
+        return getAssociations ( offset, count, reverseOrder, new NotGlyphTester ( ignoreTester ), associations, counts );
+    }
+
+    /**
+     * Obtain <code>count</code> character associations of ignored glyphs starting at specified offset from current position. If
+     * offset is negative, then search backwards in input glyph sequence. Uses the
+     * default ignores tester.
+     * @param offset from current position
+     * @param count number of character associations to obtain
+     * @return array of associations
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public GlyphSequence.CharAssociation[] getIgnoredAssociations ( int offset, int count ) throws IndexOutOfBoundsException {
+        return getIgnoredAssociations ( offset, count, offset < 0, ignoreDefault, null, null );
+    }
+
+    /**
+     * Replace subsequence of input glyph sequence starting at specified offset from current position and of
+     * length <code>count</code> glyphs with a subsequence of the sequence <code>gs</code> starting from the specified
+     * offset <code>gsOffset</code> of length <code>gsCount</code> glyphs.
+     * @param offset from current position
+     * @param count number of glyphs to replace, which, if negative means all glyphs from offset to end of input sequence
+     * @param gs glyph sequence from which to obtain replacement glyphs
+     * @param gsOffset offset of first glyph in replacement sequence
+     * @param gsCount count of glyphs in replacement sequence starting at <code>gsOffset</code>
+     * @return true if replacement occurred, or false if replacement would result in no change to input glyph sequence
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public boolean replaceInput ( int offset, int count, GlyphSequence gs, int gsOffset, int gsCount ) throws IndexOutOfBoundsException {
+        int nig = ( igs != null ) ? igs.getGlyphCount() : 0;
+        int position = getPosition() + offset;
+        if ( position < 0 ) {
+            position = 0;
+        } else if ( position > nig ) {
+            position = nig;
+        }
+        if ( ( count < 0 ) || ( ( position + count ) > nig ) ) {
+            count = nig - position;
+        }
+        int nrg = ( gs != null ) ? gs.getGlyphCount() : 0;
+        if ( gsOffset < 0 ) {
+            gsOffset = 0;
+        } else if ( gsOffset > nrg ) {
+            gsOffset = nrg;
+        }
+        if ( ( gsCount < 0 ) || ( ( gsOffset + gsCount ) > nrg ) ) {
+            gsCount = nrg - gsOffset;
+        }
+        int ng = nig + gsCount - count;
+        IntBuffer gb = IntBuffer.allocate ( ng );
+        List al = new ArrayList ( ng );
+        for ( int i = 0, n = position; i < n; i++ ) {
+            gb.put ( igs.getGlyph ( i ) );
+            al.add ( igs.getAssociation ( i ) );
+        }
+        for ( int i = gsOffset, n = gsOffset + gsCount; i < n; i++ ) {
+            gb.put ( gs.getGlyph ( i ) );
+            al.add ( gs.getAssociation ( i ) );
+        }
+        for ( int i = position + count, n = nig; i < n; i++ ) {
+            gb.put ( igs.getGlyph ( i ) );
+            al.add ( igs.getAssociation ( i ) );
+        }
+        gb.flip();
+        if ( igs.compareGlyphs ( gb ) != 0 ) {
+            this.igs = new GlyphSequence ( igs.getCharacters(), gb, al );
+            this.indexLast = gb.limit();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Replace subsequence of input glyph sequence starting at specified offset from current position and of
+     * length <code>count</code> glyphs with all glyphs in the replacement sequence <code>gs</code>.
+     * @param offset from current position
+     * @param count number of glyphs to replace, which, if negative means all glyphs from offset to end of input sequence
+     * @param gs glyph sequence from which to obtain replacement glyphs
+     * @return true if replacement occurred, or false if replacement would result in no change to input glyph sequence
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public boolean replaceInput ( int offset, int count, GlyphSequence gs ) throws IndexOutOfBoundsException {
+        return replaceInput ( offset, count, gs, 0, gs.getGlyphCount() );
+    }
+
+    /**
+     * Erase glyphs in input glyph sequence starting at specified offset from current position, where each glyph
+     * in the specified <code>glyphs</code> array is matched, one at a time, and when a (forward searching) match is found
+     * in the input glyph sequence, the matching glyph is replaced with the glyph index 65535.
+     * @param offset from current position
+     * @param glyphs array of glyphs to erase
+     * @return the number of glyphs erased, which may be less than the number of specified glyphs
+     * @throws IndexOutOfBoundsException if offset or count results in an
+     * invalid index into input glyph sequence
+     */
+    public int erase ( int offset, int[] glyphs ) throws IndexOutOfBoundsException {
+        int start = index + offset;
+        if ( ( start < 0 ) || ( start > indexLast ) ) {
+            throw new IndexOutOfBoundsException ( "will attempt index at " + start );
+        } else {
+            int erased = 0;
+            for ( int i = start - index, n = indexLast - start; i < n; i++ ) {
+                int gi = getGlyph ( i );
+                if ( gi == glyphs [ erased ] ) {
+                    setGlyph ( i, 65535 );
+                    erased++;
+                }
+            }
+            return erased;
+        }
+    }
+
+    /**
+     * Determine if is possible that the current input sequence satisfies a script specific
+     * context testing predicate. If no predicate applies, then application is always possible.
+     * @return true if no script specific context tester applies or if a specified tester returns
+     * true for the current input sequence context
+     */
+    public boolean maybeApplicable() {
+        if ( gct == null ) {
+            return true;
+        } else {
+            return gct.test ( igs, index );
+        }
+    }
+
+    /**
+     * Apply default application semantices; namely, consume one glyph.
+     */
+    public void applyDefault() {
+        consumed += 1;
+    }
+
+    /**
+     * Determine if specified glyph is a base glyph according to the governing
+     * glyph definition table.
+     * @param gi glyph index to test
+     * @return true if glyph definition table records glyph as a base glyph; otherwise, false
+     */
+    public boolean isBase ( int gi ) {
+        if ( gdef != null ) {
+            return gdef.isGlyphClass ( gi, GlyphDefinitionTable.GLYPH_CLASS_BASE );
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Determine if specified glyph is a ligature glyph according to the governing
+     * glyph definition table.
+     * @param gi glyph index to test
+     * @return true if glyph definition table records glyph as a ligature glyph; otherwise, false
+     */
+    public boolean isLigature ( int gi ) {
+        if ( gdef != null ) {
+            return gdef.isGlyphClass ( gi, GlyphDefinitionTable.GLYPH_CLASS_LIGATURE );
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Determine if specified glyph is a mark glyph according to the governing
+     * glyph definition table.
+     * @param gi glyph index to test
+     * @return true if glyph definition table records glyph as a mark glyph; otherwise, false
+     */
+    public boolean isMark ( int gi ) {
+        if ( gdef != null ) {
+            return gdef.isGlyphClass ( gi, GlyphDefinitionTable.GLYPH_CLASS_MARK );
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Obtain an ignored glyph tester that corresponds to the specified lookup flags.
+     * @param flags lookup flags
+     * @return a glyph tester
+     */
+    public GlyphTester getIgnoreTester ( int flags ) {
+        if ( ( flags & GlyphSubtable.LF_IGNORE_BASE ) != 0 ) {
+            if ( ( flags & (GlyphSubtable.LF_IGNORE_LIGATURE | GlyphSubtable.LF_IGNORE_MARK) ) == 0 ) {
+                return ignoreBase;
+            } else {
+                return getCombinedIgnoreTester ( flags );
+            }
+        }
+        if ( ( flags & GlyphSubtable.LF_IGNORE_LIGATURE ) != 0 ) {
+            if ( ( flags & (GlyphSubtable.LF_IGNORE_BASE | GlyphSubtable.LF_IGNORE_MARK) ) == 0 ) {
+                return ignoreLigature;
+            } else {
+                return getCombinedIgnoreTester ( flags );
+            }
+        }
+        if ( ( flags & GlyphSubtable.LF_IGNORE_MARK ) != 0 ) {
+            if ( ( flags & (GlyphSubtable.LF_IGNORE_BASE | GlyphSubtable.LF_IGNORE_LIGATURE) ) == 0 ) {
+                return ignoreMark;
+            } else {
+                return getCombinedIgnoreTester ( flags );
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Obtain an ignored glyph tester that corresponds to the specified multiple (combined) lookup flags.
+     * @param flags lookup flags
+     * @return a glyph tester
+     */
+    public GlyphTester getCombinedIgnoreTester ( int flags ) {
+        GlyphTester[] gta = new GlyphTester [ 3 ];
+        int ngt = 0;
+        if ( ( flags & GlyphSubtable.LF_IGNORE_BASE ) != 0 ) {
+            gta [ ngt++ ] = ignoreBase;
+        }
+        if ( ( flags & GlyphSubtable.LF_IGNORE_LIGATURE ) != 0 ) {
+            gta [ ngt++ ] = ignoreLigature;
+        }
+        if ( ( flags & GlyphSubtable.LF_IGNORE_MARK ) != 0 ) {
+            gta [ ngt++ ] = ignoreMark;
+        }
+        return getCombinedOrTester ( gta, ngt );
+    }
+
+    /**
+     * Obtain an combined OR glyph tester.
+     * @param gta an array of glyph testers
+     * @param ngt number of glyph testers present in specified array
+     * @return a combined OR glyph tester
+     */
+    public GlyphTester getCombinedOrTester ( GlyphTester[] gta, int ngt ) {
+        if ( ngt > 0 ) {
+            return new CombinedOrGlyphTester ( gta, ngt );
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Obtain an combined AND glyph tester.
+     * @param gta an array of glyph testers
+     * @param ngt number of glyph testers present in specified array
+     * @return a combined AND glyph tester
+     */
+    public GlyphTester getCombinedAndTester ( GlyphTester[] gta, int ngt ) {
+        if ( ngt > 0 ) {
+            return new CombinedAndGlyphTester ( gta, ngt );
+        } else {
+            return null;
+        }
+    }
+
+    /** combined OR glyph tester */
+    private static class CombinedOrGlyphTester implements GlyphTester {
+        private GlyphTester[] gta;
+        private int ngt;
+        CombinedOrGlyphTester ( GlyphTester[] gta, int ngt ) {
+            this.gta = gta;
+            this.ngt = ngt;
+        }
+        /** {@inheritDoc} */
+        public boolean test ( int gi ) {
+            for ( int i = 0, n = ngt; i < n; i++ ) {
+                GlyphTester gt = gta [ i ];
+                if ( gt != null ) {
+                    if ( gt.test ( gi ) ) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    /** combined AND glyph tester */
+    private static class CombinedAndGlyphTester implements GlyphTester {
+        private GlyphTester[] gta;
+        private int ngt;
+        CombinedAndGlyphTester ( GlyphTester[] gta, int ngt ) {
+            this.gta = gta;
+            this.ngt = ngt;
+        }
+        /** {@inheritDoc} */
+        public boolean test ( int gi ) {
+            for ( int i = 0, n = ngt; i < n; i++ ) {
+                GlyphTester gt = gta [ i ];
+                if ( gt != null ) {
+                    if ( ! gt.test ( gi ) ) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+    }
+
+    /** NOT glyph tester */
+    private static class NotGlyphTester implements GlyphTester {
+        private GlyphTester gt;
+        NotGlyphTester ( GlyphTester gt ) {
+            this.gt = gt;
+        }
+        /** {@inheritDoc} */
+        public boolean test ( int gi ) {
+            if ( gt != null ) {
+                if ( gt.test ( gi ) ) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+}

Propchange: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphProcessingState.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphProcessingState.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphSequence.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphSequence.java?rev=1002978&r1=1002977&r2=1002978&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphSequence.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphSequence.java Thu Sep 30 09:47:15 2010
@@ -19,15 +19,16 @@
 
 package org.apache.fop.fonts;
 
-import java.nio.CharBuffer;
+import java.nio.IntBuffer;
 
-import java.util.Arrays;
-import java.util.Iterator;
+import java.util.ArrayList;
 import java.util.List;
-import java.util.ListIterator;
 
-// CSOFF: NoWhitespaceAfterCheck
+import org.apache.fop.util.CharUtilities;
+
+// CSOFF: InnerAssignmentCheck
 // CSOFF: LineLengthCheck
+// CSOFF: NoWhitespaceAfterCheck
 
 /**
  * A GlyphSequence encapsulates a sequence of character codes, a sequence of glyph codes,
@@ -37,197 +38,473 @@ import java.util.ListIterator;
  * character code sequence with which the glyph codes are associated.
  * @author Glenn Adams
  */
-public class GlyphSequence implements CharSequence {
+public class GlyphSequence implements Cloneable {
 
-    private CharSequence characters;
-    private CharSequence glyphs;
-    private CharAssociation[] associations;
+    /** default character buffer capacity in case new character buffer is created */
+    private static final int DEFAULT_CHARS_CAPACITY = 8;
+
+    /** character buffer */
+    private IntBuffer characters;
+    /** glyph buffer */
+    private IntBuffer glyphs;
+    /** association list */
+    private List associations;
 
     /**
-     * Instantiate a glyph sequence.
-     * @param characters a (possibly empty) sequence of associated (originating) characters
-     * @param sequences a (possibly empty) list of glyph sequences
-     * @param associations a (possibly empty) list of glyph to character associations, one for each glyph in the concatenated glyph sequences
-     * @param reverse a boolean indicating if the glyphs are in reverse order with respect to the nominal inline progression direction
+     * Instantiate a glyph sequence, reusing (i.e., not copying) the referenced
+     * character and glyph buffers and associations. If characters is null, then
+     * an empty character buffer is created. If glyphs is null, then a glyph buffer
+     * is created whose capacity is that of the character buffer. If associations is
+     * null, then identity associations are created.
+     * @param characters a (possibly null) buffer of associated (originating) characters
+     * @param glyphs a (possibly null) buffer of glyphs
+     * @param associations a (possibly null) array of glyph to character associations
      */
-    public GlyphSequence ( CharSequence characters, List/*<GlyphSequence>*/ sequences, List/*<CharAssociation>*/ associations, boolean reverse ) {
-        this ( characters, concatenateSequences ( sequences, reverse ), concatenateAssociations ( associations, reverse ) );
+    public GlyphSequence ( IntBuffer characters, IntBuffer glyphs, List associations ) {
+        if ( characters == null ) {
+            characters = IntBuffer.allocate ( DEFAULT_CHARS_CAPACITY );
+        }
+        if ( glyphs == null ) {
+            glyphs = IntBuffer.allocate ( characters.capacity() );
+        }
+        if ( associations == null ) {
+            associations = makeIdentityAssociations ( characters.limit(), glyphs.limit() );
+        }
+        this.characters = characters;
+        this.glyphs = glyphs;
+        this.associations = associations;
     }
 
     /**
-     * Instantiate a glyph sequence.
-     * @param characters a (possibly empty) sequence of associated (originating) characters
-     * @param glyphs a (possibly empty) list of glyphs
-     * @param associations a (possibly empty) list of glyph to character associations, one for each glyph in the concatenated glyph sequences
+     * Instantiate a glyph sequence using an existing glyph sequence, where the new glyph sequence shares
+     * the character array of the existing sequence (but not the buffer object), and creates new copies
+     * of glyphs buffer and association list.
+     * @param gs an existing glyph sequence
      */
-    public GlyphSequence ( CharSequence characters, CharSequence glyphs, CharAssociation[] associations ) {
-        if ( ( characters == null ) || ( glyphs == null ) ) {
-            throw new IllegalArgumentException ( "characters and glyphs must be non-null" );
-        } else if ( ( associations != null ) && ( associations.length != glyphs.length() ) ) {
-            throw new IllegalArgumentException ( "number of associations must match number of glyphs" );
+    public GlyphSequence ( GlyphSequence gs ) {
+        this ( gs.characters.duplicate(), copyBuffer ( gs.glyphs ), copyAssociations ( gs.associations ) );
+    }
+
+    /**
+     * Instantiate a glyph sequence using an existing glyph sequence, where the new glyph sequence shares
+     * the character array of the existing sequence (but not the buffer object), but uses the specified
+     * backtrack, input, and lookahead glyph arrays to populate the glyphs, and uses the specified
+     * of glyphs buffer and association list.
+     * backtrack, input, and lookahead association arrays to populate the associations.
+     * @param gs an existing glyph sequence
+     * @param bga backtrack glyph array
+     * @param iga input glyph array
+     * @param lga lookahead glyph array
+     * @param bal backtrack association list
+     * @param ial input association list
+     * @param lal lookahead association list
+     */
+    public GlyphSequence ( GlyphSequence gs, int[] bga, int[] iga, int[] lga, CharAssociation[] bal, CharAssociation[] ial, CharAssociation[] lal ) {
+        this ( gs.characters.duplicate(), concatGlyphs ( bga, iga, lga ), concatAssociations ( bal, ial, lal ) );
+    }
+
+    /**
+     * Obtain reference to underlying character buffer.
+     * @return character buffer reference
+     */
+    public IntBuffer getCharacters() {
+        return characters;
+    }
+
+    /**
+     * Obtain array of characters. If <code>copy</code> is true, then
+     * a newly instantiated array is returned, otherwise a reference to
+     * the underlying buffer's array is returned. N.B. in case a reference
+     * to the undelying buffer's array is returned, the length
+     * of the array is not necessarily the number of characters in array.
+     * To determine the number of characters, use {@link #getCharacterCount}.
+     * @param copy true if to return a newly instantiated array of characters
+     * @return array of characters
+     */
+    public int[] getCharacterArray ( boolean copy ) {
+        if ( copy ) {
+            return toArray ( characters );
         } else {
-            this.characters = characters;
-            this.glyphs = glyphs;
-            if ( associations == null ) {
-                associations = makeIdentityAssociations ( characters, glyphs );
-            }
-            this.associations = associations;
+            return characters.array();
         }
     }
 
-    /** @return sequence of corresponding (originating) characters */
-    public CharSequence getCharacters() {
-        return characters;
+    /**
+     * Obtain the number of characters in character array, where
+     * each character constitutes a unicode scalar value.
+     * @return number of characters available in character array
+     */
+    public int getCharacterCount() {
+        return characters.limit();
     }
 
-    /** @return sequence of glyphs in glyph sequence */
-    public CharSequence getGlyphs() {
-        return glyphs;
+    /**
+     * Obtain glyph id at specified index.
+     * @param index to obtain glyph
+     * @return the glyph identifier of glyph at specified index
+     * @throws IndexOutOfBoundsException if index is less than zero
+     * or exceeds last valid position
+     */
+    public int getGlyph ( int index ) throws IndexOutOfBoundsException {
+        return glyphs.get ( index );
     }
 
-    /** @return glyph to character associations, one for each glyph */
-    public CharAssociation[] getAssociations() {
-        return associations;
+    /**
+     * Set glyph id at specified index.
+     * @param index to set glyph
+     * @param gi glyph index
+     * @throws IndexOutOfBoundsException if index is greater or equal to
+     * the limit of the underlying glyph buffer
+     */
+    public void setGlyph ( int index, int gi ) throws IndexOutOfBoundsException {
+        if ( gi > 65535 ) {
+            gi = 65535;
+        }
+        glyphs.put ( index, gi );
     }
 
     /**
-     * Obtain the sequence of characters that corresponds to the glyph sequence at interval
-     * [offset,offset+count).
-     * @param offset to first glyph
-     * @param count of glyphs
-     * @return corresponding character sequence
-     */
-    public CharSequence getCharsForGlyphs ( int offset, int count ) throws DiscontinuousAssociationException {          // CSOK: JavadocMethodCheck
-        int sFirst = -1, eLast = -1;
-        for ( int i = 0, n = count; i < n; i++ ) {
-            CharAssociation ca = associations [ offset + i ];
-            int s = ca.getStart();
-            int e = ca.getEnd();
-            if ( sFirst < 0 ) {
-                sFirst = s;
-            }
-            if ( eLast < 0 ) {
-                eLast = e;
-            } else if ( s == eLast ) {
-                eLast = e;
-            } else {
-                throw new DiscontinuousAssociationException();
+     * Obtain reference to underlying glyph buffer.
+     * @return glyph buffer reference
+     */
+    public IntBuffer getGlyphs() {
+        return glyphs;
+    }
+
+    /**
+     * Obtain count glyphs starting at offset. If <code>count</code> is
+     * negative, then it is treated as if the number of available glyphs
+     * were specified.
+     * @param offset into glyph sequence
+     * @param count of glyphs to obtain starting at offset, or negative,
+     * indicating all avaialble glyphs starting at offset
+     * @return glyph array
+     */
+    public int[] getGlyphs ( int offset, int count ) {
+        int ng = getGlyphCount();
+        if ( offset < 0 ) {
+            offset = 0;
+        } else if ( offset > ng ) {
+            offset = ng;
+        }
+        if ( count < 0 ) {
+            count = ng - offset;
+        }
+        int[] ga = new int [ count ];
+        for ( int i = offset, n = offset + count, k = 0; i < n; i++ ) {
+            if ( k < ga.length ) {
+                ga [ k++ ] = glyphs.get ( i );
             }
         }
-        return characters.subSequence ( sFirst, eLast );
+        return ga;
     }
 
     /**
-     * Obtain the glyph subsequence corresponding to the half-open interval [start,end).
-     * @param start of subsequence
-     * @param end of subsequence
-     * @return a subsequence of this sequence
+     * Obtain array of glyphs. If <code>copy</code> is true, then
+     * a newly instantiated array is returned, otherwise a reference to
+     * the underlying buffer's array is returned. N.B. in case a reference
+     * to the undelying buffer's array is returned, the length
+     * of the array is not necessarily the number of glyphs in array.
+     * To determine the number of glyphs, use {@link #getGlyphCount}.
+     * @param copy true if to return a newly instantiated array of glyphs
+     * @return array of glyphs
      */
-    public GlyphSequence getGlyphSubsequence ( int start, int end ) {
-        CharAssociation[] subset = new CharAssociation[end - start];
-        System.arraycopy(associations, start, subset, 0, end - start);
-        return new GlyphSequence ( characters, glyphs.subSequence ( start, end ), subset );
+    public int[] getGlyphArray ( boolean copy ) {
+        if ( copy ) {
+            return toArray ( glyphs );
+        } else {
+            return glyphs.array();
+        }
     }
 
-    /** @return the number of glyphs in this glyph sequence */
-    public int length() {
-        return glyphs.length();
+    /**
+     * Obtain the number of glyphs in glyphs array, where
+     * each glyph constitutes a font specific glyph index.
+     * @return number of glyphs available in character array
+     */
+    public int getGlyphCount() {
+        return glyphs.limit();
     }
 
     /**
-     * Obtain glyph id at specified index.
-     * @param index to obtain glyph
-     * @return the glyph identifier of glyph at specified index
+     * Obtain association at specified index.
+     * @param index into associations array
+     * @return glyph to character associations at specified index
+     * @throws IndexOutOfBoundsException if index is less than zero
+     * or exceeds last valid position
+     */
+    public CharAssociation getAssociation ( int index ) throws IndexOutOfBoundsException {
+        return (CharAssociation) associations.get ( index );
+    }
+
+    /**
+     * Obtain reference to underlying associations list.
+     * @return associations list
+     */
+    public List getAssociations() {
+        return associations;
+    }
+
+    /**
+     * Obtain count associations starting at offset.
+     * @param offset into glyph sequence
+     * @param count of associations to obtain starting at offset, or negative,
+     * indicating all avaialble associations starting at offset
+     * @return associations
      */
-    public char charAt ( int index ) {
-        return glyphs.charAt ( index );
+    public CharAssociation[] getAssociations ( int offset, int count ) {
+        int ng = getGlyphCount();
+        if ( offset < 0 ) {
+            offset = 0;
+        } else if ( offset > ng ) {
+            offset = ng;
+        }
+        if ( count < 0 ) {
+            count = ng - offset;
+        }
+        CharAssociation[] aa = new CharAssociation [ count ];
+        for ( int i = offset, n = offset + count, k = 0; i < n; i++ ) {
+            if ( k < aa.length ) {
+                aa [ k++ ] = (CharAssociation) associations.get ( i );
+            }
+        }
+        return aa;
     }
 
     /**
-     * Obtain glyph code subsequence over interval [start,end).
-     * @param start of subsequence
-     * @param end of subsequence
-     * @return the glyph code subsequence
+     * Compare glyphs.
+     * @param gb buffer containing glyph indices with which this glyph sequence's glyphs are to be compared
+     * @return zero if glyphs are the same, otherwise returns 1 or -1 according to whether this glyph sequence's
+     * glyphs are lexicographically greater or lesser than the glyphs in the specified string buffer
      */
-    public CharSequence subSequence ( int start, int end ) {
-        return glyphs.subSequence ( start, end );
+    public int compareGlyphs ( IntBuffer gb ) {
+        int ng = getGlyphCount();
+        for ( int i = 0, n = gb.limit(); i < n; i++ ) {
+            if ( i < ng ) {
+                int g1 = glyphs.get ( i );
+                int g2 = gb.get ( i );
+                if ( g1 > g2 ) {
+                    return 1;
+                } else if ( g1 < g2 ) {
+                    return -1;
+                }
+            } else {
+                return -1;              // this gb is a proper prefix of specified gb
+            }
+        }
+        return 0;                       // same lengths with no difference
+    }
+
+    /** {@inheritDoc} */
+    public Object clone() {
+        try {
+            GlyphSequence gs = (GlyphSequence) super.clone();
+            gs.characters = copyBuffer ( characters );
+            gs.glyphs = copyBuffer ( glyphs );
+            gs.associations = copyAssociations ( associations );
+            return gs;
+        } catch ( CloneNotSupportedException e ) {
+            return null;
+        }
     }
 
     /** {@inheritDoc} */
     public String toString() {
-        return glyphs.toString();
+        StringBuffer sb = new StringBuffer();
+        sb.append ( '{' );
+        sb.append ( "chars = [" );
+        sb.append ( characters );
+        sb.append ( "], glyphs = [" );
+        sb.append ( glyphs );
+        sb.append ( "], associations = [" );
+        sb.append ( associations );
+        sb.append ( "]" );
+        sb.append ( '}' );
+        return sb.toString();
     }
 
-    private CharAssociation[] makeIdentityAssociations ( CharSequence characters, CharSequence glyphs ) {
-        int nc = characters.length();
-        int ng = glyphs.length();
-        CharAssociation[] ca = new CharAssociation [ ng ];
-        for ( int i = 0, n = ng; i < n; i++ ) {
-            int k = ( i > nc ) ? nc : i;
-            ca [ i ] = new CharAssociation ( i, ( k == nc ) ? 0 : 1 );
+    /**
+     * Determine if two arrays of glyphs are identical.
+     * @param ga1 first glyph array
+     * @param ga2 second glyph array
+     * @return true if arrays are botth null or both non-null and have identical elements
+     */
+    public static boolean sameGlyphs ( int[] ga1, int[] ga2 ) {
+        if ( ga1 == ga2 ) {
+            return true;
+        } else if ( ( ga1 == null ) || ( ga2 == null ) ) {
+            return false;
+        } else if ( ga1.length != ga2.length ) {
+            return false;
+        } else {
+            for ( int i = 0, n = ga1.length; i < n; i++ ) {
+                if ( ga1[i] != ga2[i] ) {
+                    return false;
+                }
+            }
+            return true;
         }
-        return ca;
     }
 
-    private static CharSequence concatenateSequences ( List/*<GlyphSequence>*/ sequences, boolean reverse ) {
+    /**
+     * Concatenante glyph arrays.
+     * @param bga backtrack glyph array
+     * @param iga input glyph array
+     * @param lga lookahead glyph array
+     * @return new integer buffer containing concatenated glyphs
+     */
+    public static IntBuffer concatGlyphs ( int[] bga, int[] iga, int[] lga ) {
         int ng = 0;
-        for ( Iterator it = sequences.iterator(); it.hasNext();) {
-            GlyphSequence gs = (GlyphSequence) it.next();
-            ng += gs.length();
+        if ( bga != null ) {
+            ng += bga.length;
         }
-        CharBuffer cb = CharBuffer.allocate ( ng );
-        if ( ! reverse ) {
-            for ( ListIterator it = sequences.listIterator(); it.hasNext();) {
-                GlyphSequence gs = (GlyphSequence) it.next();
-                cb.append ( (CharSequence) gs );
-            }
-        } else {
-            for ( ListIterator it = sequences.listIterator ( sequences.size() ); it.hasPrevious();) {
-                GlyphSequence gs = (GlyphSequence) it.previous();
-                cb.append ( (CharSequence) gs );
-            }
+        if ( iga != null ) {
+            ng += iga.length;
+        }
+        if ( lga != null ) {
+            ng += lga.length;
+        }
+        IntBuffer gb = IntBuffer.allocate ( ng );
+        if ( bga != null ) {
+            gb.put ( bga );
         }
-        cb.rewind();
-        return cb;
+        if ( iga != null ) {
+            gb.put ( iga );
+        }
+        if ( lga != null ) {
+            gb.put ( lga );
+        }
+        gb.flip();
+        return gb;
     }
 
-    private static CharAssociation[] concatenateAssociations ( List/*<CharAssociation>*/ associations, boolean reverse ) {
+    /**
+     * Concatenante association arrays.
+     * @param baa backtrack association array
+     * @param iaa input association array
+     * @param laa lookahead association array
+     * @return new list containing concatenated associations
+     */
+    public static List concatAssociations ( CharAssociation[] baa, CharAssociation[] iaa, CharAssociation[] laa ) {
         int na = 0;
-        CharAssociation[] ca = new CharAssociation [ associations.size() ];
-        if ( ! reverse ) {
-            for ( ListIterator it = associations.listIterator(); it.hasNext();) {
-                CharAssociation a = (CharAssociation) it.next();
-                ca [ na++ ] = a;
+        if ( baa != null ) {
+            na += baa.length;
+        }
+        if ( iaa != null ) {
+            na += iaa.length;
+        }
+        if ( laa != null ) {
+            na += laa.length;
+        }
+        if ( na > 0 ) {
+            List gl = new ArrayList ( na );
+            if ( baa != null ) {
+                for ( int i = 0; i < baa.length; i++ ) {
+                    gl.add ( baa[i] );
+                }
             }
-        } else {
-            for ( ListIterator it = associations.listIterator ( associations.size() ); it.hasPrevious();) {
-                CharAssociation a = (CharAssociation) it.previous();
-                ca [ na++ ] = a;
+            if ( iaa != null ) {
+                for ( int i = 0; i < iaa.length; i++ ) {
+                    gl.add ( iaa[i] );
+                }
             }
+            if ( laa != null ) {
+                for ( int i = 0; i < laa.length; i++ ) {
+                    gl.add ( laa[i] );
+                }
+            }
+            return gl;
+        } else {
+            return null;
+        }
+    }
+
+    private static int[] toArray ( IntBuffer ib ) {
+        if ( ib != null ) {
+            int n = ib.limit();
+            int[] ia = new int[n];
+            ib.get ( ia, 0, n );
+            return ia;
+        } else {
+            return new int[0];
+        }
+    }
+
+    private static List makeIdentityAssociations ( int numChars, int numGlyphs ) {
+        int nc = numChars;
+        int ng = numGlyphs;
+        List av = new ArrayList ( ng );
+        for ( int i = 0, n = ng; i < n; i++ ) {
+            int k = ( i > nc ) ? nc : i;
+            av.add ( new CharAssociation ( i, ( k == nc ) ? 0 : 1 ) );
+        }
+        return av;
+    }
+
+    private static IntBuffer copyBuffer ( IntBuffer ib ) {
+        if ( ib != null ) {
+            int[] ia = new int [ ib.capacity() ];
+            int   p  = ib.position();
+            int   l  = ib.limit();
+            System.arraycopy ( ib.array(), 0, ia, 0, ia.length );
+            return IntBuffer.wrap ( ia, p, l - p );
+        } else {
+            return null;
+        }
+    }
+
+    private static List copyAssociations ( List ca ) {
+        if ( ca != null ) {
+            return new ArrayList ( ca );
+        } else {
+            return ca;
         }
-        return ca;
     }
 
     /**
      * A structure class encapsulating an interval of character codes (in a CharSequence)
-     * expressed as an offset and count (of code elements in a CharSequence, i.e., numbere of
+     * expressed as an offset and count (of code elements in a CharSequence, i.e., number of
      * UTF-16 code elements. N.B. count does not necessarily designate the number of Unicode
      * scalar values expressed by the CharSequence; in particular, it does not do so if there
      * is one or more UTF-16 surrogate pairs present in the CharSequence.)
      */
-    public static class CharAssociation {
+    public static class CharAssociation implements Cloneable {
 
         private final int offset;
         private final int count;
+        private final int[] subIntervals;
 
         /**
          * Instantiate a character association.
          * @param offset into array of UTF-16 code elements (in associated CharSequence)
          * @param count of UTF-16 character code elements (in associated CharSequence)
+         * @param subIntervals if disjoint, then array of sub-intervals, otherwise null; even
+         * members of array are sub-interval starts, and odd members are sub-interval
+         * ends (exclusive)
          */
-        public CharAssociation ( int offset, int count ) {
+        public CharAssociation ( int offset, int count, int[] subIntervals ) {
             this.offset = offset;
             this.count = count;
+            this.subIntervals = ( ( subIntervals != null ) && ( subIntervals.length > 2 ) ) ? subIntervals : null;
+        }
+
+        /**
+         * Instantiate a non-disjoint character association.
+         * @param offset into array of UTF-16 code elements (in associated CharSequence)
+         * @param count of UTF-16 character code elements (in associated CharSequence)
+         */
+        public CharAssociation ( int offset, int count ) {
+            this ( offset, count, null );
+        }
+
+        /**
+         * Instantiate a non-disjoint character association.
+         * @param subIntervals if disjoint, then array of sub-intervals, otherwise null; even
+         * members of array are sub-interval starts, and odd members are sub-interval
+         * ends (exclusive)
+         */
+        public CharAssociation ( int[] subIntervals ) {
+            this ( getSubIntervalsStart ( subIntervals ), getSubIntervalsLength ( subIntervals ), subIntervals );
         }
 
         /** @return offset (start of association interval) */
@@ -250,5 +527,231 @@ public class GlyphSequence implements Ch
             return getOffset() + getCount();
         }
 
+        /** @return true if association is disjoint */
+        public boolean isDisjoint() {
+            return subIntervals != null;
+        }
+
+        /** @return subintervals of disjoint association */
+        public int[] getSubIntervals() {
+            return subIntervals;
+        }
+
+        /** @return count of subintervals of disjoint association */
+        public int getSubIntervalCount() {
+            return ( subIntervals != null ) ? ( subIntervals.length / 2 ) : 0;
+        }
+
+        /** {@inheritDoc} */
+        public Object clone() {
+            try {
+                return super.clone();
+            } catch ( CloneNotSupportedException e ) {
+                return null;
+            }
+        }
+
+        /**
+         * Replicate association to form <code>repeat</code> new associations.
+         * @param a association to replicate
+         * @param repeat count
+         * @return array of replicated associations
+         */
+        public static CharAssociation[] replicate ( CharAssociation a, int repeat ) {
+            CharAssociation[] aa = new CharAssociation [ repeat ];
+            for ( int i = 0, n = aa.length; i < n; i++ ) {
+                aa [ i ] = (CharAssociation) a.clone();
+            }
+            return aa;
+        }
+
+        /**
+         * Join (merge) multiple associations into a single, potentially disjoint
+         * association.
+         * @param aa array of associations to join
+         * @return (possibly disjoint) association containing joined associations
+         */
+        public static CharAssociation join ( CharAssociation[] aa ) {
+            // extract sorted intervals
+            int[] ia = extractIntervals ( aa );
+            if ( ( ia == null ) || ( ia.length == 0 ) ) {
+                return new CharAssociation ( 0, 0 );
+            } else if ( ia.length == 2 ) {
+                int s = ia[0];
+                int e = ia[1];
+                return new CharAssociation ( s, e - s );
+            } else {
+                return new CharAssociation ( mergeIntervals ( ia ) );
+            }
+        }
+
+        private static int getSubIntervalsStart ( int[] ia ) {
+            int us = Integer.MAX_VALUE;
+            int ue = Integer.MIN_VALUE;
+            if ( ia != null ) {
+                for ( int i = 0, n = ia.length; i < n; i += 2 ) {
+                    int s = ia [ i + 0 ];
+                    int e = ia [ i + 1 ];
+                    if ( s < us ) {
+                        us = s;
+                    }
+                    if ( e > ue ) {
+                        ue = e;
+                    }
+                }
+                if ( ue < 0 ) {
+                    ue = 0;
+                }
+                if ( us > ue ) {
+                    us = ue;
+                }
+            }
+            return us;
+        }
+
+        private static int getSubIntervalsLength ( int[] ia ) {
+            int us = Integer.MAX_VALUE;
+            int ue = Integer.MIN_VALUE;
+            if ( ia != null ) {
+                for ( int i = 0, n = ia.length; i < n; i += 2 ) {
+                    int s = ia [ i + 0 ];
+                    int e = ia [ i + 1 ];
+                    if ( s < us ) {
+                        us = s;
+                    }
+                    if ( e > ue ) {
+                        ue = e;
+                    }
+                }
+                if ( ue < 0 ) {
+                    ue = 0;
+                }
+                if ( us > ue ) {
+                    us = ue;
+                }
+            }
+            return ue - us;
+        }
+
+        /**
+         * Extract sorted sub-intervals.
+         */
+        private static int[] extractIntervals ( CharAssociation[] aa ) {
+            int ni = 0;
+            for ( int i = 0, n = aa.length; i < n; i++ ) {
+                CharAssociation a = aa [ i ];
+                if ( a.isDisjoint() ) {
+                    ni += a.getSubIntervalCount();
+                } else {
+                    ni += 1;
+                }
+            }
+            int[] sa = new int [ ni ];
+            int[] ea = new int [ ni ];
+            for ( int i = 0, k = 0; i < aa.length; i++ ) {
+                CharAssociation a = aa [ i ];
+                if ( a.isDisjoint() ) {
+                    int[] da = a.getSubIntervals();
+                    for ( int j = 0; j < da.length; j += 2 ) {
+                        sa [ k ] = da [ j + 0 ];
+                        ea [ k ] = da [ j + 1 ];
+                        k++;
+                    }
+                } else {
+                    sa [ k ] = a.getStart();
+                    ea [ k ] = a.getEnd();
+                    k++;
+                }
+            }
+            return sortIntervals ( sa, ea );
+        }
+
+        private static final int[] sortIncrements16                                                             // CSOK: ConstantNameCheck
+            = { 1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1 };
+
+        private static final int[] sortIncrements03                                                             // CSOK: ConstantNameCheck
+            = { 7, 3, 1 };
+
+        /**
+         * Sort sub-intervals using modified Shell Sort.
+         */
+        private static int[] sortIntervals ( int[] sa, int[] ea ) {
+            assert sa != null;
+            assert ea != null;
+            assert sa.length == ea.length;
+            int ni = sa.length;
+            int[] incr = ( ni < 21 ) ? sortIncrements03 : sortIncrements16;
+            for ( int k = 0; k < incr.length; k++ ) {
+                for ( int h = incr [ k ], i = h, n = ni, j; i < n; i++ ) {
+                    int s1 = sa [ i ];
+                    int e1 = ea [ i ];
+                    for ( j = i; j >= h; j -= h) {
+                        int s2 = sa [ j - h ];
+                        int e2 = ea [ j - h ];
+                        if ( s2 > s1 ) {
+                            sa [ j ] = s2;
+                            ea [ j ] = e2;
+                        } else if ( ( s2 == s1 ) && ( e2 > e1 ) ) {
+                            sa [ j ] = s2;
+                            ea [ j ] = e2;
+                        } else {
+                            break;
+                        }
+                    }
+                    sa [ j ] = s1;
+                    ea [ j ] = e1;
+                }
+            }
+            int[] ia = new int [ ni * 2 ];
+            for ( int i = 0; i < ni; i++ ) {
+                ia [ ( i * 2 ) + 0 ] = sa [ i ];
+                ia [ ( i * 2 ) + 1 ] = ea [ i ];
+            }
+            return ia;
+        }
+
+        /**
+         * Merge overlapping and abutting sub-intervals.
+         */
+        private static int[] mergeIntervals ( int[] ia ) {
+            int ni = ia.length;
+            int i, n, nm, is, ie;
+            // count merged sub-intervals
+            for ( i = 0, n = ni, nm = 0, is = ie = -1; i < n; i += 2 ) {
+                int s = ia [ i + 0 ];
+                int e = ia [ i + 1 ];
+                if ( ( ie < 0 ) || ( s > ie ) ) {
+                    is = s;
+                    ie = e;
+                    nm++;
+                } else if ( s >= is ) {
+                    if ( e > ie ) {
+                        ie = e;
+                    }
+                }
+            }
+            int[] mi = new int [ nm * 2 ];
+            // populate merged sub-intervals
+            for ( i = 0, n = ni, nm = 0, is = ie = -1; i < n; i += 2 ) {
+                int s = ia [ i + 0 ];
+                int e = ia [ i + 1 ];
+                int k = nm * 2;
+                if ( ( ie < 0 ) || ( s > ie ) ) {
+                    is = s;
+                    ie = e;
+                    mi [ k + 0 ] = is;
+                    mi [ k + 1 ] = ie;
+                    nm++;
+                } else if ( s >= is ) {
+                    if ( e > ie ) {
+                        ie = e;
+                    }
+                    mi [ k - 1 ] = ie;
+                }
+            }
+            return mi;
+        }
+
     }
+
 }

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphSubstitution.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphSubstitution.java?rev=1002978&r1=1002977&r2=1002978&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphSubstitution.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/fonts/GlyphSubstitution.java Thu Sep 30 09:47:15 2010
@@ -22,7 +22,7 @@ package org.apache.fop.fonts;
 // CSOFF: LineLengthCheck
 
 /**
- * The <code>GlyphSubstitution</code> interface is implemented by a font related object
+ * The <code>GlyphSubstitution</code> interface is implemented by a glyph substitution subtable
  * that supports the determination of glyph substitution information based on script and
  * language of the corresponding character content.
  * @author Glenn Adams
@@ -30,13 +30,12 @@ package org.apache.fop.fonts;
 public interface GlyphSubstitution {
 
     /**
-     * Perform glyph substitutions. If no substitution applies, then returns the unmodified input sequence.
-     * @param gs sequence to map to output glyph sequence
-     * @param script the script associated with the characters corresponding to the glyph sequence
-     * @param language the language associated with the characters corresponding to the glyph sequence
-     * @return resulting glyph sequence, where each 'glyph' in the returned sequence has been mapped
-     * (or not) by substitution
+     * Perform glyph substitution at the current index, mutating the substitution state object as required.
+     * Only the context associated with the current index is processed.
+     * @param ss glyph substitution state object
+     * @return true if the glyph subtable was applied, meaning that the current context matches the
+     * associated input context glyph coverage table
      */
-    GlyphSequence substitute ( GlyphSequence gs, String script, String language );
+    boolean substitute ( GlyphSubstitutionState ss );
 
 }



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