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

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

Added: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/BidiUtil.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/BidiUtil.java?rev=987282&view=auto
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/BidiUtil.java (added)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/BidiUtil.java Thu Aug 19 19:46:41 2010
@@ -0,0 +1,1551 @@
+/*
+ * 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.layoutmgr;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.area.LineArea;
+import org.apache.fop.area.inline.Anchor;
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.area.inline.InlineBlockParent;
+import org.apache.fop.area.inline.InlineParent;
+import org.apache.fop.area.inline.Leader;
+import org.apache.fop.area.inline.Space;
+import org.apache.fop.area.inline.SpaceArea;
+import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.area.inline.WordArea;
+import org.apache.fop.area.inline.Viewport;
+import org.apache.fop.fo.CharIterator;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FOText;
+import org.apache.fop.fo.flow.AbstractPageNumberCitation;
+import org.apache.fop.fo.flow.BidiOverride;
+import org.apache.fop.fo.flow.Block;
+import org.apache.fop.fo.flow.BlockContainer;
+import org.apache.fop.fo.flow.Character;
+import org.apache.fop.fo.flow.ExternalGraphic;
+import org.apache.fop.fo.flow.Inline;
+import org.apache.fop.fo.flow.InlineContainer;
+import org.apache.fop.fo.flow.InlineLevel;
+import org.apache.fop.fo.flow.PageNumber;
+import org.apache.fop.fo.pagination.Flow;
+import org.apache.fop.fo.pagination.PageSequence;
+import org.apache.fop.traits.Direction;
+import org.apache.fop.traits.WritingModeTraitsGetter;
+import org.apache.fop.text.bidi.BidiClassUtils;
+import org.apache.fop.util.CharUtilities;
+import org.apache.fop.util.BidiConstants;
+
+// CSOFF: AvoidNestedBlocksCheck
+// CSOFF: EmptyForIteratorPadCheck
+// CSOFF: NoWhitespaceAfterCheck
+// CSOFF: InnerAssignmentCheck
+// CSOFF: SimplifyBooleanReturnCheck
+// CSOFF: LineLengthCheck
+// CSOFF: ParameterNumberCheck
+
+/**
+ * <p>A utility class for performing bidirectional processing.</p>
+ * @author Glenn Adams
+ */
+public final class BidiUtil {
+
+    /**
+     * logging instance
+     */
+    private static final Log log = LogFactory.getLog(BidiUtil.class);                                                   // CSOK: ConstantNameCheck
+
+    private BidiUtil() {
+    }
+
+    /**
+     * Resolve inline directionality.
+     * @param ps a page sequence FO instance
+     */
+    public static void resolveInlineDirectionality ( PageSequence ps ) {
+        if (log.isDebugEnabled()) {
+            log.debug ( "BD: RESOLVE: " + ps );
+        }
+        List ranges = pruneEmptyRanges ( collectRanges ( ps, new Stack() ) );
+        resolveInlineDirectionality ( ranges );
+    }
+
+    /**
+     * Reorder line area.
+     * @param la a line area instance
+     */
+    public static void reorder ( LineArea la ) {
+
+        // 1. collect inline levels
+        List runs = collectRuns ( la.getInlineAreas(), new Vector() );
+        if (log.isDebugEnabled()) {
+            dumpRuns ( "BD: REORDER: INPUT:", runs );
+        }
+        
+        // 2. split heterogeneous inlines
+        runs = splitRuns ( runs );
+        if (log.isDebugEnabled()) {
+            dumpRuns ( "BD: REORDER: SPLIT INLINES:", runs );
+        }
+
+        // 3. determine minimum and maximum levels
+        int[] mm = computeMinMaxLevel ( runs, null );
+        if (log.isDebugEnabled()) {
+            log.debug( "BD: REORDER: { min = " + mm[0] + ", max = " + mm[1] + "}" );
+        }
+
+        // 4. reorder from maximum level to minimum odd level
+        int mn = mm[0];
+        int mx = mm[1];
+        for ( int l1 = mx, l2 = ( ( mn & 1 ) == 0 ) ? ( mn + 1 ) : mn; l1 >= l2; l1-- ) {
+            runs = reorderRuns ( runs, l1 );
+        }
+        if (log.isDebugEnabled()) {
+            dumpRuns ( "BD: REORDER: REORDERED RUNS:", runs );
+        }
+
+        // 5. reverse word consituents (characters and glyphs) while mirroring
+        boolean mirror = true;
+        reverseWords ( runs, mirror );
+        if (log.isDebugEnabled()) {
+            dumpRuns ( "BD: REORDER: REORDERED WORDS:", runs );
+        }
+
+        // 6. replace line area's inline areas with reordered runs' inline areas
+        replaceInlines ( la, replicateSplitWords ( runs ) );
+    }
+
+    private static void resolveInlineDirectionality ( List ranges ) {
+        for ( Iterator it = ranges.iterator(); it.hasNext(); ) {
+            DelimitedTextRange r = (DelimitedTextRange) it.next();
+            r.resolve();
+            if (log.isDebugEnabled()) {
+                log.debug ( r );
+            }
+        }
+    }
+
+    /**
+     * Collect the sequence of delimited text ranges of node FO, where each new
+     * range is pushed onto RANGES.
+     */
+    private static Stack collectRanges ( FONode fn, Stack ranges ) {
+        // return existing ranges if passed null node
+        if ( fn == null ) {
+            return ranges;
+        }
+        // if boundary before, then push new range
+        if ( isRangeBoundaryBefore ( fn ) ) {
+            maybeNewRange ( ranges, fn );
+        }
+        // get current range, if one exists
+        DelimitedTextRange r;
+        if ( ranges.size() > 0 ) {
+            r = (DelimitedTextRange) ranges.peek();
+        } else {
+            r = null;
+        }
+        // proceses this node
+        if ( fn instanceof FOText ) {
+            if ( r != null ) {
+                r.append ( ( (FOText) fn ) .charIterator(), fn );
+            }
+        } else if ( fn instanceof Character ) {
+            if ( r != null ) {
+                r.append ( ( (Character) fn ) .charIterator(), fn );
+            }
+        } else if ( fn instanceof BidiOverride ) {
+            if ( r != null ) {
+                ranges = collectBidiOverrideRanges ( (BidiOverride) fn, r, ranges );
+            }
+        } else if ( fn instanceof PageSequence ) {
+            ranges = collectRanges ( ( (PageSequence) fn ) .getMainFlow(), ranges );
+        } else {
+            for ( Iterator it = fn.getChildNodes(); ( it != null ) && it.hasNext(); ) {
+                ranges = collectRanges ( (FONode) it.next(), ranges );
+            }
+        }
+        // if boundary after, then push new range
+        if ( isRangeBoundaryAfter ( fn ) ) {
+            maybeNewRange ( ranges, fn );
+        }
+        return ranges;
+    }
+
+    private static Stack collectBidiOverrideRanges ( BidiOverride bo, DelimitedTextRange r, Stack ranges ) {
+        char pfx = 0;
+        char sfx = 0;
+        int unicodeBidi = bo.getUnicodeBidi();
+        int direction = bo.getDirection();
+        if ( unicodeBidi == Constants.EN_BIDI_OVERRIDE ) {
+            pfx = ( direction == Constants.EN_RTL ) ? CharUtilities.RLO : CharUtilities.LRO;
+            sfx = CharUtilities.PDF;
+        } else if ( unicodeBidi == Constants.EN_EMBED ) {
+            pfx = ( direction == Constants.EN_RTL ) ? CharUtilities.RLE : CharUtilities.LRE;
+            sfx = CharUtilities.PDF;
+        }
+        if ( pfx != 0 ) {
+            r.append ( pfx, bo );
+        }
+        for ( Iterator it = bo.getChildNodes(); ( it != null ) && it.hasNext(); ) {
+            ranges = collectRanges ( (FONode) it.next(), ranges );
+        }
+        if ( sfx != 0 ) {
+            r.append ( sfx, bo );
+        }
+        return ranges;
+    }
+
+    private static List collectRuns ( List inlines, List runs ) {
+        for ( Iterator it = inlines.iterator(); it.hasNext(); ) {
+            InlineArea ia = (InlineArea) it.next();
+            if ( ia instanceof WordArea ) {
+                runs = collectRuns ( (WordArea) ia, runs );
+            } else if ( ia instanceof SpaceArea ) {
+                runs = collectRuns ( (SpaceArea) ia, runs );
+            } else if ( ia instanceof InlineParent ) {
+                runs = collectRuns ( (InlineParent) ia, runs );
+            } else if ( ia instanceof Viewport ) {
+                runs = collectRuns ( (Viewport) ia, runs );
+            } else if ( ia instanceof Leader ) {
+                runs = collectRuns ( (Leader) ia, runs );
+            } else if ( ia instanceof Space ) {
+                runs = collectRuns ( (Space) ia, runs );
+            } else if ( ia instanceof Anchor ) {
+                runs = collectRuns ( (Anchor) ia, runs );
+            } else if ( ia instanceof InlineBlockParent ) {
+                runs = collectRuns ( (InlineBlockParent) ia, runs );
+            }
+        }
+        return runs;
+    }
+
+    private static List collectRuns ( Anchor a, List runs ) {
+        return runs;
+    }
+
+    private static List collectRuns ( InlineBlockParent a, List runs ) {
+        return runs;
+    }
+
+    private static List collectRuns ( InlineParent a, List runs ) {
+        return collectRuns ( a.getChildAreas(), runs );
+    }
+
+    private static List collectRuns ( Leader a, List runs ) {
+        return runs;
+    }
+
+    private static List collectRuns ( Space a, List runs ) {
+        return runs;
+    }
+
+    private static List collectRuns ( SpaceArea a, List runs ) {
+        runs.add ( new InlineRun ( a, new int[] {a.getBidiLevel()}) );
+        return runs;
+    }
+
+    private static List collectRuns ( Viewport a, List runs ) {
+        return runs;
+    }
+
+    private static List collectRuns ( WordArea a, List runs ) {
+        runs.add ( new InlineRun ( a, a.getBidiLevels() ) );
+        return runs;
+    }
+
+    private static List splitRuns ( List runs ) {
+        List runsNew = new Vector();
+        for ( Iterator it = runs.iterator(); it.hasNext(); ) {
+            InlineRun ir = (InlineRun) it.next();
+            if ( ir.isHomogenous() ) {
+                runsNew.add ( ir );
+            } else {
+                runsNew.addAll ( ir.split() );
+            }
+        }
+        if ( ! runsNew.equals ( runs ) ) {
+            runs = runsNew;
+        }
+        return runs;
+    }
+
+    private static int[] computeMinMaxLevel ( List runs, int[] mm ) {
+        if ( mm == null ) {
+            mm = new int[] {Integer.MAX_VALUE, Integer.MIN_VALUE};
+        }
+        for ( Iterator it = runs.iterator(); it.hasNext(); ) {
+            InlineRun ir = (InlineRun) it.next();
+            ir.updateMinMax ( mm );
+        }
+        return mm;
+    }
+    private static List reorderRuns ( List runs, int level ) {
+        List runsNew = new Vector();
+        for ( int i = 0, n = runs.size(); i < n; i++ ) {
+            InlineRun iri = (InlineRun) runs.get(i);
+            if ( iri.getMinLevel() < level ) {
+                runsNew.add ( iri );
+            } else {
+                int s = i;
+                int e = s;
+                while ( e < n ) {
+                    InlineRun ire = (InlineRun) runs.get(e);
+                    if ( ire.getMinLevel() < level ) {
+                        break;
+                    } else {
+                        e++;
+                    }
+                }
+                if ( s < e ) {
+                    runsNew.addAll ( reverseRuns ( runs, s, e ) );
+                }
+                i = e - 1;
+            }
+        }
+        if ( ! runsNew.equals ( runs ) ) {
+            runs = runsNew;
+        }
+        return runs;
+    }
+    private static List reverseRuns ( List runs, int s, int e ) {
+        int n = e - s;
+        Vector runsNew = new Vector ( n );
+        if ( n > 0 ) {
+            for ( int i = 0; i < n; i++ ) {
+                int k = ( n - i - 1 );
+                InlineRun ir = (InlineRun) runs.get(s + k);
+                ir.reverse();
+                runsNew.add ( ir );
+            }
+        }
+        return runsNew;
+    }
+    private static void reverseWords ( List runs, boolean mirror ) {
+        for ( Iterator it = runs.iterator(); it.hasNext(); ) {
+            InlineRun ir = (InlineRun) it.next();
+            ir.maybeReverseWord ( mirror );
+        }
+    }
+    private static List replicateSplitWords ( List runs ) {
+        // [TBD] for each run which inline word area appears multiple times in
+        // runs, replicate that word
+        return runs;
+    }
+    private static void replaceInlines ( LineArea la, List runs ) {
+        List inlines = new ArrayList();
+        for ( Iterator it = runs.iterator(); it.hasNext(); ) {
+            InlineRun ir = (InlineRun) it.next();
+            inlines.add ( ir.getInline() );
+        }
+        inlines = unflattenInlines ( inlines );
+        la.setInlineAreas ( inlines );
+    }
+    private static List unflattenInlines ( List inlines ) {
+        List inlinesNew = new ArrayList();                              // unflattened inlines being consed
+        TextArea tLast = null;                                          // last text area parent
+        TextArea tNew = null;                                           // new text area being consed
+        int lLast = -1;                                                 // last bidi level
+        for ( Iterator it = inlines.iterator(); it.hasNext(); ) {
+            InlineArea ia = (InlineArea) it.next();
+            if ( ( ia instanceof WordArea ) || ( ia instanceof SpaceArea ) ) {
+                TextArea t = (TextArea) ia.getParentArea();
+                int l = ia.getBidiLevel();
+                if ( isEndOfTextArea ( t, tLast, l, lLast ) ) {
+                    if ( tNew != null ) {
+                        inlinesNew.add ( tNew );
+                        tNew = null;
+                    }
+                }
+                if ( tNew == null ) {
+                    tNew = createUnflattenedText ( t );
+                }
+                tNew.addChildArea ( ia );
+                tLast = t;
+                lLast = l;
+            } else {
+                inlinesNew.add ( ia );
+            }
+        }
+        if ( tNew != null ) {
+            inlinesNew.add ( tNew );
+        }
+        return inlinesNew;
+    }
+    private static boolean isEndOfTextArea ( TextArea t, TextArea tLast, int level, int levelLast ) {
+        if ( ( tLast != null ) && ( t != tLast ) ) {
+            return true;
+        } else if ( ( levelLast != -1 ) && ( level != levelLast ) ) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+    private static TextArea createUnflattenedText ( TextArea t ) {
+        TextArea tNew = new TextArea();
+        if ( t != null ) {
+            tNew.setBPD ( t.getBPD() );
+            tNew.setTraits ( t.getTraits() );
+            tNew.setBlockProgressionOffset ( t.getBlockProgressionOffset() );
+            tNew.setBaselineOffset ( t.getBaselineOffset() );
+        }
+        return tNew;
+    }
+    private static void dumpRuns ( String header, List runs ) {
+        log.debug ( header );
+        for ( Iterator it = runs.iterator(); it.hasNext(); ) {
+            InlineRun ir = (InlineRun) it.next();
+            log.debug ( ir );
+        }
+    }
+
+    /**
+     * <p>Conditionally add a new delimited text range to RANGES, where new range is
+     * associated with node FN. A new text range is added unless all of the following are true:</p>
+     * <ul>
+     * <li>there exists a current range RCUR in RANGES</li>
+     * <li>RCUR is empty</li>
+     * <li>the node of the RCUR is the same node as FN or a descendent node of FN</li>
+     * </ul>
+     */
+    private static DelimitedTextRange maybeNewRange ( Stack ranges, FONode fn ) {
+        DelimitedTextRange rCur = null;
+        DelimitedTextRange rNew = null;
+        if ( ranges.empty() ) {
+            if ( fn instanceof Block ) {
+                rNew = new DelimitedTextRange(fn);
+            }
+        } else if ( ( rCur = (DelimitedTextRange) ranges.peek() ) != null ) {
+            if ( ! rCur.isEmpty() || ! isSelfOrDescendent ( rCur.getNode(), fn ) ) {
+                rNew = new DelimitedTextRange(fn);
+            }
+        }
+        if ( rNew != null ) {
+            ranges.push ( rNew );
+        } else {
+            rNew = rCur;
+        }
+        return rNew;
+    }
+
+    /**
+     * Determine if node N2 is the same or a descendent of node N1.
+     */
+    private static boolean isSelfOrDescendent ( FONode n1, FONode n2 ) {
+        for ( FONode n = n2; n != null; n = n.getParent() ) {
+            if ( n == n1 ) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean isRangeBoundary ( FONode fn ) {
+        if ( fn instanceof Block ) {                                    // fo:block
+            return true;
+        } else if ( fn instanceof InlineLevel ) {                       // fo:inline, fo:leader, fo:bidi-override, fo:title
+            return false;
+        } else if ( fn instanceof InlineContainer ) {                   // fo:inline-container
+            return false;
+        } else if ( fn instanceof BlockContainer ) {                    // fo:block-container
+            return true;
+        } else if ( fn instanceof AbstractPageNumberCitation ) {        // fo:page-number-citation, fo:page-number-citation-last
+            return false;
+        } else if ( fn instanceof PageNumber ) {                        // fo:page-number
+            return false;
+        } else if ( fn instanceof ExternalGraphic ) {                   // fo:external-graphic
+            return false;
+        } else if ( fn instanceof FOText ) {                            // #PCDATA
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    private static boolean isRangeBoundaryBefore ( FONode fn ) {
+        return isRangeBoundary ( fn );
+    }
+
+    private static boolean isRangeBoundaryAfter ( FONode fn ) {
+        return isRangeBoundary ( fn );
+    }
+
+    private static List pruneEmptyRanges ( Stack ranges ) {
+        Vector rv = new Vector();
+        for ( Iterator it = ranges.iterator(); it.hasNext(); ) {
+            DelimitedTextRange r = (DelimitedTextRange) it.next();
+            if ( ! r.isEmpty() ) {
+                rv.add ( r );
+            }
+        }
+        return rv;
+    }
+
+    private static String padLeft ( int n, int width ) {
+        return padLeft ( Integer.toString ( n ), width );
+    }
+
+    private static String padLeft ( String s, int width ) {
+        StringBuffer sb = new StringBuffer();
+        for ( int i = s.length(); i < width; i++ ) {
+            sb.append(' ');
+        }
+        sb.append ( s );
+        return sb.toString();
+    }
+
+    /* not used yet
+    private static String padRight ( int n, int width ) {
+        return padRight ( Integer.toString ( n ), width );
+    }
+    */
+
+    private static String padRight ( String s, int width ) {
+        StringBuffer sb = new StringBuffer ( s );
+        for ( int i = sb.length(); i < width; i++ ) {
+            sb.append(' ');
+        }
+        return sb.toString();
+    }
+
+    private static class DelimitedTextRange {
+        private FONode fn;                              // node that generates this text range
+        private StringBuffer buffer;                    // flattened character sequence of generating FO nodes
+        private List intervals;                         // list of intervals over buffer of generating FO nodes
+        DelimitedTextRange ( FONode fn ) {
+            this.fn = fn;
+            this.buffer = new StringBuffer();
+            this.intervals = new Vector();
+        }
+        FONode getNode() {
+            return fn;
+        }
+        void append ( CharIterator it, FONode fn ) {
+            if ( it != null ) {
+                int s = buffer.length();
+                int e = s;
+                while ( it.hasNext() ) {
+                    char c = it.nextChar();
+                    buffer.append ( c );
+                    e++;
+                }
+                intervals.add ( new TextInterval ( fn, s, e ) );
+            }
+        }
+        void append ( char c, FONode fn ) {
+            if ( c != 0 ) {
+                int s = buffer.length();
+                int e = s + 1;
+                buffer.append ( c );
+                intervals.add ( new TextInterval ( fn, s, e ) );
+            }
+        }
+        boolean isEmpty() {
+            return buffer.length() == 0;
+        }
+        void resolve() {
+            WritingModeTraitsGetter tg;
+            if ( ( tg = getWritingModeTraitsGetter ( getNode() ) ) != null ) {
+                resolve ( tg.getInlineProgressionDirection() );
+            }
+        }
+        public String toString() {
+            StringBuffer sb = new StringBuffer ( "DR: " + fn.getLocalName() + "{ <" + CharUtilities.toNCRefs ( buffer.toString() ) + ">" );
+            sb.append ( ", intervals <" );
+            boolean first = true;
+            for ( Iterator it = intervals.iterator(); it.hasNext(); ) {
+                TextInterval ti = (TextInterval) it.next();
+                if ( first ) {
+                    first = false;
+                } else {
+                    sb.append(',');
+                }
+                sb.append ( ti.toString() );
+            }
+            sb.append(">}");
+            return sb.toString();
+        }
+        private void resolve ( Direction paragraphEmbeddingLevel ) {
+            int [] levels;
+            if ( ( levels = UnicodeBidiAlgorithm.resolveLevels ( buffer, paragraphEmbeddingLevel ) ) != null ) {
+                assignLevels ( levels );
+                assignTextLevels();
+                assignBlockLevel(paragraphEmbeddingLevel);
+            }
+        }
+        /**
+         * <p>Assign resolved levels to all text intervals of this delimited text range.</p>
+         * <p>Has a possible side effect of replacing the intervals array with a new array
+         * containing new text intervals, such that each text interval is associated with
+         * a single level run.</p>
+         * @param levels array of levels each corresponding to each index of the delimited
+         * text range
+         */
+        private void assignLevels ( int[] levels ) {
+            Vector intervalsNew = new Vector ( intervals.size() );
+            for ( Iterator it = intervals.iterator(); it.hasNext(); ) {
+                TextInterval ti = (TextInterval) it.next();
+                intervalsNew.addAll ( assignLevels ( ti, levels ) );
+            }
+            if ( ! intervalsNew.equals ( intervals ) ) {
+                intervals = intervalsNew;
+            }
+        }
+        /**
+         * <p>Assign resolved levels to a specified text interval over this delimited text
+         * range.</p>
+         * <p>Returns a list of text intervals containing either (1) the single, input text
+         * interval or (2) two or more new text intervals obtained from sub-dividing the input
+         * text range into level runs, i.e., runs of text assigned to a single level.</p>
+         * @param ti a text interval to which levels are to be assigned
+         * @param levels array of levels each corresponding to each index of the delimited
+         * text range
+         * @returns a list of text intervals as described above
+         */
+        private List assignLevels ( TextInterval ti, int[] levels ) {
+            Vector tiv = new Vector();
+            FONode fn = ti.getNode();
+            int fnStart = ti.getStart();                                     // start of node's text in delimited text range
+            for ( int i = fnStart, n = ti.getEnd(); i < n; ) {
+                int s = i;                                              // inclusive start index of interval in delimited text range
+                int e = s;                                              // exclusive end index of interval in delimited text range
+                int l = levels [ s ];                                   // current run level
+                while ( e < n ) {                                       // skip to end of run level or end of interval
+                    if ( levels [ e ] != l ) {
+                        break;
+                    } else {
+                        e++;
+                    }
+                }
+                if ( ( ti.getStart() == s ) && ( ti.getEnd() == e ) ) {
+                    ti.setLevel ( l );                                       // reuse interval, assigning it single level
+                } else {
+                    ti = new TextInterval ( fn, fnStart, s, e, l );     // subdivide interval
+                }
+                if (log.isDebugEnabled()) {
+                    log.debug ( "AL(" + l + "): " + ti );
+                }
+                tiv.add ( ti );
+                i = e;
+            }
+            return tiv;
+        }
+        /**
+         * <p>Assign resolved levels for each interval to source #PCDATA in the associated FOText.</p>
+         */
+        private void assignTextLevels() {
+            for ( Iterator it = intervals.iterator(); it.hasNext(); ) {
+                TextInterval ti = (TextInterval) it.next();
+                ti.assignTextLevels();
+            }
+        }
+        private void assignBlockLevel ( Direction paragraphEmbeddingLevel ) {
+            int defaultLevel = ( paragraphEmbeddingLevel == Direction.RL ) ? 1 : 0;
+            for ( Iterator it = intervals.iterator(); it.hasNext(); ) {
+                TextInterval ti = (TextInterval) it.next();
+                assignBlockLevel ( ti.getNode(), defaultLevel );
+            }
+        }
+        private void assignBlockLevel ( FONode node, int defaultLevel ) {
+            for ( FONode fn = node; fn != null; fn = fn.getParent() ) {
+                if ( fn instanceof Block ) {
+                    Block bn = (Block) fn;
+                    if ( bn.getBidiLevel() < 0 ) {
+                        bn.setBidiLevel ( defaultLevel );
+                    }
+                    break;
+                }
+            }
+        }
+        private WritingModeTraitsGetter getWritingModeTraitsGetter ( FONode fn ) {
+            for ( FONode n = fn; n != null; n = n.getParent() ) {
+                if ( n instanceof WritingModeTraitsGetter ) {
+                    return (WritingModeTraitsGetter) n;
+                }
+            }
+            return null;
+        }
+    }
+
+    private static class TextInterval {
+        private FONode fn;              // associated node
+        private int textStart;          // starting index within delimited text range of associated node's text
+        private int start;              // starting index within delimited text range
+        private int end;                // ending index within delimited text range
+        private int level;              // resolved level or default (-1)
+        TextInterval ( FONode fn, int start, int end ) {
+            this ( fn, start, start, end, -1 );
+        }
+        TextInterval ( FONode fn, int textStart, int start, int end, int level ) {
+            this.fn = fn;
+            this.textStart = textStart;
+            this.start = start;
+            this.end = end;
+            this.level = level;
+        }
+        FONode getNode() {
+            return fn;
+        }
+        int getTextStart() {
+            return textStart;
+        }
+        int getStart() {
+            return start;
+        }
+        int getEnd() {
+            return end;
+        }
+        int getLevel() {
+            return level;
+        }
+        void setLevel ( int level ) {
+            this.level = level;
+        }
+        public int length() {
+            return end - start;
+        }
+        public String getText() {
+            if ( fn instanceof FOText ) {
+                return new String ( ( (FOText) fn ) .getCharArray() );
+            } else if ( fn instanceof Character ) {
+                return new String ( new char[] {( (Character) fn ) .getCharacter()} );
+            } else {
+                return null;
+            }
+        }
+        public void assignTextLevels() {
+            if ( fn instanceof FOText ) {
+                ( (FOText) fn ) .setBidiLevel ( level, start - textStart, end - textStart );
+            } else if ( fn instanceof Character ) {
+                ( (Character) fn ) .setBidiLevel ( level );
+            }
+        }
+        public boolean equals ( Object o ) {
+            if ( o instanceof TextInterval ) {
+                TextInterval ti = (TextInterval) o;
+                if ( ti.getNode() != fn ) {
+                    return false;
+                } else if ( ti.getStart() != start ) {
+                    return false;
+                } else if ( ti.getEnd() != end ) {
+                    return false;
+                } else {
+                    return true;
+                }
+            } else {
+                return false;
+            }
+        }
+        public int hashCode() {
+            int l = ( fn != null ) ? fn.hashCode() : 0;
+            l = ( l ^ start ) + ( l << 19 );
+            l = ( l ^ end )   + ( l << 11 );
+            return l;
+        }
+        public String toString() {
+            StringBuffer sb = new StringBuffer();
+            char c;
+            if ( fn instanceof FOText ) {
+                c = 'T';
+            } else if ( fn instanceof Character ) {
+                c = 'C';
+            } else if ( fn instanceof BidiOverride ) {
+                c = 'B';
+            } else {
+                c = '?';
+            }
+            sb.append ( c );
+            sb.append ( "[" + start + "," + end + "][" + textStart + "](" + level + ")" );
+            return sb.toString();
+        }
+    }
+
+    /**
+     * The <code>InlineRun</code> class is a utility class used to capture a sequence of
+     * reordering levels associated with an inline area.
+     */
+    private static class InlineRun {
+        private InlineArea inline;
+        private int[] levels;
+        private int minLevel;
+        private int maxLevel;
+        private int reversals;
+        InlineRun ( InlineArea inline, int[] levels ) {
+            this.inline = inline;
+            this.levels = levels;
+            setMinMax ( levels );
+        }
+        private InlineRun ( InlineArea inline, int level, int count ) {
+            this ( inline, makeLevels ( level, count ) );
+        }
+        InlineArea getInline() {
+            return inline;
+        }
+        int getMinLevel() {
+            return minLevel;
+        }
+        private void setMinMax ( int[] levels ) {
+            int mn = Integer.MAX_VALUE;
+            int mx = Integer.MIN_VALUE;
+            if ( ( levels != null ) && ( levels.length > 0 ) ) {
+                for ( int i = 0, n = levels.length; i < n; i++ ) {
+                    int l = levels [ i ];
+                    if ( l < mn ) {
+                        mn = l;
+                    }
+                    if ( l > mx ) {
+                        mx = l;
+                    }
+                }
+            } else {
+                mn = mx = -1;
+            }
+            this.minLevel = mn;
+            this.maxLevel = mx;
+        }
+        public boolean isHomogenous() {
+            return minLevel == maxLevel;
+        }
+        public List split() {
+            List runs = new Vector();
+            for ( int i = 0, n = levels.length; i < n; ) {
+                int l = levels [ i ];
+                int s = i;
+                int e = s;
+                while ( e < n ) {
+                    if ( levels [ e ] != l ) {
+                        break;
+                    } else {
+                        e++;
+                    }
+                }
+                if ( s < e ) {
+                    runs.add ( new InlineRun ( inline, l, e - s ) );
+                }
+                i = e;
+            }
+            assert runs.size() < 2 : "heterogeneous inlines not yet supported!!";
+            return runs;
+        }
+        public void updateMinMax ( int[] mm ) {
+            if ( minLevel < mm[0] ) {
+                mm[0] = minLevel;
+            }
+            if ( maxLevel > mm[1] ) {
+                mm[1] = maxLevel;
+            }
+        }
+        public boolean maybeNeedsMirroring() {
+            return ( minLevel == maxLevel ) && ( ( minLevel & 1 ) != 0 );
+        }
+        public void reverse() {
+            reversals++;
+        }
+        public void maybeReverseWord ( boolean mirror ) {
+            if ( inline instanceof WordArea ) {
+                WordArea w = (WordArea) inline;
+                if ( ( reversals & 1 ) != 0 ) {
+                    w.reverse ( mirror );
+                } else if ( mirror && maybeNeedsMirroring() ) {
+                    w.mirror();
+                }
+            }
+        }
+        public boolean equals ( Object o ) {
+            if ( o instanceof InlineRun ) {
+                InlineRun ir = (InlineRun) o;
+                if ( ir.inline != inline ) {
+                    return false;
+                } else if ( ir.minLevel != minLevel ) {
+                    return false;
+                } else if ( ir.maxLevel != maxLevel ) {
+                    return false;
+                } else if ( ( ir.levels != null ) && ( levels != null ) ) {
+                    if ( ir.levels.length != levels.length ) {
+                        return false;
+                    } else {
+                        for ( int i = 0, n = levels.length; i < n; i++ ) {
+                            if ( ir.levels[i] != levels[i] ) {
+                                return false;
+                            }
+                        }
+                        return true;
+                    }
+                } else if ( ( ir.levels == null ) && ( levels == null ) ) {
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {
+                return false;
+            }
+        }
+        public int hashCode() {
+            int l = ( inline != null ) ? inline.hashCode() : 0;
+            l = ( l ^ minLevel ) + ( l << 19 );
+            l = ( l ^ maxLevel )   + ( l << 11 );
+            return l;
+        }
+        public String toString() {
+            StringBuffer sb = new StringBuffer( "RR: { type = \'" );
+            char c;
+            String content = null;
+            if ( inline instanceof WordArea ) {
+                c = 'W';
+                content = ( (WordArea) inline ) .getWord();
+            } else if ( inline instanceof SpaceArea ) {
+                c = 'S';
+                content = ( (SpaceArea) inline ) .getSpace();
+            } else if ( inline instanceof InlineParent ) {
+                c = 'I';
+            } else if ( inline instanceof InlineBlockParent ) {
+                c = 'B';
+            } else if ( inline instanceof Viewport ) {
+                c = 'V';
+            } else if ( inline instanceof Leader ) {
+                c = 'L';
+            } else if ( inline instanceof Anchor ) {
+                c = 'A';
+            } else if ( inline instanceof Space ) {
+                c = 'G'; // 'G' => glue
+            } else {
+                c = '?';
+            }
+            sb.append ( c );
+            sb.append ( "\', levels = \'" );
+            sb.append ( generateLevels ( levels ) );
+            sb.append ( "\', min = " );
+            sb.append ( minLevel );
+            sb.append ( ", max = " );
+            sb.append ( maxLevel );
+            sb.append ( ", reversals = " );
+            sb.append ( reversals );
+            sb.append ( ", content = <" );
+            sb.append ( CharUtilities.toNCRefs ( content ) );
+            sb.append ( "> }" );
+            return sb.toString();
+        }
+        private String generateLevels ( int[] levels ) {
+            StringBuffer lb = new StringBuffer();
+            int maxLevel = -1;
+            int numLevels = levels.length;
+            for ( int i = 0; i < numLevels; i++ ) {
+                int l = levels [ i ];
+                if ( l > maxLevel ) {
+                    maxLevel = l;
+                }
+            }
+            if ( maxLevel < 0 ) {
+                // leave level buffer empty
+            } else if ( maxLevel < 10 ) {
+                // use string of decimal digits
+                for ( int i = 0; i < numLevels; i++ ) {
+                    lb.append ( (char) ( '0' + levels [ i ] ) );
+                }
+            } else {
+                // use comma separated list
+                boolean first = true;
+                for ( int i = 0; i < numLevels; i++ ) {
+                    if ( first ) {
+                        first = false;
+                    } else {
+                        lb.append(',');
+                    }
+                    lb.append ( levels [ i ] );
+                }
+            }
+            return lb.toString();
+        }
+        private static int[] makeLevels ( int level, int count ) {
+            int[] levels = new int [ count ];
+            Arrays.fill ( levels, level );
+            return levels;
+        }
+    }
+
+    /**
+     * The <code>UnicodeBidiAlgorithm</code> class implements functionality prescribed by
+     * the Unicode Bidirectional Algorithm, Unicode Standard Annex #9.
+     */
+    private static final class UnicodeBidiAlgorithm implements BidiConstants {
+
+        private UnicodeBidiAlgorithm() {
+        }
+
+        /**
+         * Resolve the directionality levels of each character in a character seqeunce.
+         * If some character is encoded in the character sequence as a Unicode Surrogate Pair,
+         * then the directionality level of each of the two members of the  pair will be identical.
+         * @returns null if bidirectional processing is not required; otherwise, returns an array
+         * of integers, where each integer corresponds to exactly one UTF-16
+         * encoding element present in the input character sequence, and where each integer denotes
+         * the directionality level of the corresponding encoding element
+         * @param cs input character sequence representing a UTF-16 encoded string
+         * @param defaultLevel the default paragraph level, which must be zero (LR) or one (RL)
+         */
+        public static int[] resolveLevels ( CharSequence cs, Direction defaultLevel ) {
+            int[] chars = new int [ cs.length() ];
+            if ( convertToScalar ( cs, chars ) || ( defaultLevel == Direction.RL ) ) {
+                return resolveLevels ( chars, ( defaultLevel == Direction.RL ) ? 1 : 0, new int [ chars.length ] );
+            } else {
+                return null;
+            }
+        }
+
+        private static int[] resolveLevels ( int[] chars, int defaultLevel, int[] levels ) {
+            return resolveLevels ( chars, getClasses ( chars ), defaultLevel, levels );
+        }
+
+        private static int[] resolveLevels ( int[] chars, int[] classes, int defaultLevel, int[] levels ) {
+            resolveExplicit ( classes, defaultLevel, levels );
+            resolveRuns ( classes, defaultLevel, levels );
+            dump ( "RL: CC(" + chars.length + ")", chars, classes, defaultLevel, levels );
+            return levels;
+        }
+
+        private static void resolveExplicit ( int[] classes, int defaultLevel, int[] levels ) {
+            int[] es = new int [ MAX_LEVELS ];          /* embeddings stack */
+            int ei = 0;                                 /* embeddings stack index */
+            int ec = defaultLevel;                      /* current embedding level */
+            for ( int i = 0, n = classes.length; i < n; i++ ) {
+                int bc = classes [ i ];                 /* bidi class of current char */
+                int el;                                 /* embedding level to assign to current char */
+                switch ( bc ) {
+                case LRE:                               // start left-to-right embedding
+                case RLE:                               // start right-to-left embedding
+                case LRO:                               // start left-to-right override
+                case RLO:                               // start right-to-left override
+                    {
+                        int en;                         /* new embedding level */
+                        if ( ( bc == RLE ) || ( bc == RLO ) ) {
+                            en = ( ec + 1 ) | 1;
+                        } else {
+                            en = ( ec + 2 ) & ~1;
+                        }
+                        if ( en < ( MAX_LEVELS + 1 ) ) {
+                            es [ ei++ ] = ec;
+                            if ( ( bc == LRO ) || ( bc == RLO ) ) {
+                                ec = en | OVERRIDE;
+                            } else {
+                                ec = en & ~OVERRIDE;
+                            }
+                        } else {
+                            throw new IllegalStateException ( "maximum bidi levels exceeded" );
+                        }
+                        el = ec;
+                        break;
+                    }
+                case PDF:                               // pop directional formatting
+                    {
+                        el = ec;
+                        if ( ei > 0 ) {
+                            ec = es [ --ei ];
+                        } else {
+                            throw new IllegalStateException ( "isolated pop directional formatting character" );
+                        }
+                        break;
+                    }
+                case B:                                 // paragraph separator
+                    {
+                        el = ec = defaultLevel;
+                        ei = 0;
+                        break;
+                    }
+                default:
+                    {
+                        el = ec;
+                        break;
+                    }
+                }
+                switch ( bc ) {
+                case LRE: case RLE: case LRO: case RLO: case PDF:
+                    classes [ i ] = BN;
+                    break;
+                default:
+                    if ( ( el & OVERRIDE ) != 0 ) {
+                        classes [ i ] = ( ( el & 1 ) != 0 ) ? R : L;
+                    }
+                    break;
+                }
+                levels [ i ] = el & ~OVERRIDE;
+            }
+        }
+
+        private static void resolveRuns ( int[] classes, int defaultLevel, int[] levels ) {
+            if ( levels.length != classes.length ) {
+                throw new IllegalArgumentException ( "levels sequence length must match classes sequence length" );
+            }
+            for ( int i = 0, n = levels.length; i < n; ) {
+                int s = i;
+                int e = s;
+                int l = levels [ s ];
+                while ( e < n ) {
+                    if ( levels [ e ] != l ) {
+                        break;
+                    } else {
+                        e++;
+                    }
+                }
+                resolveRun ( classes, defaultLevel, levels, s, e, l );
+                i = e;
+            }
+        }
+
+        private static void resolveRun ( int[] classes, int defaultLevel, int[] levels, int start, int end, int level ) {
+
+            // determine start of run direction
+            int ls;
+            if ( start == 0 ) {
+                ls = max ( defaultLevel, level );
+            } else {
+                ls = max ( levels [ start - 1 ], level );
+            }
+            int sor = ( ( ls & 1 ) != 0 ) ? R : L;
+
+            // determine end of run direction
+            int le;
+            if ( end == levels.length ) {
+                le = max ( level, defaultLevel );
+            } else {
+                le = max ( level, levels [ end + 1 ] );
+            }
+            int eor = ( ( le & 1 ) != 0 ) ? R : L;
+
+            if (log.isDebugEnabled()) {
+                log.debug ( "BR[" + padLeft ( start, 3 ) + "," + padLeft ( end, 3 ) + "] :" + padLeft ( level, 2 ) + ": SOR(" + getClassName(sor) + "), EOR(" + getClassName(eor) + ")" );
+            }
+
+            resolveWeak ( classes, defaultLevel, levels, start, end, level, sor, eor );
+            resolveNeutrals ( classes, defaultLevel, levels, start, end, level, sor, eor );
+            resolveImplicit ( classes, defaultLevel, levels, start, end, level, sor, eor );
+
+        }
+
+        private static void resolveWeak ( int[] classes, int defaultLevel, int[] levels, int start, int end, int level, int sor, int eor ) {
+
+            // W1 - X BN* NSM -> X BN* X
+            for ( int i = start, n = end, bcPrev = sor; i < n; i++ ) {
+                int bc = classes [ i ];
+                if ( bc == NSM ) {
+                    classes [ i ] = bcPrev;
+                } else if ( bc != BN ) {
+                    bcPrev = bc;
+                }
+            }
+
+            // W2 - AL ... EN -> AL ... AN
+            for ( int i = start, n = end, bcPrev = sor; i < n; i++ ) {
+                int bc = classes [ i ];
+                if ( bc == EN ) {
+                    if ( bcPrev == AL ) {
+                        classes [ i ] = AN;
+                    }
+                } else if ( isStrong ( bc ) ) {
+                    bcPrev = bc;
+                }
+            }
+
+            // W3 - AL -> R
+            for ( int i = start, n = end; i < n; i++ ) {
+                int bc = classes [ i ];
+                if ( bc == AL ) {
+                    classes [ i ] = R;
+                }
+            }
+
+            // W4 - EN BN* ES BN* EN -> EN BN* EN BN* EN; XN BN* CS BN* XN -> XN BN* XN BN* XN
+            for ( int i = start, n = end, bcPrev = sor; i < n; i++ ) {
+                int bc = classes [ i ];
+                if ( bc == ES ) {
+                    int bcNext = eor;
+                    for ( int j = i + 1; j < n; j++ ) {
+                        if ( ( bc = classes [ j ] ) != BN ) {
+                            bcNext = bc;
+                            break;
+                        }
+                    }
+                    if ( ( bcPrev == EN ) && ( bcNext == EN ) ) {
+                        classes [ i ] = EN;
+                    }
+                } else if ( bc == CS ) {
+                    int bcNext = eor;
+                    for ( int j = i + 1; j < n; j++ ) {
+                        if ( ( bc = classes [ j ] ) != BN ) {
+                            bcNext = bc;
+                            break;
+                        }
+                    }
+                    if ( ( bcPrev == EN ) && ( bcNext == EN ) ) {
+                        classes [ i ] = EN;
+                    } else if ( ( bcPrev == AN ) && ( bcNext == AN ) ) {
+                        classes [ i ] = AN;
+                    }
+                } else if ( bc != BN ) {
+                    bcPrev = bc;
+                }
+            }
+
+            // W5 - EN (ET|BN)* -> EN (EN|BN)*; (ET|BN)* EN -> (EN|BN)* EN
+            for ( int i = start, n = end, bcPrev = sor; i < n; i++ ) {
+                int bc = classes [ i ];
+                if ( bc == ET ) {
+                    int bcNext = eor;
+                    for ( int j = i + 1; j < n; j++ ) {
+                        bc = classes [ j ];
+                        if ( ( bc != BN ) && ( bc != ET ) ) {
+                            bcNext = bc;
+                            break;
+                        }
+                    }
+                    if ( ( bcPrev == EN ) || ( bcNext == EN ) ) {
+                        classes [ i ] = EN;
+                    }
+                } else if ( ( bc != BN ) && ( bc != ET ) ) {
+                    bcPrev = bc;
+                }
+            }
+
+            // W6 - BN* (ET|ES|CS) BN* -> ON* ON ON*
+            for ( int i = start, n = end; i < n; i++ ) {
+                int bc = classes [ i ];
+                if ( ( bc == ET ) || ( bc == ES ) || ( bc == CS ) ) {
+                    classes [ i ] = ON;
+                    resolveAdjacentBoundaryNeutrals ( classes, start, end, i, ON );
+                }
+            }
+
+            // W7 - L ... EN -> L ... L
+            for ( int i = start, n = end, bcPrev = sor; i < n; i++ ) {
+                int bc = classes [ i ];
+                if ( bc == EN ) {
+                    if ( bcPrev == L ) {
+                        classes [ i ] = L;
+                    }
+                } else if ( ( bc == L ) || ( bc == R ) ) {
+                    bcPrev = bc;
+                }
+            }
+
+        }
+
+        private static void resolveNeutrals ( int[] classes, int defaultLevel, int[] levels, int start, int end, int level, int sor, int eor ) {
+
+            // N1 - (L|R) N+ (L|R) -> L L+ L | R R+ R
+            for ( int i = start, n = end, bcPrev = sor; i < n; i++ ) {
+                int bc = classes [ i ];
+                if ( isNeutral ( bc ) ) {
+                    int bcNext = eor;
+                    for ( int j = i + 1; j < n; j++ ) {
+                        bc = classes [ j ];
+                        if ( ( bc == L ) || ( bc == R ) ) {
+                            bcNext = bc;
+                            break;
+                        }
+                    }
+                    if ( bcPrev == bcNext ) {
+                        classes [ i ] = bcPrev;
+                        resolveAdjacentBoundaryNeutrals ( classes, start, end, i, bcPrev );
+                    }
+                } else if ( ( bc == L ) || ( bc == R ) ) {
+                    bcPrev = bc;
+                }
+            }
+
+            // N2 - N -> default level
+            for ( int i = start, n = end, bcDefault = ( ( defaultLevel & 1 ) != 0 ) ? R : L; i < n; i++ ) {
+                int bc = classes [ i ];
+                if ( isNeutral ( bc ) ) {
+                    classes [ i ] = bcDefault;
+                    resolveAdjacentBoundaryNeutrals ( classes, start, end, i, bcDefault );
+                }
+            }
+
+        }
+
+        private static void resolveAdjacentBoundaryNeutrals ( int[] classes, int start, int end, int index, int bcNew ) {
+            if ( ( index < start ) || ( index >= end ) ) {
+                throw new IllegalArgumentException();
+            } else {
+                for ( int i = index - 1; i >= start; i-- ) {
+                    int bc = classes [ i ];
+                    if ( bc == BN ) {
+                        classes [ i ] = bcNew;
+                    } else {
+                        break;
+                    }
+                }
+                for ( int i = index + 1; i < end; i++ ) {
+                    int bc = classes [ i ];
+                    if ( bc == BN ) {
+                        classes [ i ] = bcNew;
+                    } else {
+                        break;
+                    }
+                }
+            }
+        }
+
+        private static void resolveImplicit ( int[] classes, int defaultLevel, int[] levels, int start, int end, int level, int sor, int eor ) {
+
+            for ( int i = start, n = end; i < n; i++ ) {
+                int bc = classes [ i ];                 // bidi class
+                int el = levels [ i ];                  // embedding level
+                int ed = 0;                             // embedding level delta
+                if ( ( el & 1 ) == 0 ) {                // even
+                    if ( bc == R ) {
+                        ed = 1;
+                    } else if ( bc == AN ) {
+                        ed = 2;
+                    } else if ( bc == EN ) {
+                        ed = 2;
+                    }
+                } else {                                // odd
+                    if ( bc == L ) {
+                        ed = 1;
+                    } else if ( bc == EN ) {
+                        ed = 1;
+                    } else if ( bc == AN ) {
+                        ed = 1;
+                    }
+                }
+                levels [ i ] = el + ed;
+            }
+
+        }
+
+        private static boolean isStrong ( int bc ) {
+            switch ( bc ) {
+            case L:
+            case R:
+            case AL:
+                return true;
+            default:
+                return false;
+            }
+        }
+
+        private static boolean isNeutral ( int bc ) {
+            switch ( bc ) {
+            case WS:
+            case ON:
+            case S:
+            case B:
+                return true;
+            default:
+                return false;
+            }
+        }
+
+        private static int max ( int x, int y ) {
+            if ( x > y ) {
+                return x;
+            } else {
+                return y;
+            }
+        }
+
+        private static int[] getClasses ( int[] chars ) {
+            int[] classes = new int [ chars.length ];
+            int bc;
+            for ( int i = 0, n = chars.length; i < n; i++ ) {
+                int ch = chars [ i ];
+                if ( ch >= 0 ) {
+                    bc = BidiClassUtils.getBidiClass ( chars [ i ] );
+                } else {
+                    bc = SURROGATE;
+                }
+                classes [ i ] = bc;
+            }
+            return classes;
+        }
+
+        /**
+         * Convert character sequence (a UTF-16 encoded string) to an array of unicode scalar values
+         * expressed as integers. If a valid UTF-16 surrogate pair is encountered, it is converted to
+         * two integers, the first being the equivalent unicode scalar  value, and the second being
+         * negative one (-1). This special mechanism is used to track the use of surrogate pairs while
+         * working with unicode scalar values, and permits maintaining indices that apply both to the
+         * input UTF-16 and out scalar value sequences.
+         * @returns a boolean indicating that content is present that triggers bidirectional processing
+         * @param cs a UTF-16 encoded character sequence
+         * @param chars an integer array to accept the converted scalar values, where the length of the
+         * array must be the same as the length of the input character sequence
+         * @throws IllegalArgumentException if the input sequence is not a valid UTF-16 string, e.g.,
+         * if it contains an isolated UTF-16 surrogate
+         */
+        private static boolean convertToScalar ( CharSequence cs, int[] chars ) throws IllegalArgumentException {
+            boolean triggered = false;
+            if ( chars.length != cs.length() ) {
+                throw new IllegalArgumentException ( "characters array length must match input sequence length" );
+            }
+            for ( int i = 0, n = chars.length; i < n; ) {
+                int chIn = cs.charAt ( i );
+                int chOut;
+                if ( chIn < 0xD800 ) {
+                    chOut = chIn;
+                } else if ( chIn < 0xDC00 ) {
+                    int chHi = chIn;
+                    int chLo;
+                    if ( ( i + 1 ) < n ) {
+                        chLo = cs.charAt ( i + 1 );
+                        if ( ( chLo >= 0xDC00 ) && ( chLo <= 0xDFFF ) ) {
+                            chOut = convertToScalar ( chHi, chLo );
+                        } else {
+                            throw new IllegalArgumentException ( "isolated high surrogate" );
+                        }
+                    } else {
+                        throw new IllegalArgumentException ( "truncated surrogate pair" );
+                    }
+                } else if ( chIn < 0xE000 ) {
+                    throw new IllegalArgumentException ( "isolated low surrogate" );
+                } else {
+                    chOut = chIn;
+                }
+                if ( ! triggered && triggersBidi ( chOut ) ) {
+                    triggered = true;
+                }
+                if ( ( chOut & 0xFF0000 ) == 0 ) {
+                    chars [ i++ ] = chOut;
+                } else {
+                    chars [ i++ ] = chOut;
+                    chars [ i++ ] = -1;
+                }
+            }
+            return triggered;
+        }
+
+        /**
+         * Convert UTF-16 surrogate pair to unicode scalar valuee.
+         * @returns a unicode scalar value
+         * @param chHi high (most significant or first) surrogate
+         * @param chLo low (least significant or second) surrogate
+         * @throws IllegalArgumentException if one of the input surrogates is not valid
+         */
+        private static int convertToScalar ( int chHi, int chLo ) {
+            if ( ( chHi < 0xD800 ) || ( chHi > 0xDBFF ) ) {
+                throw new IllegalArgumentException ( "bad high surrogate" );
+            } else if ( ( chLo < 0xDC00 ) || ( chLo > 0xDFFF ) ) {
+                throw new IllegalArgumentException ( "bad low surrogate" );
+            } else {
+                return ( ( ( chHi & 0x03FF ) << 10 ) | ( chLo & 0x03FF ) ) + 0x10000;
+            }
+        }
+
+        /**
+         * Determine of character CH triggers bidirectional processing. Bidirectional
+         * processing is deemed triggerable if CH is a strong right-to-left character,
+         * an arabic letter or number, or is a right-to-left embedding or override
+         * character.
+         * @returns true if character triggers bidirectional processing
+         * @param ch a unicode scalar value
+         */
+        private static boolean triggersBidi ( int ch ) {
+            switch ( BidiClassUtils.getBidiClass ( ch ) ) {
+            case R:
+            case AL:
+            case AN:
+            case RLE:
+            case RLO:
+                return true;
+            default:
+                return false;
+            }
+        }
+
+        private static void dump ( String header, int[] chars, int[] classes, int defaultLevel, int[] levels ) {
+            log.debug ( header );
+            log.debug ( "BD: default level(" + defaultLevel + ")" );
+            StringBuffer sb = new StringBuffer();
+            for ( int i = 0, n = chars.length; i < n; i++ ) {
+                int ch = chars [ i ];
+                sb.setLength(0);
+                if ( ( ch > 0x20 ) && ( ch < 0x7F ) ) {
+                    sb.append ( (char) ch );
+                } else {
+                    sb.append ( CharUtilities.charToNCRef ( ch ) );
+                }
+                for ( int k = sb.length(); k < 12; k++ ) {
+                    sb.append ( ' ' );
+                }
+                sb.append ( ": " + padRight ( getClassName ( classes[i] ), 4 ) + " " + levels[i] );
+                log.debug ( sb );
+            }
+        }
+
+        private static String getClassName ( int bc ) {
+            switch ( bc ) {
+            case L:                                     // left-to-right
+                return "L";
+            case LRE:                                   // left-to-right embedding
+                return "LRE";
+            case LRO:                                   // left-to-right override
+                return "LRO";
+            case R:                                     // right-to-left 
+                return "R";
+            case AL:                                    // right-to-left arabic
+                return "AL";
+            case RLE:                                   // right-to-left embedding
+                return "RLE";
+            case RLO:                                   // right-to-left override
+                return "RLO";
+            case PDF:                                   // pop directional formatting
+                return "PDF";
+            case EN:                                    // european number
+                return "EN";
+            case ES:                                    // european number separator
+                return "ES";
+            case ET:                                    // european number terminator
+                return "ET";
+            case AN:                                    // arabic number
+                return "AN";
+            case CS:                                    // common number separator
+                return "CS";
+            case NSM:                                   // non-spacing mark
+                return "NSM";
+            case BN:                                    // boundary neutral
+                return "BN";
+            case B:                                     // paragraph separator
+                return "B";
+            case S:                                     // segment separator
+                return "S";
+            case WS:                                    // whitespace
+                return "WS";
+            case ON:                                    // other neutrals
+                return "ON";
+            case SURROGATE:                             // placeholder for low surrogate
+                return "SUR";
+            default:
+                return "?";
+            }
+        }
+
+    }
+
+}

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

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

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java Thu Aug 19 19:46:41 2010
@@ -421,6 +421,8 @@ public class BlockLayoutManager extends 
 
             curBlockArea.setIPD(super.getContentAreaIPD());
 
+            curBlockArea.setBidiLevel ( getBlockFO().getBidiLevel() );
+
             TraitSetter.addBreaks(curBlockArea,
                     getBlockFO().getBreakBefore(), getBlockFO().getBreakAfter());
 
@@ -563,4 +565,3 @@ public class BlockLayoutManager extends 
     }
 
 }
-

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java Thu Aug 19 19:46:41 2010
@@ -51,6 +51,7 @@ import org.apache.fop.datatypes.URISpeci
 import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.extensions.ExternalDocument;
 import org.apache.fop.layoutmgr.inline.ImageLayout;
+import org.apache.fop.traits.WritingMode;
 
 /**
  * LayoutManager for an external-document extension element.  This class is instantiated by
@@ -190,7 +191,7 @@ public class ExternalDocumentLayoutManag
         vp.setIPD(imageSize.width);
         vp.setBPD(imageSize.height);
         vp.setContentPosition(imageLayout.getPlacement());
-        vp.setOffset(0);
+        vp.setBlockProgressionOffset(0);
 
         //Link them all together...
         lineArea.addInlineArea(vp);
@@ -231,8 +232,9 @@ public class ExternalDocumentLayoutManag
             referenceRect = new Rectangle(0, 0, imageSize.height, imageSize.width);
         }
         FODimension reldims = new FODimension(0, 0);
+        // [TBD] BIDI ALERT
         CTM pageCTM = CTM.getCTMandRelDims(pageSeq.getReferenceOrientation(),
-            Constants.EN_LR_TB, referenceRect, reldims);
+                                           WritingMode.LR_TB, referenceRect, reldims);
 
         Page page = new Page(referenceRect, pageNumber, pageNumberString, isBlank);
 

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/LayoutContext.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/LayoutContext.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/LayoutContext.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/LayoutContext.java Thu Aug 19 19:46:41 2010
@@ -26,6 +26,7 @@ import org.apache.fop.fo.Constants;
 import org.apache.fop.layoutmgr.inline.AlignmentContext;
 import org.apache.fop.layoutmgr.inline.HyphContext;
 import org.apache.fop.traits.MinOptMax;
+import org.apache.fop.traits.WritingMode;
 
 
 /**
@@ -92,7 +93,7 @@ public class LayoutContext {
     //overlap with refIPD. Need to investigate how best to refactor that.
 
     /** the writing mode established by the nearest ancestor reference area */
-    private int writingMode = Constants.EN_LR_TB;
+    private WritingMode writingMode = WritingMode.LR_TB;
 
     /** Current pending space-after or space-end from preceding area */
     private SpaceSpecifier trailingSpace;
@@ -564,7 +565,7 @@ public class LayoutContext {
      * Get the writing mode of the relevant reference area.
      * @return the applicable writing mode
      */
-    public int getWritingMode() {
+    public WritingMode getWritingMode() {
         return writingMode;
     }
 
@@ -572,7 +573,7 @@ public class LayoutContext {
      * Set the writing mode.
      * @param writingMode the writing mode
      */
-    public void setWritingMode(int writingMode) {
+    public void setWritingMode(WritingMode writingMode) {
         this.writingMode = writingMode;
     }
 

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java Thu Aug 19 19:46:41 2010
@@ -67,6 +67,7 @@ import org.apache.fop.fo.pagination.Side
 import org.apache.fop.fo.pagination.StaticContent;
 import org.apache.fop.fo.pagination.Title;
 import org.apache.fop.layoutmgr.inline.BasicLinkLayoutManager;
+import org.apache.fop.layoutmgr.inline.BidiLayoutManager;
 import org.apache.fop.layoutmgr.inline.CharacterLayoutManager;
 import org.apache.fop.layoutmgr.inline.ContentLayoutManager;
 import org.apache.fop.layoutmgr.inline.ExternalGraphicLayoutManager;
@@ -246,28 +247,9 @@ public class LayoutManagerMapping implem
     public static class BidiOverrideLayoutManagerMaker extends Maker {
         /** {@inheritDoc} */
         public void make(FONode node, List lms) {
-            /* [GA] remove broken code
-            if (false) {
-                // this is broken; it does nothing
-                // it should make something like an InlineStackingLM
-                super.make(node, lms);
-            } else {
-                ArrayList childList = new ArrayList();
-                // this is broken; it does nothing
-                // it should make something like an InlineStackingLM
-                super.make(node, childList);
-                for (int count = childList.size() - 1; count >= 0; count--) {
-                    LayoutManager lm = (LayoutManager) childList.get(count);
-                    if (lm instanceof InlineLevelLayoutManager) {
-                        LayoutManager blm = new BidiLayoutManager
-                            ((BidiOverride) node, (InlineLayoutManager) lm);
-                        lms.add(blm);
-                    } else {
-                        lms.add(lm);
-                    }
-                }
+            if ( node instanceof BidiOverride ) {
+                lms.add(new BidiLayoutManager((BidiOverride) node));
             }
-            */
         }
     }
 

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java Thu Aug 19 19:46:41 2010
@@ -77,8 +77,10 @@ public class PageSequenceLayoutManager e
     public void activateLayout() {
         initialize();
 
-        LineArea title = null;
+        // perform step 5.8 of refinement process (Unicode BIDI Processing)
+        BidiUtil.resolveInlineDirectionality(getPageSequence());
 
+        LineArea title = null;
         if (getPageSequence().getTitleFO() != null) {
             try {
                 ContentLayoutManager clm = getLayoutManagerMaker().

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java Thu Aug 19 19:46:41 2010
@@ -91,7 +91,7 @@ public abstract class AbstractGraphicsLa
         vp.setBPD(imageLayout.getViewportSize().height);
         vp.setContentPosition(placement);
         vp.setClip(imageLayout.isClipped());
-        vp.setOffset(0);
+        vp.setBlockProgressionOffset(0);
 
         // Common Border, Padding, and Background Properties
         TraitSetter.addBorders(vp, fobj.getCommonBorderPaddingBackground()

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java Thu Aug 19 19:46:41 2010
@@ -24,6 +24,7 @@ import org.apache.fop.datatypes.LengthBa
 import org.apache.fop.datatypes.SimplePercentBaseContext;
 import org.apache.fop.fo.Constants;
 import org.apache.fop.fonts.Font;
+import org.apache.fop.traits.WritingMode;
 
 /**
  * The alignment context is carried within a LayoutContext and as
@@ -171,13 +172,13 @@ public class AlignmentContext implements
      * @param lineHeight the computed value of the lineHeight property
      * @param writingMode the current writing mode
      */
-    public AlignmentContext(Font font, int lineHeight, int writingMode) {
+    public AlignmentContext(Font font, int lineHeight, WritingMode writingMode) {
         this.areaHeight = font.getAscender() - font.getDescender();
         this.lineHeight = lineHeight;
         this.xHeight = font.getXHeight();
         this.parentAlignmentContext = null;
         this.scaledBaselineTable
-                    = ScaledBaselineTableFactory.makeFontScaledBaselineTable(font, writingMode);
+            = ScaledBaselineTableFactory.makeFontScaledBaselineTable(font, writingMode);
         this.actualBaselineTable = scaledBaselineTable;
         this.alignmentBaselineIdentifier = getDominantBaselineIdentifier();
         this.alignmentPoint = font.getAscender();
@@ -301,7 +302,7 @@ public class AlignmentContext implements
      * Return the writing mode.
      * @return the writing mode
      */
-    public int getWritingMode() {
+    public WritingMode getWritingMode() {
         return scaledBaselineTable.getWritingMode();
     }
 
@@ -514,7 +515,7 @@ public class AlignmentContext implements
     }
 
     private boolean isHorizontalWritingMode() {
-        return (getWritingMode() == EN_LR_TB || getWritingMode() == EN_RL_TB);
+        return (getWritingMode() == WritingMode.LR_TB || getWritingMode() == WritingMode.RL_TB);
     }
 
     /** {@inheritDoc} */

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/BasicScaledBaselineTable.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/BasicScaledBaselineTable.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/BasicScaledBaselineTable.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/BasicScaledBaselineTable.java Thu Aug 19 19:46:41 2010
@@ -23,6 +23,7 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import org.apache.fop.fo.Constants;
+import org.apache.fop.traits.WritingMode;
 
 
 /**
@@ -38,7 +39,7 @@ public class BasicScaledBaselineTable im
     private int depth;
     private int xHeight;
     private int dominantBaselineIdentifier;
-    private int writingMode;
+    private WritingMode writingMode;
     private int dominantBaselineOffset;
     private int beforeEdgeOffset;
     private int afterEdgeOffset;
@@ -60,7 +61,7 @@ public class BasicScaledBaselineTable im
                                     , int depth
                                     , int xHeight
                                     , int dominantBaselineIdentifier
-                                    , int writingMode) {
+                                    , WritingMode writingMode) {
         this.altitude = altitude;
         this.depth = depth;
         this.xHeight = xHeight;
@@ -83,7 +84,7 @@ public class BasicScaledBaselineTable im
      * Return the writing mode for this baseline table.
      * @return the writing mode
      */
-    public int getWritingMode() {
+    public WritingMode getWritingMode() {
         return this.writingMode;
     }
 
@@ -137,7 +138,7 @@ public class BasicScaledBaselineTable im
     }
 
     private boolean isHorizontalWritingMode() {
-        return writingMode == EN_LR_TB || writingMode == EN_RL_TB;
+        return writingMode == WritingMode.LR_TB || writingMode == WritingMode.RL_TB;
     }
 
     /**

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/BidiLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/BidiLayoutManager.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/BidiLayoutManager.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/BidiLayoutManager.java Thu Aug 19 19:46:41 2010
@@ -19,56 +19,19 @@
 
 package org.apache.fop.layoutmgr.inline;
 
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.fop.area.inline.InlineArea;
 import org.apache.fop.fo.flow.BidiOverride;
 
-
 /**
- * If this bidi has a different writing mode direction
- * ltr or rtl than its parent writing mode then this
- * reverses the inline areas (at the character level).
+ * Layout manager for fo:bidi-override.
  */
-public class BidiLayoutManager extends LeafNodeLayoutManager {
-
-    private List children;
+public class BidiLayoutManager extends InlineLayoutManager {
 
     /**
      * Construct bidi layout manager.
-     * @param node bidi override FO
-     * @param cLM parent layout manager
+     * @param node an BidiOverride FONode
      */
-    public BidiLayoutManager(BidiOverride node, InlineLayoutManager cLM) {
+    public BidiLayoutManager(BidiOverride node) {
         super(node);
-        setParent(cLM);
-        children = new ArrayList();
-/*
-        for (int count = cLM.size() - 1; count >= 0; count--) {
-            InlineArea ia = cLM.get(count);
-            if (ia instanceof Word) {
-                // reverse word
-                Word word = (Word) ia;
-                StringBuffer sb = new StringBuffer(word.getWord());
-                word.setWord(sb.reverse().toString());
-            }
-            children.add(ia);
-        }
-*/
-    }
-
-    /** @return number of children */
-    public int size() {
-        return children.size();
-    }
-
-    /**
-     * @param index of child inline area
-     * @return a child inline area
-     */
-    public InlineArea get(int index) {
-        return (InlineArea) children.get(index);
     }
 
 }

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java Thu Aug 19 19:46:41 2010
@@ -76,13 +76,17 @@ public class CharacterLayoutManager exte
     private TextArea getCharacterInlineArea(Character node) {
         TextArea text = new TextArea();
         char ch = node.getCharacter();
+        int blockProgressionOffset = 0;
+        int level = node.bidiLevelAt(0);
         if (CharUtilities.isAnySpace(ch)) {
             // add space unless it's zero-width:
             if (!CharUtilities.isZeroWidthSpace(ch)) {
-                text.addSpace(ch, 0, CharUtilities.isAdjustableSpace(ch));
+                text.addSpace(ch, 0, CharUtilities.isAdjustableSpace(ch),
+                              blockProgressionOffset, level);
             }
         } else {
-            text.addWord(String.valueOf(ch), 0);
+            int[] levels = ( level >= 0 ) ? new int[] {level} : null;
+            text.addWord(String.valueOf(ch), 0, null, levels, blockProgressionOffset);
         }
         TraitSetter.setProducerID(text, node.getId());
         TraitSetter.addTextDecoration(text, node.getTextDecoration());
@@ -222,4 +226,3 @@ public class CharacterLayoutManager exte
     }
 
 }
-

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java Thu Aug 19 19:46:41 2010
@@ -201,7 +201,7 @@ public class InlineLayoutManager extends
         InlineArea area;
         if (hasInlineParent) {
             area = new InlineParent();
-            area.setOffset(0);
+            area.setBlockProgressionOffset(0);
         } else {
             area = new InlineBlockParent();
         }
@@ -458,12 +458,12 @@ public class InlineLayoutManager extends
                                         || lastLM instanceof InlineLevelLayoutManager);
         parent.setBPD(alignmentContext.getHeight());
         if (parent instanceof InlineParent) {
-            parent.setOffset(alignmentContext.getOffset());
+            parent.setBlockProgressionOffset(alignmentContext.getOffset());
         } else if (parent instanceof InlineBlockParent) {
             // All inline elements are positioned by the renderers relative to
             // the before edge of their content rectangle
             if (borderProps != null) {
-                parent.setOffset(borderProps.getPaddingBefore(false, this)
+                parent.setBlockProgressionOffset(borderProps.getPaddingBefore(false, this)
                                 + borderProps.getBorderBeforeWidth(false));
             }
         }

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java Thu Aug 19 19:46:41 2010
@@ -344,4 +344,10 @@ public class LeaderLayoutManager extends
         this.contentAreaIPD = contentAreaIPD;
     }
 
+    /** {@inheritDoc} */
+    public void reset() {
+        childLMs.clear();
+        super.reset();
+    }
+
 }

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/LeafNodeLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/LeafNodeLayoutManager.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/LeafNodeLayoutManager.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/LeafNodeLayoutManager.java Thu Aug 19 19:46:41 2010
@@ -225,7 +225,7 @@ public abstract class LeafNodeLayoutMana
      * @param context the layout context used for adding the area
      */
     protected void offsetArea(InlineArea area, LayoutContext context) {
-        area.setOffset(alignmentContext.getOffset());
+        area.setBlockProgressionOffset(alignmentContext.getOffset());
     }
 
     /**

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java Thu Aug 19 19:46:41 2010
@@ -44,6 +44,7 @@ import org.apache.fop.fonts.FontTriplet;
 import org.apache.fop.hyphenation.Hyphenation;
 import org.apache.fop.hyphenation.Hyphenator;
 import org.apache.fop.layoutmgr.Adjustment;
+import org.apache.fop.layoutmgr.BidiUtil;
 import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
 import org.apache.fop.layoutmgr.BreakElement;
 import org.apache.fop.layoutmgr.BreakingAlgorithm;
@@ -91,6 +92,7 @@ public class LineLayoutManager extends I
 
     /** {@inheritDoc} */
     public void initialize() {
+        bidiLevel = fobj.getBidiLevel();
         textAlignment = fobj.getTextAlign();
         textAlignmentLast = fobj.getTextAlignLast();
         textIndent = fobj.getTextIndent();
@@ -155,6 +157,7 @@ public class LineLayoutManager extends I
     }
 
 
+    private int bidiLevel = -1;
     private int textAlignment = EN_JUSTIFY;
     private int textAlignmentLast;
     private int effectiveAlignment;
@@ -1487,6 +1490,7 @@ public class LineLayoutManager extends I
         }
         lineArea.setBPD(lbp.lineHeight);
         lineArea.setIPD(lbp.lineWidth);
+        lineArea.setBidiLevel(bidiLevel);
         lineArea.addTrait(Trait.SPACE_BEFORE, new Integer(lbp.spaceBefore));
         lineArea.addTrait(Trait.SPACE_AFTER, new Integer(lbp.spaceAfter));
         alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline);
@@ -1595,7 +1599,10 @@ public class LineLayoutManager extends I
                 && (!context.isLastArea() || !isLastPosition)) {
             lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
         }
-        lineArea.finalise();
+        lineArea.finish();
+        if ( lineArea.getBidiLevel() >= 0 ) {
+            BidiUtil.reorder ( lineArea );
+        }
         parentLayoutManager.addChildArea(lineArea);
     }
 
@@ -1645,6 +1652,9 @@ public class LineLayoutManager extends I
             blocklc.setTrailingSpace(new SpaceSpecifier(false));
         }
         lineArea.updateExtentsFromChildren();
+        if ( lineArea.getBidiLevel() >= 0 ) {
+            BidiUtil.reorder ( lineArea );
+        }
         parentLayoutManager.addChildArea(lineArea);
     }
 
@@ -1679,4 +1689,3 @@ public class LineLayoutManager extends I
     }
 
 }
-

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java?rev=987282&r1=987281&r2=987282&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java Thu Aug 19 19:46:41 2010
@@ -101,7 +101,7 @@ public class PageNumberLayoutManager ext
         TraitSetter.setProducerID(ta, fobj.getId());
         ta.setIPD(baseArea.getIPD());
         ta.setBPD(baseArea.getBPD());
-        ta.setOffset(baseArea.getOffset());
+        ta.setBlockProgressionOffset(baseArea.getBlockProgressionOffset());
         ta.setBaselineOffset(baseArea.getBaselineOffset());
         ta.addTrait(Trait.COLOR, fobj.getColor()); //only to initialize the trait map
         ta.getTraits().putAll(baseArea.getTraits());



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