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 [13/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/src/java/org/apache/fop/complexscripts/scripts/GurmukhiScriptProcessor.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/scripts/GurmukhiScriptProcessor.java?rev=1293736&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/scripts/GurmukhiScriptProcessor.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/scripts/GurmukhiScriptProcessor.java Sun Feb 26 02:29:01 2012
@@ -0,0 +1,543 @@
+/*
+ * 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.scripts;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.complexscripts.fonts.GlyphDefinitionTable;
+import org.apache.fop.complexscripts.util.GlyphSequence;
+import org.apache.fop.complexscripts.util.ScriptContextTester;
+
+// CSOFF: AvoidNestedBlocksCheck
+// CSOFF: NoWhitespaceAfterCheck
+// CSOFF: WhitespaceAfter
+// CSOFF: InnerAssignmentCheck
+// CSOFF: SimplifyBooleanReturnCheck
+// CSOFF: LineLengthCheck
+
+/**
+ * <p>The <code>GurmukhiScriptProcessor</code> class implements a script processor for
+ * performing glyph substitution and positioning operations on content associated with the Gurmukhi script.</p>
+ * @author Glenn Adams
+ */
+public class GurmukhiScriptProcessor extends IndicScriptProcessor {
+
+    /** logging instance */
+    private static final Log log = LogFactory.getLog(GurmukhiScriptProcessor.class);                                  // CSOK: ConstantNameCheck
+
+    GurmukhiScriptProcessor ( String script ) {
+        super ( script );
+    }
+
+    @Override
+    protected Class<? extends GurmukhiSyllabizer> getSyllabizerClass() {
+        return GurmukhiSyllabizer.class;
+    }
+
+    @Override
+    // find rightmost pre-base matra
+    protected int findPreBaseMatra ( GlyphSequence gs ) {
+        int   ng = gs.getGlyphCount();
+        int   lk = -1;
+        for ( int i = ng; i > 0; i-- ) {
+            int k = i - 1;
+            if ( containsPreBaseMatra ( gs, k ) ) {
+                lk = k;
+                break;
+            }
+        }
+        return lk;
+    }
+
+    @Override
+    // find leftmost pre-base matra target, starting from source
+    protected int findPreBaseMatraTarget ( GlyphSequence gs, int source ) {
+        int   ng = gs.getGlyphCount();
+        int   lk = -1;
+        for ( int i = ( source < ng ) ? source : ng; i > 0; i-- ) {
+            int k = i - 1;
+            if ( containsConsonant ( gs, k ) ) {
+                if ( containsHalfConsonant ( gs, k ) ) {
+                    lk = k;
+                } else if ( lk == -1 ) {
+                    lk = k;
+                } else {
+                    break;
+                }
+            }
+        }
+        return lk;
+    }
+
+    private static boolean containsPreBaseMatra ( GlyphSequence gs, int k ) {
+        GlyphSequence.CharAssociation a = gs.getAssociation ( k );
+        int[] ca = gs.getCharacterArray ( false );
+        for ( int i = a.getStart(), e = a.getEnd(); i < e; i++ ) {
+            if ( isPreM ( ca [ i ] ) ) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean containsConsonant ( GlyphSequence gs, int k ) {
+        GlyphSequence.CharAssociation a = gs.getAssociation ( k );
+        int[] ca = gs.getCharacterArray ( false );
+        for ( int i = a.getStart(), e = a.getEnd(); i < e; i++ ) {
+            if ( isC ( ca [ i ] ) ) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean containsHalfConsonant ( GlyphSequence gs, int k ) {
+        Boolean half = (Boolean) gs.getAssociation ( k ) . getPredication ( "half" );
+        return ( half != null ) ? half.booleanValue() : false;
+    }
+
+    @Override
+    protected int findReph ( GlyphSequence gs ) {
+        int   ng = gs.getGlyphCount();
+        int   li = -1;
+        for ( int i = 0; i < ng; i++ ) {
+            if ( containsReph ( gs, i ) ) {
+                li = i;
+                break;
+            }
+        }
+        return li;
+    }
+
+    @Override
+    protected int findRephTarget ( GlyphSequence gs, int source ) {
+        int   ng = gs.getGlyphCount();
+        int   c1 = -1;
+        int   c2 = -1;
+        // first candidate target is after first non-half consonant
+        for ( int i = 0; i < ng; i++ ) {
+            if ( ( i != source ) && containsConsonant ( gs, i ) ) {
+                if ( ! containsHalfConsonant ( gs, i ) ) {
+                    c1 = i + 1;
+                    break;
+                }
+            }
+        }
+        // second candidate target is after last non-prebase matra after first candidate or before first syllable or vedic mark
+        for ( int i = ( c1 >= 0 ) ? c1 : 0; i < ng; i++ ) {
+            if ( containsMatra ( gs, i ) && ! containsPreBaseMatra ( gs, i ) ) {
+                c2 = i + 1;
+            } else if ( containsOtherMark ( gs, i ) ) {
+                c2 = i;
+                break;
+            }
+        }
+        if ( c2 >= 0 ) {
+            return c2;
+        } else if ( c1 >= 0 ) {
+            return c1;
+        } else {
+            return source;
+        }
+    }
+
+    private static boolean containsReph ( GlyphSequence gs, int k ) {
+        Boolean rphf = (Boolean) gs.getAssociation ( k ) . getPredication ( "rphf" );
+        return ( rphf != null ) ? rphf.booleanValue() : false;
+    }
+
+    private static boolean containsMatra ( GlyphSequence gs, int k ) {
+        GlyphSequence.CharAssociation a = gs.getAssociation ( k );
+        int[] ca = gs.getCharacterArray ( false );
+        for ( int i = a.getStart(), e = a.getEnd(); i < e; i++ ) {
+            if ( isM ( ca [ i ] ) ) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean containsOtherMark ( GlyphSequence gs, int k ) {
+        GlyphSequence.CharAssociation a = gs.getAssociation ( k );
+        int[] ca = gs.getCharacterArray ( false );
+        for ( int i = a.getStart(), e = a.getEnd(); i < e; i++ ) {
+            switch ( typeOf ( ca [ i ] ) ) {
+            case C_T:   // tone (e.g., udatta, anudatta)
+            case C_A:   // accent (e.g., acute, grave)
+            case C_O:   // other (e.g., candrabindu, anusvara, visarga, etc)
+                return true;
+            default:
+                break;
+            }
+        }
+        return false;
+    }
+
+    private static class GurmukhiSyllabizer extends DefaultSyllabizer {
+        GurmukhiSyllabizer ( String script, String language ) {
+            super ( script, language );
+        }
+        @Override
+        // | C ...
+        protected int findStartOfSyllable ( int[] ca, int s, int e ) {
+            if ( ( s < 0 ) || ( s >= e ) ) {
+                return -1;
+            } else {
+                while ( s < e ) {
+                    int c = ca [ s ];
+                    if ( isC ( c ) ) {
+                        break;
+                    } else {
+                        s++;
+                    }
+                }
+                return s;
+            }
+        }
+        @Override
+        // D* L? | ...
+        protected int findEndOfSyllable ( int[] ca, int s, int e ) {
+            if ( ( s < 0 ) || ( s >= e ) ) {
+                return -1;
+            } else {
+                int nd = 0;
+                int nl = 0;
+                int i;
+                // consume dead consonants
+                while ( ( i = isDeadConsonant ( ca, s, e ) ) > s ) {
+                    s = i; nd++;
+                }
+                // consume zero or one live consonant
+                if ( ( i = isLiveConsonant ( ca, s, e ) ) > s ) {
+                    s = i; nl++;
+                }
+                return ( ( nd > 0 ) || ( nl > 0 ) ) ? s : -1;
+            }
+        }
+        // D := ( C N? H )?
+        private int isDeadConsonant ( int[] ca, int s, int e ) {
+            if ( s < 0 ) {
+                return -1;
+            } else {
+                int c, i = 0;
+                int nc = 0, nh = 0;
+                do {
+                    // C
+                    if ( ( s + i ) < e ) {
+                        c = ca [ s + i ];
+                        if ( isC ( c ) ) {
+                            i++;
+                            nc++;
+                        } else {
+                            break;
+                        }
+                    }
+                    // N?
+                    if ( ( s + i ) < e ) {
+                        c = ca [ s + 1 ];
+                        if ( isN ( c ) ) {
+                            i++;
+                        }
+                    }
+                    // H
+                    if ( ( s + i ) < e ) {
+                        c = ca [ s + i ];
+                        if ( isH ( c ) ) {
+                            i++;
+                            nh++;
+                        } else {
+                            break;
+                        }
+                    }
+                } while ( false );
+                return ( nc > 0 ) && ( nh > 0 ) ? s + i : -1;
+            }
+        }
+        // L := ( (C|V) N? X* )?; where X = ( MATRA | ACCENT MARK | TONE MARK | OTHER MARK )
+        private int isLiveConsonant ( int[] ca, int s, int e ) {
+            if ( s < 0 ) {
+                return -1;
+            } else {
+                int c, i = 0;
+                int nc = 0, nv = 0, nx = 0;
+                do {
+                    // C
+                    if ( ( s + i ) < e ) {
+                        c = ca [ s + i ];
+                        if ( isC ( c ) ) {
+                            i++;
+                            nc++;
+                        } else if ( isV ( c ) ) {
+                            i++;
+                            nv++;
+                        } else {
+                            break;
+                        }
+                    }
+                    // N?
+                    if ( ( s + i ) < e ) {
+                        c = ca [ s + i ];
+                        if ( isN ( c ) ) {
+                            i++;
+                        }
+                    }
+                    // X*
+                    while ( ( s + i ) < e ) {
+                        c = ca [ s + i ];
+                        if ( isX ( c ) ) {
+                            i++;
+                            nx++;
+                        } else {
+                            break;
+                        }
+                    }
+                } while ( false );
+                // if no X but has H, then ignore C|I
+                if ( nx == 0 ) {
+                    if ( ( s + i ) < e ) {
+                        c = ca [ s + i ];
+                        if ( isH ( c ) ) {
+                            if ( nc > 0 ) {
+                                nc--;
+                            } else if ( nv > 0 ) {
+                                nv--;
+                            }
+                        }
+                    }
+                }
+                return ( ( nc > 0 ) || ( nv > 0 ) ) ? s + i : -1;
+            }
+        }
+    }
+
+    // gurmukhi character types
+    static final short C_U          = 0;            // unassigned
+    static final short C_C          = 1;            // consonant
+    static final short C_V          = 2;            // vowel
+    static final short C_M          = 3;            // vowel sign (matra)
+    static final short C_S          = 4;            // symbol or sign
+    static final short C_T          = 5;            // tone mark
+    static final short C_A          = 6;            // accent mark
+    static final short C_P          = 7;            // punctuation
+    static final short C_D          = 8;            // digit
+    static final short C_H          = 9;            // halant (virama)
+    static final short C_O          = 10;           // other signs
+    static final short C_N          = 0x0100;       // nukta(ized)
+    static final short C_R          = 0x0200;       // reph(ized)
+    static final short C_PRE        = 0x0400;       // pre-base
+    static final short C_M_TYPE     = 0x00FF;       // type mask
+    static final short C_M_FLAGS    = 0x7F00;       // flag mask
+    // gurmukhi block range
+    static final int ccaStart       =  0x0A00;      // first code point mapped by cca                                   // CSOK: ConstantNameCheck
+    static final int ccaEnd         =  0x0A80;      // last code point + 1 mapped by cca                                // CSOK: ConstantNameCheck
+    // gurmukhi character type lookups
+    static final short[] cca = {                                                                                        // CSOK: ConstantNameCheck
+        C_U,                        // 0x0A00       // UNASSIGNED
+        C_O,                        // 0x0A01       // ADAK BINDI
+        C_O,                        // 0x0A02       // BINDI
+        C_O,                        // 0x0A03       // VISARGA
+        C_U,                        // 0x0A04       // UNASSIGNED
+        C_V,                        // 0x0A05       // A
+        C_V,                        // 0x0A06       // AA
+        C_V,                        // 0x0A07       // I
+        C_V,                        // 0x0A08       // II
+        C_V,                        // 0x0A09       // U
+        C_V,                        // 0x0A0A       // UU
+        C_U,                        // 0x0A0B       // UNASSIGNED
+        C_U,                        // 0x0A0C       // UNASSIGNED
+        C_U,                        // 0x0A0D       // UNASSIGNED
+        C_U,                        // 0x0A0E       // UNASSIGNED
+        C_V,                        // 0x0A0F       // E
+        C_V,                        // 0x0A10       // AI
+        C_U,                        // 0x0A11       // UNASSIGNED
+        C_U,                        // 0x0A12       // UNASSIGNED
+        C_V,                        // 0x0A13       // O
+        C_V,                        // 0x0A14       // AU
+        C_C,                        // 0x0A15       // KA
+        C_C,                        // 0x0A16       // KHA
+        C_C,                        // 0x0A17       // GA
+        C_C,                        // 0x0A18       // GHA
+        C_C,                        // 0x0A19       // NGA
+        C_C,                        // 0x0A1A       // CA
+        C_C,                        // 0x0A1B       // CHA
+        C_C,                        // 0x0A1C       // JA
+        C_C,                        // 0x0A1D       // JHA
+        C_C,                        // 0x0A1E       // NYA
+        C_C,                        // 0x0A1F       // TTA
+        C_C,                        // 0x0A20       // TTHA
+        C_C,                        // 0x0A21       // DDA
+        C_C,                        // 0x0A22       // DDHA
+        C_C,                        // 0x0A23       // NNA
+        C_C,                        // 0x0A24       // TA
+        C_C,                        // 0x0A25       // THA
+        C_C,                        // 0x0A26       // DA
+        C_C,                        // 0x0A27       // DHA
+        C_C,                        // 0x0A28       // NA
+        C_U,                        // 0x0A29       // UNASSIGNED
+        C_C,                        // 0x0A2A       // PA
+        C_C,                        // 0x0A2B       // PHA
+        C_C,                        // 0x0A2C       // BA
+        C_C,                        // 0x0A2D       // BHA
+        C_C,                        // 0x0A2E       // MA
+        C_C,                        // 0x0A2F       // YA
+        C_C|C_R,                    // 0x0A30       // RA                                                               // CSOK: WhitespaceAround
+        C_U,                        // 0x0A31       // UNASSIGNED
+        C_C,                        // 0x0A32       // LA
+        C_C,                        // 0x0A33       // LLA
+        C_U,                        // 0x0A34       // UNASSIGNED
+        C_C,                        // 0x0A35       // VA
+        C_C,                        // 0x0A36       // SHA
+        C_U,                        // 0x0A37       // UNASSIGNED
+        C_C,                        // 0x0A38       // SA
+        C_C,                        // 0x0A39       // HA
+        C_U,                        // 0x0A3A       // UNASSIGNED
+        C_U,                        // 0x0A3B       // UNASSIGNED
+        C_N,                        // 0x0A3C       // NUKTA
+        C_U,                        // 0x0A3D       // UNASSIGNED
+        C_M,                        // 0x0A3E       // AA
+        C_M|C_PRE,                  // 0x0A3F       // I                                                               // CSOK: WhitespaceAround
+        C_M,                        // 0x0A40       // II
+        C_M,                        // 0x0A41       // U
+        C_M,                        // 0x0A42       // UU
+        C_U,                        // 0x0A43       // UNASSIGNED
+        C_U,                        // 0x0A44       // UNASSIGNED
+        C_U,                        // 0x0A45       // UNASSIGNED
+        C_U,                        // 0x0A46       // UNASSIGNED
+        C_M,                        // 0x0A47       // EE
+        C_M,                        // 0x0A48       // AI
+        C_U,                        // 0x0A49       // UNASSIGNED
+        C_U,                        // 0x0A4A       // UNASSIGNED
+        C_M,                        // 0x0A4B       // OO
+        C_M,                        // 0x0A4C       // AU
+        C_H,                        // 0x0A4D       // VIRAMA (HALANT)
+        C_U,                        // 0x0A4E       // UNASSIGNED
+        C_U,                        // 0x0A4F       // UNASSIGNED
+        C_U,                        // 0x0A50       // UNASSIGNED
+        C_T,                        // 0x0A51       // UDATTA
+        C_U,                        // 0x0A52       // UNASSIGNED
+        C_U,                        // 0x0A53       // UNASSIGNED
+        C_U,                        // 0x0A54       // UNASSIGNED
+        C_U,                        // 0x0A55       // UNASSIGNED
+        C_U,                        // 0x0A56       // UNASSIGNED
+        C_U,                        // 0x0A57       // UNASSIGNED
+        C_U,                        // 0x0A58       // UNASSIGNED
+        C_C|C_N,                    // 0x0A59       // KHHA                                                             // CSOK: WhitespaceAround
+        C_C|C_N,                    // 0x0A5A       // GHHA                                                             // CSOK: WhitespaceAround
+        C_C|C_N,                    // 0x0A5B       // ZA                                                               // CSOK: WhitespaceAround
+        C_C|C_N,                    // 0x0A5C       // RRA                                                              // CSOK: WhitespaceAround
+        C_U,                        // 0x0A5D       // UNASSIGNED
+        C_C|C_N,                    // 0x0A5E       // FA                                                               // CSOK: WhitespaceAround
+        C_U,                        // 0x0A5F       // UNASSIGNED
+        C_U,                        // 0x0A60       // UNASSIGNED
+        C_U,                        // 0x0A61       // UNASSIGNED
+        C_U,                        // 0x0A62       // UNASSIGNED
+        C_U,                        // 0x0A63       // UNASSIGNED
+        C_U,                        // 0x0A64       // UNASSIGNED
+        C_U,                        // 0x0A65       // UNASSIGNED
+        C_D,                        // 0x0A66       // ZERO
+        C_D,                        // 0x0A67       // ONE
+        C_D,                        // 0x0A68       // TWO
+        C_D,                        // 0x0A69       // THREE
+        C_D,                        // 0x0A6A       // FOUR
+        C_D,                        // 0x0A6B       // FIVE
+        C_D,                        // 0x0A6C       // SIX
+        C_D,                        // 0x0A6D       // SEVEN
+        C_D,                        // 0x0A6E       // EIGHT
+        C_D,                        // 0x0A6F       // NINE
+        C_O,                        // 0x0A70       // TIPPI
+        C_O,                        // 0x0A71       // ADDAK
+        C_V,                        // 0x0A72       // IRI
+        C_V,                        // 0x0A73       // URA
+        C_S,                        // 0x0A74       // EK ONKAR
+        C_O,                        // 0x0A75       // YAKASH
+        C_U,                        // 0x0A76       // UNASSIGNED
+        C_U,                        // 0x0A77       // UNASSIGNED
+        C_U,                        // 0x0A78       // UNASSIGNED
+        C_U,                        // 0x0A79       // UNASSIGNED
+        C_U,                        // 0x0A7A       // UNASSIGNED
+        C_U,                        // 0x0A7B       // UNASSIGNED
+        C_U,                        // 0x0A7C       // UNASSIGNED
+        C_U,                        // 0x0A7D       // UNASSIGNED
+        C_U,                        // 0x0A7E       // UNASSIGNED
+        C_U                         // 0x0A7F       // UNASSIGNED
+    };
+    static int typeOf(int c) {
+        if ( ( c >= ccaStart ) && ( c < ccaEnd ) ) {
+            return cca [ c - ccaStart ] & C_M_TYPE;
+        } else {
+            return C_U;
+        }
+    }
+    static boolean isType(int c, int t) {
+        return typeOf ( c ) == t;
+    }
+    static boolean hasFlag(int c, int f) {
+        if ( ( c >= ccaStart ) && ( c < ccaEnd ) ) {
+            return ( cca [ c - ccaStart ] & f ) == f;
+        } else {
+            return false;
+        }
+    }
+    static boolean isC(int c) {
+        return isType(c,C_C);
+    }
+    static boolean isR(int c) {
+        return isType(c,C_C) && hasR(c);
+    }
+    static boolean isV(int c) {
+        return isType(c,C_V);
+    }
+    static boolean isN(int c) {
+        return c == 0x0A3C;
+    }
+    static boolean isH(int c) {
+        return c == 0x0A4D;
+    }
+    static boolean isM(int c) {
+        return isType(c,C_M);
+    }
+    static boolean isPreM(int c) {
+        return isType(c,C_M) && hasFlag(c,C_PRE);
+    }
+    static boolean isX(int c) {
+        switch ( typeOf ( c ) ) {
+        case C_M: // matra (combining vowel)
+        case C_A: // accent mark
+        case C_T: // tone mark
+        case C_O: // other (modifying) mark
+            return true;
+        default:
+            return false;
+        }
+    }
+    static boolean hasR(int c) {
+        return hasFlag(c,C_R);
+    }
+    static boolean hasN(int c) {
+        return hasFlag(c,C_N);
+    }
+
+    @Override
+    public GlyphSequence reorderCombiningMarks ( GlyphDefinitionTable gdef, GlyphSequence gs, int[][] gpa, String script, String language ) {
+        return super.reorderCombiningMarks ( gdef, gs, gpa, script, language );
+    }
+
+}

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java?rev=1293736&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java Sun Feb 26 02:29:01 2012
@@ -0,0 +1,589 @@
+/*
+ * 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.scripts;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.complexscripts.fonts.GlyphTable;
+import org.apache.fop.complexscripts.util.CharScript;
+import org.apache.fop.complexscripts.util.GlyphContextTester;
+import org.apache.fop.complexscripts.util.GlyphSequence;
+import org.apache.fop.complexscripts.util.ScriptContextTester;
+
+// CSOFF: AvoidNestedBlocksCheck
+// CSOFF: NoWhitespaceAfterCheck
+// CSOFF: InnerAssignmentCheck
+// CSOFF: SimplifyBooleanReturnCheck
+// CSOFF: EmptyForIteratorPadCheck
+// CSOFF: WhitespaceAfterCheck
+// CSOFF: ParameterNumberCheck
+// CSOFF: LineLengthCheck
+
+/**
+ * <p>The <code>IndicScriptProcessor</code> class implements a script processor for
+ * performing glyph substitution and positioning operations on content associated with the Indic script.</p>
+ * @author Glenn Adams
+ */
+public class IndicScriptProcessor extends DefaultScriptProcessor {
+
+    /** logging instance */
+    private static final Log log = LogFactory.getLog(IndicScriptProcessor.class);                                      // CSOK: ConstantNameCheck
+
+    /** required features to use for substitutions */
+    private static final String[] gsubReqFeatures =                                                                    // CSOK: ConstantNameCheck
+    {
+        "abvf",                                                 // above base forms
+        "abvs",                                                 // above base substitutions
+        "akhn",                                                 // akhand
+        "blwf",                                                 // below base forms
+        "blws",                                                 // below base substitutions
+        "ccmp",                                                 // glyph composition/decomposition
+        "cjct",                                                 // conjunct forms
+        "clig",                                                 // contextual ligatures
+        "half",                                                 // half forms
+        "haln",                                                 // halant forms
+        "locl",                                                 // localized forms
+        "nukt",                                                 // nukta forms
+        "pref",                                                 // pre-base forms
+        "pres",                                                 // pre-base substitutions
+        "pstf",                                                 // post-base forms
+        "psts",                                                 // post-base substitutions
+        "rkrf",                                                 // rakar forms
+        "rphf",                                                 // reph form
+        "vatu"                                                  // vattu variants
+    };
+
+    /** optional features to use for substitutions */
+    private static final String[] gsubOptFeatures =                                                                     // CSOK: ConstantNameCheck
+    {
+        "afrc",                                                 // alternative fractions
+        "calt",                                                 // contextual alternatives
+        "dlig"                                                  // discretionary ligatures
+    };
+
+    /** required features to use for positioning */
+    private static final String[] gposReqFeatures =                                                                     // CSOK: ConstantNameCheck
+    {
+        "abvm",                                                 // above base marks
+        "blwm",                                                 // below base marks
+        "dist",                                                 // distance (adjustment)
+        "kern"                                                  // kerning
+    };
+
+    /** required features to use for positioning */
+    private static final String[] gposOptFeatures =                                                                     // CSOK: ConstantNameCheck
+    {
+    };
+
+    private static class SubstitutionScriptContextTester implements ScriptContextTester {
+        private static Map/*<String,GlyphContextTester>*/ testerMap = new HashMap/*<String,GlyphContextTester>*/();
+        public GlyphContextTester getTester ( String feature ) {
+            return (GlyphContextTester) testerMap.get ( feature );
+        }
+    }
+
+    private static class PositioningScriptContextTester implements ScriptContextTester {
+        private static Map/*<String,GlyphContextTester>*/ testerMap = new HashMap/*<String,GlyphContextTester>*/();
+        public GlyphContextTester getTester ( String feature ) {
+            return (GlyphContextTester) testerMap.get ( feature );
+        }
+    }
+
+    /**
+     * Make script specific flavor of Indic script processor.
+     * @param script tag
+     * @return script processor instance
+     */
+    public static ScriptProcessor makeProcessor ( String script ) {
+        switch ( CharScript.scriptCodeFromTag ( script ) ) {
+        case CharScript.SCRIPT_DEVANAGARI:
+        case CharScript.SCRIPT_DEVANAGARI_2:
+            return new DevanagariScriptProcessor ( script );
+        case CharScript.SCRIPT_GUJARATI:
+        case CharScript.SCRIPT_GUJARATI_2:
+            return new GujaratiScriptProcessor ( script );
+        case CharScript.SCRIPT_GURMUKHI:
+        case CharScript.SCRIPT_GURMUKHI_2:
+            return new GurmukhiScriptProcessor ( script );
+        // [TBD] implement other script processors
+        default:
+            return new IndicScriptProcessor ( script );
+        }
+    }
+
+    private final ScriptContextTester subContextTester;
+    private final ScriptContextTester posContextTester;
+
+    IndicScriptProcessor ( String script ) {
+        super ( script );
+        this.subContextTester = new SubstitutionScriptContextTester();
+        this.posContextTester = new PositioningScriptContextTester();
+    }
+
+    /** {@inheritDoc} */
+    public String[] getSubstitutionFeatures() {
+        return gsubReqFeatures;
+    }
+
+    /** {@inheritDoc} */
+    public String[] getOptionalSubstitutionFeatures() {
+        return gsubOptFeatures;
+    }
+
+    /** {@inheritDoc} */
+    public ScriptContextTester getSubstitutionContextTester() {
+        return subContextTester;
+    }
+
+    /** {@inheritDoc} */
+    public String[] getPositioningFeatures() {
+        return gposReqFeatures;
+    }
+
+    /** {@inheritDoc} */
+    public String[] getOptionalPositioningFeatures() {
+        return gposOptFeatures;
+    }
+
+    /** {@inheritDoc} */
+    public ScriptContextTester getPositioningContextTester() {
+        return posContextTester;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public GlyphSequence substitute ( GlyphSequence gs, String script, String language, GlyphTable.UseSpec[] usa, ScriptContextTester sct ) {
+        assert usa != null;
+        // 1. syllabize
+        GlyphSequence[] sa = syllabize ( gs, script, language );
+        // 2. process each syllable
+        for ( int i = 0, n = sa.length; i < n; i++ ) {
+            GlyphSequence s = sa [ i ];
+            // apply basic shaping subs
+            for ( int j = 0, m = usa.length; j < m; j++ ) {
+                GlyphTable.UseSpec us = usa [ j ];
+                if ( isBasicShapingUse ( us ) ) {
+                    s.setPredications ( true );
+                    s = us.substitute ( s, script, language, sct );
+                }
+            }
+            // reorder pre-base matra
+            s = reorderPreBaseMatra ( s );
+            // reorder reph
+            s = reorderReph ( s );
+            // apply presentation subs
+            for ( int j = 0, m = usa.length; j < m; j++ ) {
+                GlyphTable.UseSpec us = usa [ j ];
+                if ( isPresentationUse ( us ) ) {
+                    s.setPredications ( true );
+                    s = us.substitute ( s, script, language, sct );
+                }
+            }
+            // record result
+            sa [ i ] = s;
+        }
+        // 3. return reassembled substituted syllables
+        return unsyllabize ( gs, sa );
+    }
+
+    /**
+     * Get script specific syllabizer class.
+     * @return a syllabizer class object or null
+     */
+    protected Class<? extends Syllabizer> getSyllabizerClass() {
+        return null;
+    }
+
+    private GlyphSequence[] syllabize ( GlyphSequence gs, String script, String language ) {
+        return Syllabizer.getSyllabizer ( script, language, getSyllabizerClass() ) . syllabize ( gs );
+    }
+
+    private GlyphSequence unsyllabize ( GlyphSequence gs, GlyphSequence[] sa ) {
+        return GlyphSequence.join ( gs, sa );
+    }
+
+    private static Set<String> basicShapingFeatures;
+    private static final String[] basicShapingFeatureStrings = {                                                        // CSOK: ConstantNameCheck
+        "abvf",
+        "akhn",
+        "blwf",
+        "cjct",
+        "half",
+        "locl",
+        "nukt",
+        "pref",
+        "pstf",
+        "rkrf",
+        "rphf",
+        "vatu",
+    };
+    static {
+        basicShapingFeatures = new HashSet<String>();
+        for ( String s : basicShapingFeatureStrings ) {
+            basicShapingFeatures.add ( s );
+        }
+    }
+    private boolean isBasicShapingUse ( GlyphTable.UseSpec us ) {
+        assert us != null;
+        if ( basicShapingFeatures != null ) {
+            return basicShapingFeatures.contains ( us.getFeature() );
+        } else {
+            return false;
+        }
+    }
+
+    private static  Set<String> presentationFeatures;
+    private static final String[] presentationFeatureStrings = {                                                        // CSOK: ConstantNameCheck
+        "abvs",
+        "blws",
+        "calt",
+        "haln",
+        "pres",
+        "psts",
+    };
+    static {
+        presentationFeatures = new HashSet<String>();
+        for ( String s : presentationFeatureStrings ) {
+            presentationFeatures.add ( s );
+        }
+    }
+    private boolean isPresentationUse ( GlyphTable.UseSpec us ) {
+        assert us != null;
+        if ( presentationFeatures != null ) {
+            return presentationFeatures.contains ( us.getFeature() );
+        } else {
+            return false;
+        }
+    }
+
+    private GlyphSequence reorderPreBaseMatra ( GlyphSequence gs ) {
+        int source;
+        if ( ( source = findPreBaseMatra ( gs ) ) >= 0 ) {
+            int target;
+            if ( ( target = findPreBaseMatraTarget ( gs, source ) ) >= 0 ) {
+                if ( target != source ) {
+                    gs = reorder ( gs, source, target );
+                }
+            }
+        }
+        return gs;
+    }
+
+    /**
+     * Find pre-base matra in sequence.
+     * @param gs input sequence
+     * @return index of pre-base matra or -1 if not found
+     */
+    protected int findPreBaseMatra ( GlyphSequence gs ) {
+        return -1;
+    }
+
+    /**
+     * Find pre-base matra target in sequence.
+     * @param gs input sequence
+     * @param source index of pre-base matra
+     * @return index of pre-base matra target or -1
+     */
+    protected int findPreBaseMatraTarget ( GlyphSequence gs, int source ) {
+        return -1;
+    }
+
+    private GlyphSequence reorderReph ( GlyphSequence gs ) {
+        int source;
+        if ( ( source = findReph ( gs ) ) >= 0 ) {
+            int target;
+            if ( ( target = findRephTarget ( gs, source ) ) >= 0 ) {
+                if ( target != source ) {
+                    gs = reorder ( gs, source, target );
+                }
+            }
+        }
+        return gs;
+    }
+
+    /**
+     * Find reph in sequence.
+     * @param gs input sequence
+     * @return index of reph or -1 if not found
+     */
+    protected int findReph ( GlyphSequence gs ) {
+        return -1;
+    }
+
+    /**
+     * Find reph target in sequence.
+     * @param gs input sequence
+     * @param source index of reph
+     * @return index of reph target or -1
+     */
+    protected int findRephTarget ( GlyphSequence gs, int source ) {
+        return -1;
+    }
+
+    private GlyphSequence reorder ( GlyphSequence gs, int source, int target ) {
+        return GlyphSequence.reorder ( gs, source, 1, target );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean position ( GlyphSequence gs, String script, String language, int fontSize, GlyphTable.UseSpec[] usa, int[] widths, int[][] adjustments, ScriptContextTester sct ) {
+        boolean adjusted = super.position ( gs, script, language, fontSize, usa, widths, adjustments, sct );
+        return adjusted;
+    }
+
+    /** Abstract syllabizer. */
+    protected abstract static class Syllabizer {
+        private String script;
+        private String language;
+        Syllabizer ( String script, String language ) {
+            this.script = script;
+            this.language = language;
+        }
+        /**
+         * Subdivide glyph sequence GS into syllabic segments each represented by a distinct
+         * output glyph sequence.
+         * @param gs input glyph sequence
+         * @return segmented syllabic glyph sequences
+         */
+        abstract GlyphSequence[] syllabize ( GlyphSequence gs );
+        /** {@inheritDoc} */
+        public int hashCode() {
+            int hc = 0;
+            hc =  7 * hc + ( hc ^ script.hashCode() );
+            hc = 11 * hc + ( hc ^ language.hashCode() );
+            return hc;
+        }
+        /** {@inheritDoc} */
+        public boolean equals ( Object o ) {
+            if ( o instanceof Syllabizer ) {
+                Syllabizer s = (Syllabizer) o;
+                if ( ! s.script.equals ( script ) ) {
+                    return false;
+                } else if ( ! s.language.equals ( language ) ) {
+                    return false;
+                } else {
+                    return true;
+                }
+            } else {
+                return false;
+            }
+        }
+        /** {@inheritDoc} */
+        public int compareTo ( Object o ) {
+            int d;
+            if ( o instanceof Syllabizer ) {
+                Syllabizer s = (Syllabizer) o;
+                if ( ( d = script.compareTo ( s.script ) ) == 0 ) {
+                    d = language.compareTo ( s.language );
+                }
+            } else {
+                d = -1;
+            }
+            return d;
+        }
+        private static Map<String,Syllabizer> syllabizers = new HashMap<String,Syllabizer>();
+        static Syllabizer getSyllabizer ( String script, String language, Class<? extends Syllabizer> syllabizerClass ) {
+            String sid = makeSyllabizerId ( script, language );
+            Syllabizer s = syllabizers.get ( sid );
+            if ( s == null ) {
+                if ( ( s = makeSyllabizer ( script, language, syllabizerClass ) ) == null ) {
+                    s = new DefaultSyllabizer ( script, language );
+                }
+                syllabizers.put ( sid, s );
+            }
+            return s;
+        }
+        static String makeSyllabizerId ( String script, String language ) {
+            return script + ":" + language;
+        }
+        static Syllabizer makeSyllabizer ( String script, String language, Class<? extends Syllabizer> syllabizerClass ) {
+            Syllabizer s;
+            try {
+                Constructor<? extends Syllabizer> cf = syllabizerClass.getDeclaredConstructor ( new Class[] { String.class, String.class } );
+                s = (Syllabizer) cf.newInstance ( script, language );
+            } catch ( NoSuchMethodException e ) {
+                s = null;
+            } catch ( InstantiationException e ) {
+                s = null;
+            } catch ( IllegalAccessException e ) {
+                s = null;
+            } catch ( InvocationTargetException e ) {
+                s = null;
+            }
+            return s;
+        }
+    }
+
+    /** Default syllabizer. */
+    protected static class DefaultSyllabizer extends Syllabizer {
+        DefaultSyllabizer ( String script, String language ) {
+            super ( script, language );
+        }
+        /** {@inheritDoc} */
+        @Override
+        GlyphSequence[] syllabize ( GlyphSequence gs ) {
+            int[] ca = gs.getCharacterArray ( false );
+            int   nc = gs.getCharacterCount();
+            if ( nc == 0 ) {
+                return new GlyphSequence[] { gs };
+            } else {
+                return segmentize ( gs, segmentize ( ca, nc ) );
+            }
+        }
+        /**
+         * Construct array of segements from original character array (associated with original glyph sequence)
+         * @param ca input character sequence
+         * @param nc number of characters in sequence
+         * @return array of syllable segments
+         */
+        protected Segment[] segmentize ( int[] ca, int nc ) {
+            Vector<Segment> sv = new Vector<Segment> ( nc );
+            for ( int s = 0, e = nc; s < e; ) {
+                int i;
+                if ( ( i = findStartOfSyllable ( ca, s, e ) ) > s ) {
+                    // from s to i is non-syllable segment
+                    sv.add ( new Segment ( s, i, Segment.OTHER ) );
+                    s = i; // move s to start of syllable
+                } else if ( i > s ) {
+                    // from s to e is non-syllable segment
+                    sv.add ( new Segment ( s, e, Segment.OTHER ) );
+                    s = e; // move s to end of input sequence
+                }
+                if ( ( i = findEndOfSyllable ( ca, s, e ) ) > s ) {
+                    // from s to i is syllable segment
+                    sv.add ( new Segment ( s, i, Segment.SYLLABLE ) );
+                    s = i; // move s to end of syllable
+                } else {
+                    // from s to e is non-syllable segment
+                    sv.add ( new Segment ( s, e, Segment.OTHER ) );
+                    s = e; // move s to end of input sequence
+                }
+            }
+            return sv.toArray ( new Segment [ sv.size() ] );
+        }
+        /**
+         * Construct array of glyph sequences from original glyph sequence and segment array.
+         * @param gs original input glyph sequence
+         * @param sa segment array
+         * @return array of glyph sequences each belonging to an (ordered) segment in SA
+         */
+        protected GlyphSequence[] segmentize ( GlyphSequence gs, Segment[] sa ) {
+            int   ng = gs.getGlyphCount();
+            int[] ga = gs.getGlyphArray ( false );
+            GlyphSequence.CharAssociation[] aa = gs.getAssociations ( 0, -1 );
+            Vector<GlyphSequence> nsv = new Vector<GlyphSequence>();
+            for ( int i = 0, ns = sa.length; i < ns; i++ ) {
+                Segment s = sa [ i ];
+                Vector<Integer> ngv = new Vector<Integer> ( ng );
+                Vector<GlyphSequence.CharAssociation> nav = new Vector<GlyphSequence.CharAssociation> ( ng );
+                for ( int j = 0; j < ng; j++ ) {
+                    GlyphSequence.CharAssociation ca = aa [ j ];
+                    if ( ca.contained ( s.getOffset(), s.getCount() ) ) {
+                        ngv.add ( ga [ j ] );
+                        nav.add ( ca );
+                    }
+                }
+                if ( ngv.size() > 0 ) {
+                    nsv.add ( new GlyphSequence ( gs, null, toIntArray ( ngv ), null, null, nav.toArray ( new GlyphSequence.CharAssociation [ nav.size() ] ), null ) );
+                }
+            }
+            if ( nsv.size() > 0 ) {
+                return nsv.toArray ( new GlyphSequence [ nsv.size() ] );
+            } else {
+                return new GlyphSequence[] { gs };
+            }
+        }
+        /**
+         * Find start of syllable in character array, starting at S, ending at E.
+         * @param ca character array
+         * @param s start index
+         * @param e end index
+         * @return index of start or E if no start found
+         */
+        protected int findStartOfSyllable ( int[] ca, int s, int e ) {
+            return e;
+        }
+        /**
+         * Find end of syllable in character array, starting at S, ending at E.
+         * @param ca character array
+         * @param s start index
+         * @param e end index
+         * @return index of start or S if no end found
+         */
+        protected int findEndOfSyllable ( int[] ca, int s, int e ) {
+            return s;
+        }
+        private static int[] toIntArray ( Vector<Integer> iv ) {
+            int ni = iv.size();
+            int[] ia = new int [ iv.size() ];
+            for ( int i = 0, n = ni; i < n; i++ ) {
+                ia [ i ] = (int) iv.get ( i );
+            }
+            return ia;
+        }
+    }
+
+    /** Syllabic segment. */
+    protected static class Segment {
+
+        static final int OTHER = 0;            // other (non-syllable) characters
+        static final int SYLLABLE = 1;         // (orthographic) syllable
+
+        private int start;
+        private int end;
+        private int type;
+
+        Segment ( int start, int end, int type ) {
+            this.start = start;
+            this.end = end;
+            this.type = type;
+        }
+
+        int getStart() {
+            return start;
+        }
+
+        int getEnd() {
+            return end;
+        }
+
+        int getOffset() {
+            return start;
+        }
+
+        int getCount() {
+            return end - start;
+        }
+
+        int getType() {
+            return type;
+        }
+    }
+}

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java?rev=1293736&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java Sun Feb 26 02:29:01 2012
@@ -0,0 +1,234 @@
+/*
+ * 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.scripts;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.fop.complexscripts.fonts.GlyphDefinitionTable;
+import org.apache.fop.complexscripts.fonts.GlyphPositioningTable;
+import org.apache.fop.complexscripts.fonts.GlyphSubstitutionTable;
+import org.apache.fop.complexscripts.fonts.GlyphTable;
+import org.apache.fop.complexscripts.util.CharScript;
+import org.apache.fop.complexscripts.util.GlyphSequence;
+import org.apache.fop.complexscripts.util.ScriptContextTester;
+
+// CSOFF: InnerAssignmentCheck
+// CSOFF: LineLengthCheck
+// CSOFF: ParameterNumberCheck
+
+/**
+ * Abstract script processor base class for which an implementation of the substitution and positioning methods
+ * must be supplied.
+ * @author Glenn Adams
+ */
+public abstract class ScriptProcessor {
+
+    private final String script;
+
+    private static Map<String, ScriptProcessor> processors = new HashMap<String, ScriptProcessor>();
+
+    /**
+     * Instantiate a script processor.
+     * @param script a script identifier
+     */
+    protected ScriptProcessor ( String script ) {
+        if ( ( script == null ) || ( script.length() == 0 ) ) {
+            throw new IllegalArgumentException ( "script must be non-empty string" );
+        } else {
+            this.script = script;
+        }
+    }
+
+    /** @return script identifier */
+    public final String getScript() {
+        return script;
+    }
+
+    /**
+     * Obtain script specific required substitution features.
+     * @return array of suppported substitution features or null
+     */
+    public abstract String[] getSubstitutionFeatures();
+
+    /**
+     * Obtain script specific optional substitution features.
+     * @return array of suppported substitution features or null
+     */
+    public String[] getOptionalSubstitutionFeatures() {
+        return new String[0];
+    }
+
+    /**
+     * Obtain script specific substitution context tester.
+     * @return substitution context tester or null
+     */
+    public abstract ScriptContextTester getSubstitutionContextTester();
+
+    /**
+     * Perform substitution processing using a specific set of lookup tables.
+     * @param gsub the glyph substitution table that applies
+     * @param gs an input glyph sequence
+     * @param script a script identifier
+     * @param language a language identifier
+     * @param lookups a mapping from lookup specifications to glyph subtables to use for substitution processing
+     * @return the substituted (output) glyph sequence
+     */
+    public final GlyphSequence substitute ( GlyphSubstitutionTable gsub, GlyphSequence gs, String script, String language, Map/*<LookupSpec,List<LookupTable>>>*/ lookups ) {
+        return substitute ( gs, script, language, assembleLookups ( gsub, getSubstitutionFeatures(), lookups ), getSubstitutionContextTester() );
+    }
+
+    /**
+     * Perform substitution processing using a specific set of ordered glyph table use specifications.
+     * @param gs an input glyph sequence
+     * @param script a script identifier
+     * @param language a language identifier
+     * @param usa an ordered array of glyph table use specs
+     * @param sct a script specific context tester (or null)
+     * @return the substituted (output) glyph sequence
+     */
+    public GlyphSequence substitute ( GlyphSequence gs, String script, String language, GlyphTable.UseSpec[] usa, ScriptContextTester sct ) {
+        assert usa != null;
+        for ( int i = 0, n = usa.length; i < n; i++ ) {
+            GlyphTable.UseSpec us = usa [ i ];
+            gs = us.substitute ( gs, script, language, sct );
+        }
+        return gs;
+    }
+
+    /**
+     * Reorder combining marks in glyph sequence so that they precede (within the sequence) the base
+     * character to which they are applied. N.B. In the case of RTL segments, marks are not reordered by this,
+     * method since when the segment is reversed by BIDI processing, marks are automatically reordered to precede
+     * their base glyph.
+     * @param gdef the glyph definition table that applies
+     * @param gs an input glyph sequence
+     * @param gpa associated glyph position adjustments (also reordered)
+     * @param script a script identifier
+     * @param language a language identifier
+     * @return the reordered (output) glyph sequence
+     */
+    public GlyphSequence reorderCombiningMarks ( GlyphDefinitionTable gdef, GlyphSequence gs, int[][] gpa, String script, String language ) {
+        return gs;
+    }
+
+    /**
+     * Obtain script specific required positioning features.
+     * @return array of suppported positioning features or null
+     */
+    public abstract String[] getPositioningFeatures();
+
+    /**
+     * Obtain script specific optional positioning features.
+     * @return array of suppported positioning features or null
+     */
+    public String[] getOptionalPositioningFeatures() {
+        return new String[0];
+    }
+
+    /**
+     * Obtain script specific positioning context tester.
+     * @return positioning context tester or null
+     */
+    public abstract ScriptContextTester getPositioningContextTester();
+
+    /**
+     * Perform positioning processing using a specific set of lookup tables.
+     * @param gpos the glyph positioning table that applies
+     * @param gs an input glyph sequence
+     * @param script a script identifier
+     * @param language a language identifier
+     * @param fontSize size in device units
+     * @param lookups a mapping from lookup specifications to glyph subtables to use for positioning processing
+     * @param widths array of default advancements for each glyph
+     * @param adjustments accumulated adjustments array (sequence) of 4-tuples of placement [PX,PY] and advance [AX,AY] adjustments, in that order,
+     * with one 4-tuple for each element of glyph sequence
+     * @return true if some adjustment is not zero; otherwise, false
+     */
+    public final boolean position ( GlyphPositioningTable gpos, GlyphSequence gs, String script, String language, int fontSize, Map/*<LookupSpec,List<LookupTable>>*/ lookups, int[] widths, int[][] adjustments ) {
+        return position ( gs, script, language, fontSize, assembleLookups ( gpos, getPositioningFeatures(), lookups ), widths, adjustments, getPositioningContextTester() );
+    }
+
+    /**
+     * Perform positioning processing using a specific set of ordered glyph table use specifications.
+     * @param gs an input glyph sequence
+     * @param script a script identifier
+     * @param language a language identifier
+     * @param fontSize size in device units
+     * @param usa an ordered array of glyph table use specs
+     * @param widths array of default advancements for each glyph in font
+     * @param adjustments accumulated adjustments array (sequence) of 4-tuples of placement [PX,PY] and advance [AX,AY] adjustments, in that order,
+     * with one 4-tuple for each element of glyph sequence
+     * @param sct a script specific context tester (or null)
+     * @return true if some adjustment is not zero; otherwise, false
+     */
+    public boolean position ( GlyphSequence gs, String script, String language, int fontSize, GlyphTable.UseSpec[] usa, int[] widths, int[][] adjustments, ScriptContextTester sct ) {
+        assert usa != null;
+        boolean adjusted = false;
+        for ( int i = 0, n = usa.length; i < n; i++ ) {
+            GlyphTable.UseSpec us = usa [ i ];
+            if ( us.position ( gs, script, language, fontSize, widths, adjustments, sct ) ) {
+                adjusted = true;
+            }
+        }
+        return adjusted;
+    }
+
+    /**
+     * Assemble ordered array of lookup table use specifications according to the specified features and candidate lookups,
+     * where the order of the array is in accordance to the order of the applicable lookup list.
+     * @param table the governing glyph table
+     * @param features array of feature identifiers to apply
+     * @param lookups a mapping from lookup specifications to lists of look tables from which to select lookup tables according to the specified features
+     * @return ordered array of assembled lookup table use specifications
+     */
+    public final GlyphTable.UseSpec[] assembleLookups ( GlyphTable table, String[] features, Map/*<LookupSpec,List<LookupTable>>*/ lookups ) {
+        return table.assembleLookups ( features, lookups );
+    }
+
+    /**
+     * Obtain script processor instance associated with specified script.
+     * @param script a script identifier
+     * @return a script processor instance or null if none found
+     */
+    public static synchronized ScriptProcessor getInstance ( String script ) {
+        ScriptProcessor sp = null;
+        assert processors != null;
+        if ( ( sp = processors.get ( script ) ) == null ) {
+            processors.put ( script, sp = createProcessor ( script ) );
+        }
+        return sp;
+    }
+
+    // [TBD] - rework to provide more configurable binding between script name and script processor constructor
+    private static ScriptProcessor createProcessor ( String script ) {
+        ScriptProcessor sp = null;
+        int sc = CharScript.scriptCodeFromTag ( script );
+        if ( sc == CharScript.SCRIPT_ARABIC ) {
+            sp = new ArabicScriptProcessor ( script );
+        } else if ( CharScript.isIndicScript ( sc ) ) {
+            sp = IndicScriptProcessor.makeProcessor ( script );
+        } else {
+            sp = new DefaultScriptProcessor ( script );
+        }
+        return sp;
+    }
+
+}

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/util/CharMirror.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/util/CharMirror.java?rev=1293736&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/util/CharMirror.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/util/CharMirror.java Sun Feb 26 02:29:01 2012
@@ -0,0 +1,715 @@
+/*
+ * 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.util;
+
+import java.util.Arrays;
+
+/**
+ * Mirror related utilities.
+ * @author Glenn Adams
+ */
+public final class CharMirror {
+
+    private CharMirror() {
+    }
+
+    /**
+     * Mirror characters that are designated as having the bidi mirrorred property.
+     * @param s a string whose characters are to be mirrored
+     * @return the resulting string
+     */
+    public static String mirror ( String s ) {
+        StringBuffer sb = new StringBuffer ( s );
+        for ( int i = 0, n = sb.length(); i < n; i++ ) {
+            sb.setCharAt ( i, (char) mirror ( sb.charAt ( i ) ) );
+        }
+        return sb.toString();
+    }
+
+    private static int[] mirroredCharacters = {
+        0x0028,
+        0x0029,
+        0x003C,
+        0x003E,
+        0x005B,
+        0x005D,
+        0x007B,
+        0x007D,
+        0x00AB,
+        0x00BB,
+        0x0F3A,
+        0x0F3B,
+        0x0F3C,
+        0x0F3D,
+        0x169B,
+        0x169C,
+        0x2039,
+        0x203A,
+        0x2045,
+        0x2046,
+        0x207D,
+        0x207E,
+        0x208D,
+        0x208E,
+        0x2208,
+        0x2209,
+        0x220A,
+        0x220B,
+        0x220C,
+        0x220D,
+        0x2215,
+        0x223C,
+        0x223D,
+        0x2243,
+        0x2252,
+        0x2253,
+        0x2254,
+        0x2255,
+        0x2264,
+        0x2265,
+        0x2266,
+        0x2267,
+        0x2268,
+        0x2269,
+        0x226A,
+        0x226B,
+        0x226E,
+        0x226F,
+        0x2270,
+        0x2271,
+        0x2272,
+        0x2273,
+        0x2274,
+        0x2275,
+        0x2276,
+        0x2277,
+        0x2278,
+        0x2279,
+        0x227A,
+        0x227B,
+        0x227C,
+        0x227D,
+        0x227E,
+        0x227F,
+        0x2280,
+        0x2281,
+        0x2282,
+        0x2283,
+        0x2284,
+        0x2285,
+        0x2286,
+        0x2287,
+        0x2288,
+        0x2289,
+        0x228A,
+        0x228B,
+        0x228F,
+        0x2290,
+        0x2291,
+        0x2292,
+        0x2298,
+        0x22A2,
+        0x22A3,
+        0x22A6,
+        0x22A8,
+        0x22A9,
+        0x22AB,
+        0x22B0,
+        0x22B1,
+        0x22B2,
+        0x22B3,
+        0x22B4,
+        0x22B5,
+        0x22B6,
+        0x22B7,
+        0x22C9,
+        0x22CA,
+        0x22CB,
+        0x22CC,
+        0x22CD,
+        0x22D0,
+        0x22D1,
+        0x22D6,
+        0x22D7,
+        0x22D8,
+        0x22D9,
+        0x22DA,
+        0x22DB,
+        0x22DC,
+        0x22DD,
+        0x22DE,
+        0x22DF,
+        0x22E0,
+        0x22E1,
+        0x22E2,
+        0x22E3,
+        0x22E4,
+        0x22E5,
+        0x22E6,
+        0x22E7,
+        0x22E8,
+        0x22E9,
+        0x22EA,
+        0x22EB,
+        0x22EC,
+        0x22ED,
+        0x22F0,
+        0x22F1,
+        0x22F2,
+        0x22F3,
+        0x22F4,
+        0x22F6,
+        0x22F7,
+        0x22FA,
+        0x22FB,
+        0x22FC,
+        0x22FD,
+        0x22FE,
+        0x2308,
+        0x2309,
+        0x230A,
+        0x230B,
+        0x2329,
+        0x232A,
+        0x2768,
+        0x2769,
+        0x276A,
+        0x276B,
+        0x276C,
+        0x276D,
+        0x276E,
+        0x276F,
+        0x2770,
+        0x2771,
+        0x2772,
+        0x2773,
+        0x2774,
+        0x2775,
+        0x27C3,
+        0x27C4,
+        0x27C5,
+        0x27C6,
+        0x27C8,
+        0x27C9,
+        0x27D5,
+        0x27D6,
+        0x27DD,
+        0x27DE,
+        0x27E2,
+        0x27E3,
+        0x27E4,
+        0x27E5,
+        0x27E6,
+        0x27E7,
+        0x27E8,
+        0x27E9,
+        0x27EA,
+        0x27EB,
+        0x27EC,
+        0x27ED,
+        0x27EE,
+        0x27EF,
+        0x2983,
+        0x2984,
+        0x2985,
+        0x2986,
+        0x2987,
+        0x2988,
+        0x2989,
+        0x298A,
+        0x298B,
+        0x298C,
+        0x298D,
+        0x298E,
+        0x298F,
+        0x2990,
+        0x2991,
+        0x2992,
+        0x2993,
+        0x2994,
+        0x2995,
+        0x2996,
+        0x2997,
+        0x2998,
+        0x29B8,
+        0x29C0,
+        0x29C1,
+        0x29C4,
+        0x29C5,
+        0x29CF,
+        0x29D0,
+        0x29D1,
+        0x29D2,
+        0x29D4,
+        0x29D5,
+        0x29D8,
+        0x29D9,
+        0x29DA,
+        0x29DB,
+        0x29F5,
+        0x29F8,
+        0x29F9,
+        0x29FC,
+        0x29FD,
+        0x2A2B,
+        0x2A2C,
+        0x2A2D,
+        0x2A2E,
+        0x2A34,
+        0x2A35,
+        0x2A3C,
+        0x2A3D,
+        0x2A64,
+        0x2A65,
+        0x2A79,
+        0x2A7A,
+        0x2A7D,
+        0x2A7E,
+        0x2A7F,
+        0x2A80,
+        0x2A81,
+        0x2A82,
+        0x2A83,
+        0x2A84,
+        0x2A8B,
+        0x2A8C,
+        0x2A91,
+        0x2A92,
+        0x2A93,
+        0x2A94,
+        0x2A95,
+        0x2A96,
+        0x2A97,
+        0x2A98,
+        0x2A99,
+        0x2A9A,
+        0x2A9B,
+        0x2A9C,
+        0x2AA1,
+        0x2AA2,
+        0x2AA6,
+        0x2AA7,
+        0x2AA8,
+        0x2AA9,
+        0x2AAA,
+        0x2AAB,
+        0x2AAC,
+        0x2AAD,
+        0x2AAF,
+        0x2AB0,
+        0x2AB3,
+        0x2AB4,
+        0x2AC3,
+        0x2AC4,
+        0x2AC5,
+        0x2AC6,
+        0x2ACD,
+        0x2ACE,
+        0x2ACF,
+        0x2AD0,
+        0x2AD1,
+        0x2AD2,
+        0x2AD3,
+        0x2AD4,
+        0x2AD5,
+        0x2AD6,
+        0x2ADE,
+        0x2AE3,
+        0x2E02,
+        0x2E03,
+        0x2E04,
+        0x2E05,
+        0x2E09,
+        0x2E0A,
+        0x2E0C,
+        0x2E0D,
+        0x2E1C,
+        0x2E1D,
+        0x2E20,
+        0x2E21,
+        0x2E22,
+        0x2E23,
+        0x2E24,
+        0x2E25,
+        0x2E26,
+        0x300E,
+        0x300F,
+        0x3010,
+        0x3011,
+        0x3014,
+        0x3015,
+        0x3016,
+        0x3017,
+        0x3018,
+        0x3019,
+        0x301A,
+        0x301B,
+        0xFE59,
+        0xFE5A,
+        0xFF3B,
+        0xFF3D,
+        0xFF5B,
+        0xFF5D,
+        0xFF5F,
+        0xFF60,
+        0xFF62,
+        0xFF63
+    };
+
+    private static int[] mirroredCharactersMapping = {
+        0x0029,
+        0x0028,
+        0x003E,
+        0x003C,
+        0x005D,
+        0x005B,
+        0x007D,
+        0x007B,
+        0x00BB,
+        0x00AB,
+        0x0F3B,
+        0x0F3A,
+        0x0F3D,
+        0x0F3C,
+        0x169C,
+        0x169B,
+        0x203A,
+        0x2039,
+        0x2046,
+        0x2045,
+        0x207E,
+        0x207D,
+        0x208E,
+        0x208D,
+        0x220B,
+        0x220C,
+        0x220D,
+        0x2208,
+        0x2209,
+        0x220A,
+        0x29F5,
+        0x223D,
+        0x223C,
+        0x22CD,
+        0x2253,
+        0x2252,
+        0x2255,
+        0x2254,
+        0x2265,
+        0x2264,
+        0x2267,
+        0x2266,
+        0x2269,
+        0x2268,
+        0x226B,
+        0x226A,
+        0x226F,
+        0x226E,
+        0x2271,
+        0x2270,
+        0x2273,
+        0x2272,
+        0x2275,
+        0x2274,
+        0x2277,
+        0x2276,
+        0x2279,
+        0x2278,
+        0x227B,
+        0x227A,
+        0x227D,
+        0x227C,
+        0x227F,
+        0x227E,
+        0x2281,
+        0x2280,
+        0x2283,
+        0x2282,
+        0x2285,
+        0x2284,
+        0x2287,
+        0x2286,
+        0x2289,
+        0x2288,
+        0x228B,
+        0x228A,
+        0x2290,
+        0x228F,
+        0x2292,
+        0x2291,
+        0x29B8,
+        0x22A3,
+        0x22A2,
+        0x2ADE,
+        0x2AE4,
+        0x2AE3,
+        0x2AE5,
+        0x22B1,
+        0x22B0,
+        0x22B3,
+        0x22B2,
+        0x22B5,
+        0x22B4,
+        0x22B7,
+        0x22B6,
+        0x22CA,
+        0x22C9,
+        0x22CC,
+        0x22CB,
+        0x2243,
+        0x22D1,
+        0x22D0,
+        0x22D7,
+        0x22D6,
+        0x22D9,
+        0x22D8,
+        0x22DB,
+        0x22DA,
+        0x22DD,
+        0x22DC,
+        0x22DF,
+        0x22DE,
+        0x22E1,
+        0x22E0,
+        0x22E3,
+        0x22E2,
+        0x22E5,
+        0x22E4,
+        0x22E7,
+        0x22E6,
+        0x22E9,
+        0x22E8,
+        0x22EB,
+        0x22EA,
+        0x22ED,
+        0x22EC,
+        0x22F1,
+        0x22F0,
+        0x22FA,
+        0x22FB,
+        0x22FC,
+        0x22FD,
+        0x22FE,
+        0x22F2,
+        0x22F3,
+        0x22F4,
+        0x22F6,
+        0x22F7,
+        0x2309,
+        0x2308,
+        0x230B,
+        0x230A,
+        0x232A,
+        0x2329,
+        0x2769,
+        0x2768,
+        0x276B,
+        0x276A,
+        0x276D,
+        0x276C,
+        0x276F,
+        0x276E,
+        0x2771,
+        0x2770,
+        0x2773,
+        0x2772,
+        0x2775,
+        0x2774,
+        0x27C4,
+        0x27C3,
+        0x27C6,
+        0x27C5,
+        0x27C9,
+        0x27C8,
+        0x27D6,
+        0x27D5,
+        0x27DE,
+        0x27DD,
+        0x27E3,
+        0x27E2,
+        0x27E5,
+        0x27E4,
+        0x27E7,
+        0x27E6,
+        0x27E9,
+        0x27E8,
+        0x27EB,
+        0x27EA,
+        0x27ED,
+        0x27EC,
+        0x27EF,
+        0x27EE,
+        0x2984,
+        0x2983,
+        0x2986,
+        0x2985,
+        0x2988,
+        0x2987,
+        0x298A,
+        0x2989,
+        0x298C,
+        0x298B,
+        0x2990,
+        0x298F,
+        0x298E,
+        0x298D,
+        0x2992,
+        0x2991,
+        0x2994,
+        0x2993,
+        0x2996,
+        0x2995,
+        0x2998,
+        0x2997,
+        0x2298,
+        0x29C1,
+        0x29C0,
+        0x29C5,
+        0x29C4,
+        0x29D0,
+        0x29CF,
+        0x29D2,
+        0x29D1,
+        0x29D5,
+        0x29D4,
+        0x29D9,
+        0x29D8,
+        0x29DB,
+        0x29DA,
+        0x2215,
+        0x29F9,
+        0x29F8,
+        0x29FD,
+        0x29FC,
+        0x2A2C,
+        0x2A2B,
+        0x2A2E,
+        0x2A2D,
+        0x2A35,
+        0x2A34,
+        0x2A3D,
+        0x2A3C,
+        0x2A65,
+        0x2A64,
+        0x2A7A,
+        0x2A79,
+        0x2A7E,
+        0x2A7D,
+        0x2A80,
+        0x2A7F,
+        0x2A82,
+        0x2A81,
+        0x2A84,
+        0x2A83,
+        0x2A8C,
+        0x2A8B,
+        0x2A92,
+        0x2A91,
+        0x2A94,
+        0x2A93,
+        0x2A96,
+        0x2A95,
+        0x2A98,
+        0x2A97,
+        0x2A9A,
+        0x2A99,
+        0x2A9C,
+        0x2A9B,
+        0x2AA2,
+        0x2AA1,
+        0x2AA7,
+        0x2AA6,
+        0x2AA9,
+        0x2AA8,
+        0x2AAB,
+        0x2AAA,
+        0x2AAD,
+        0x2AAC,
+        0x2AB0,
+        0x2AAF,
+        0x2AB4,
+        0x2AB3,
+        0x2AC4,
+        0x2AC3,
+        0x2AC6,
+        0x2AC5,
+        0x2ACE,
+        0x2ACD,
+        0x2AD0,
+        0x2ACF,
+        0x2AD2,
+        0x2AD1,
+        0x2AD4,
+        0x2AD3,
+        0x2AD6,
+        0x2AD5,
+        0x22A6,
+        0x22A9,
+        0x2E03,
+        0x2E02,
+        0x2E05,
+        0x2E04,
+        0x2E0A,
+        0x2E09,
+        0x2E0D,
+        0x2E0C,
+        0x2E1D,
+        0x2E1C,
+        0x2E21,
+        0x2E20,
+        0x2E23,
+        0x2E22,
+        0x2E25,
+        0x2E24,
+        0x2E27,
+        0x300F,
+        0x300E,
+        0x3011,
+        0x3010,
+        0x3015,
+        0x3014,
+        0x3017,
+        0x3016,
+        0x3019,
+        0x3018,
+        0x301B,
+        0x301A,
+        0xFE5A,
+        0xFE59,
+        0xFF3D,
+        0xFF3B,
+        0xFF5D,
+        0xFF5B,
+        0xFF60,
+        0xFF5F,
+        0xFF63,
+        0xFF62
+    };
+
+    private static int mirror ( int c ) {
+        int i = Arrays.binarySearch ( mirroredCharacters, c );
+        if ( i < 0 ) {
+            return c;
+        } else {
+            return mirroredCharactersMapping [ i ];
+        }
+    }
+
+}



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