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 [19/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...

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java?rev=1293736&r1=1293735&r2=1293736&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java Sun Feb 26 02:29:01 2012
@@ -32,6 +32,7 @@ import org.apache.fop.area.Area;
 import org.apache.fop.area.LineArea;
 import org.apache.fop.area.Trait;
 import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.complexscripts.bidi.BidiResolver;
 import org.apache.fop.datatypes.Length;
 import org.apache.fop.datatypes.Numeric;
 import org.apache.fop.fo.Constants;
@@ -111,6 +112,7 @@ public class LineLayoutManager extends I
         private final double dAdjust; // Percentage to adjust (stretch or shrink)
         private final double ipdAdjust; // Percentage to adjust (stretch or shrink)
         private final int startIndent;
+        private final int endIndent;
         private final int lineHeight;
         private final int lineWidth;
         private final int spaceBefore;
@@ -119,8 +121,8 @@ public class LineLayoutManager extends I
 
         LineBreakPosition(                                       // CSOK: ParameterNumber
                 LayoutManager lm, int index, int startIndex, int breakIndex,
-                int shrink, int stretch, int diff, double ipdA, double adjust, int ind,
-                int lh, int lw, int sb, int sa, int bl) {
+                int shrink, int stretch, int diff, double ipdA, double adjust, int si,
+                int ei, int lh, int lw, int sb, int sa, int bl) {
             super(lm, breakIndex);
             availableShrink = shrink;
             availableStretch = stretch;
@@ -129,7 +131,8 @@ public class LineLayoutManager extends I
             this.startIndex = startIndex;
             ipdAdjust = ipdA;
             dAdjust = adjust;
-            startIndent = ind;
+            startIndent = si;
+            endIndent = ei;
             lineHeight = lh;
             lineWidth = lw;
             spaceBefore = sb;
@@ -140,6 +143,7 @@ public class LineLayoutManager extends I
     }
 
 
+    private int bidiLevel = -1;
     private int textAlignment = EN_JUSTIFY;
     private int textAlignmentLast;
     private int effectiveAlignment;
@@ -333,13 +337,38 @@ public class LineLayoutManager extends I
                                 int total) {
             // compute indent and adjustment ratio, according to
             // the value of text-align and text-align-last
-            int indent = 0;
+            int startIndent;
+            int endIndent;
             int difference = bestActiveNode.difference;
             int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast;
-            indent += (textAlign == Constants.EN_CENTER)
+
+            switch ( textAlign ) {
+            case Constants.EN_START:
+                startIndent = 0;
+                endIndent = difference > 0 ? difference : 0;
+                break;
+            case Constants.EN_END:
+                startIndent = difference > 0 ? difference : 0;
+                endIndent = 0;
+                break;
+            case Constants.EN_CENTER:
+                startIndent = difference / 2;
+                endIndent = startIndent;
+                break;
+            default:
+            case Constants.EN_JUSTIFY:
+                startIndent = 0;
+                endIndent = 0;
+                break;
+            }
+
+            /*
+            startIndent += (textAlign == Constants.EN_CENTER)
                       ? difference / 2 : (textAlign == Constants.EN_END) ? difference : 0;
-            indent += (bestActiveNode.line == 1 && indentFirstPart && isFirstInBlock)
+            */
+            startIndent += (bestActiveNode.line == 1 && indentFirstPart && isFirstInBlock)
                       ? textIndent : 0;
+
             double ratio = (textAlign == Constants.EN_JUSTIFY
                 || difference < 0 && -difference <= bestActiveNode.availableShrink)
                         ? bestActiveNode.adjustRatio : 0;
@@ -377,7 +406,7 @@ public class LineLayoutManager extends I
                    bestActiveNode.availableShrink - (addedPositions > 0
                        ? 0 : ((Paragraph) par).lineFiller.getShrink()),
                    bestActiveNode.availableStretch,
-                   difference, ratio, indent), activePossibility);
+                   difference, ratio, startIndent, endIndent), activePossibility);
             addedPositions++;
         }
 
@@ -389,7 +418,8 @@ public class LineLayoutManager extends I
 
         private LineBreakPosition makeLineBreakPosition(         // CSOK: ParameterNumber
                 KnuthSequence par, int firstElementIndex, int lastElementIndex, int availableShrink,
-                int availableStretch, int difference, double ratio, int indent) {
+                int availableStretch, int difference, double ratio, int startIndent,
+                int endIndent) {
             // line height calculation - spaceBefore may differ from spaceAfter
             // by 1mpt due to rounding
             int spaceBefore = (lineHeight - lead - follow) / 2;
@@ -456,14 +486,14 @@ public class LineLayoutManager extends I
                                              knuthParagraphs.indexOf(par),
                                              firstElementIndex, lastElementIndex,
                                              availableShrink, availableStretch,
-                                             difference, ratio, 0, indent,
+                                             difference, ratio, 0, startIndent, endIndent,
                                              0, ipd, 0, 0, 0);
             } else {
                 return new LineBreakPosition(thisLLM,
                                              knuthParagraphs.indexOf(par),
                                              firstElementIndex, lastElementIndex,
                                              availableShrink, availableStretch,
-                                             difference, ratio, 0, indent,
+                                             difference, ratio, 0, startIndent, endIndent,
                                              lineLead + lineFollow,
                                              ipd, spaceBefore, spaceAfter,
                                              lineLead);
@@ -544,6 +574,7 @@ public class LineLayoutManager extends I
     /** {@inheritDoc} */
     @Override
     public void initialize() {
+        bidiLevel = fobj.getBidiLevel();
         textAlignment = fobj.getTextAlign();
         textAlignmentLast = fobj.getTextAlignLast();
         textIndent = fobj.getTextIndent();
@@ -1429,8 +1460,12 @@ public class LineLayoutManager extends I
         if (lbp.startIndent != 0) {
             lineArea.addTrait(Trait.START_INDENT, lbp.startIndent);
         }
+        if (lbp.endIndent != 0) {
+            lineArea.addTrait(Trait.END_INDENT, new Integer(lbp.endIndent));
+        }
         lineArea.setBPD(lbp.lineHeight);
         lineArea.setIPD(lbp.lineWidth);
+        lineArea.setBidiLevel(bidiLevel);
         lineArea.addTrait(Trait.SPACE_BEFORE, lbp.spaceBefore);
         lineArea.addTrait(Trait.SPACE_AFTER, lbp.spaceAfter);
         alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline);
@@ -1506,7 +1541,10 @@ public class LineLayoutManager extends I
                 && (!context.isLastArea() || !isLastPosition)) {
             lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
         }
-        lineArea.finalise();
+        lineArea.finish();
+        if ( lineArea.getBidiLevel() >= 0 ) {
+            BidiResolver.reorder ( lineArea );
+        }
         parentLayoutManager.addChildArea(lineArea);
     }
 
@@ -1556,6 +1594,9 @@ public class LineLayoutManager extends I
             blocklc.setTrailingSpace(new SpaceSpecifier(false));
         }
         lineArea.updateExtentsFromChildren();
+        if ( lineArea.getBidiLevel() >= 0 ) {
+            BidiResolver.reorder ( lineArea );
+        }
         parentLayoutManager.addChildArea(lineArea);
     }
 

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/PageNumberCitationLastLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/PageNumberCitationLastLayoutManager.java?rev=1293736&r1=1293735&r2=1293736&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/PageNumberCitationLastLayoutManager.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/PageNumberCitationLastLayoutManager.java Sun Feb 26 02:29:01 2012
@@ -56,22 +56,24 @@ public class PageNumberCitationLastLayou
      */
     private InlineArea getPageNumberCitationLastInlineArea(LayoutManager parentLM) {
         TextArea text = null;
-        resolved = false;
+        int level = getBidiLevel();
         if (!getPSLM().associateLayoutManagerID(fobj.getRefId())) {
             text = new UnresolvedPageNumber(fobj.getRefId(), font, UnresolvedPageNumber.LAST);
             getPSLM().addUnresolvedArea(fobj.getRefId(), (Resolvable)text);
             String str = "MMM"; // reserve three spaces for page number
             int width = getStringWidth(str);
+            text.setBidiLevel(level);
             text.setIPD(width);
+            resolved = false;
         } else {
             PageViewport page = getPSLM().getLastPVWithID(fobj.getRefId());
             String str = page.getPageNumberString();
             // get page string from parent, build area
             text = new TextArea();
             int width = getStringWidth(str);
-            text.addWord(str, 0);
+            text.setBidiLevel(level);
+            text.addWord(str, 0, level);
             text.setIPD(width);
-
             resolved = true;
         }
 

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/PageNumberCitationLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/PageNumberCitationLayoutManager.java?rev=1293736&r1=1293735&r2=1293736&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/PageNumberCitationLayoutManager.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/PageNumberCitationLayoutManager.java Sun Feb 26 02:29:01 2012
@@ -50,27 +50,32 @@ public class PageNumberCitationLayoutMan
     /**
      * if id can be resolved then simply return a word, otherwise
      * return a resolvable area
+     *
+     * TODO: [GA] May need to run bidi algorithm and script processor
+     * on resolved page number.
      */
     private InlineArea getPageNumberCitationInlineArea() {
         PageViewport page = getPSLM().getFirstPVWithID(fobj.getRefId());
         TextArea text = null;
+        int level = getBidiLevel();
         if (page != null) {
             String str = page.getPageNumberString();
             // get page string from parent, build area
             text = new TextArea();
-            int width = getStringWidth(str);
-            text.addWord(str, 0);
-            text.setIPD(width);
+            int width = getStringWidth(str);    // TODO: [GA] !I18N!
+            text.setBidiLevel(level);
+            text.addWord(str, 0, level);
+            text.setIPD(width);                 // TODO: [GA] !I18N!
             resolved = true;
         } else {
-            resolved = false;
             text = new UnresolvedPageNumber(fobj.getRefId(), font);
             String str = "MMM"; // reserve three spaces for page number
-            int width = getStringWidth(str);
-            text.setIPD(width);
+            int width = getStringWidth(str);    // TODO: [GA] !I18N!
+            text.setBidiLevel(level);
+            text.setIPD(width);                 // TODO: [GA] !I18N!
+            resolved = false;
         }
         updateTextAreaTraits(text);
-
         return text;
     }
 

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java?rev=1293736&r1=1293735&r2=1293736&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java Sun Feb 26 02:29:01 2012
@@ -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());

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/ScaledBaselineTable.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/ScaledBaselineTable.java?rev=1293736&r1=1293735&r2=1293736&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/ScaledBaselineTable.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/ScaledBaselineTable.java Sun Feb 26 02:29:01 2012
@@ -20,6 +20,7 @@
 package org.apache.fop.layoutmgr.inline;
 
 import org.apache.fop.fo.Constants;
+import org.apache.fop.traits.WritingMode;
 
 
 /**
@@ -43,7 +44,7 @@ final class ScaledBaselineTable {
 
     private final int dominantBaselineIdentifier;
 
-    private final int writingMode;
+    private final WritingMode writingMode;
 
     private final int dominantBaselineOffset;
 
@@ -66,7 +67,7 @@ final class ScaledBaselineTable {
             int depth,
             int xHeight,
             int dominantBaselineIdentifier,
-            int writingMode) {
+            WritingMode writingMode) {
         this.altitude = altitude;
         this.depth = depth;
         this.xHeight = xHeight;
@@ -89,7 +90,7 @@ final class ScaledBaselineTable {
      * Return the writing mode for this baseline table.
      * @return the writing mode
      */
-    int getWritingMode() {
+    WritingMode getWritingMode() {
         return this.writingMode;
     }
 
@@ -139,7 +140,7 @@ final class ScaledBaselineTable {
     }
 
     private boolean isHorizontalWritingMode() {
-        return writingMode == Constants.EN_LR_TB || writingMode == Constants.EN_RL_TB;
+        return writingMode.isHorizontal();
     }
 
     /**

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java?rev=1293736&r1=1293735&r2=1293736&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java Sun Feb 26 02:29:01 2012
@@ -19,6 +19,7 @@
 
 package org.apache.fop.layoutmgr.inline;
 
+import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
@@ -29,6 +30,8 @@ import org.apache.commons.logging.LogFac
 
 import org.apache.fop.area.Trait;
 import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.complexscripts.fonts.GlyphPositioningTable;
+import org.apache.fop.complexscripts.util.CharScript;
 import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FOText;
 import org.apache.fop.fonts.Font;
@@ -71,6 +74,7 @@ public class TextLayoutManager extends L
 
         private final int startIndex;
         private final int breakIndex;
+        private int wordCharLength;
         private final int wordSpaceCount;
         private int letterSpaceCount;
         private MinOptMax areaIPD;
@@ -78,14 +82,17 @@ public class TextLayoutManager extends L
         private final boolean isSpace;
         private boolean breakOppAfter;
         private final Font font;
+        private final int level;
+        private final int[][] gposAdjustments;
 
-        AreaInfo(                                                // CSOK: ParameterNumber
-                int startIndex, int breakIndex, int wordSpaceCount, int letterSpaceCount,
-                MinOptMax areaIPD, boolean isHyphenated, boolean isSpace, boolean breakOppAfter,
-                Font font) {
+        AreaInfo                                                // CSOK: ParameterNumber
+            (int startIndex, int breakIndex, int wordSpaceCount, int letterSpaceCount,
+             MinOptMax areaIPD, boolean isHyphenated, boolean isSpace, boolean breakOppAfter,
+             Font font, int level, int[][] gposAdjustments) {
             assert startIndex <= breakIndex;
             this.startIndex = startIndex;
             this.breakIndex = breakIndex;
+            this.wordCharLength = -1;
             this.wordSpaceCount = wordSpaceCount;
             this.letterSpaceCount = letterSpaceCount;
             this.areaIPD = areaIPD;
@@ -93,10 +100,26 @@ public class TextLayoutManager extends L
             this.isSpace = isSpace;
             this.breakOppAfter = breakOppAfter;
             this.font = font;
+            this.level = level;
+            this.gposAdjustments = gposAdjustments;
         }
 
-        private int getCharLength() {
-            return breakIndex - startIndex;
+        /**
+         * Obtain number of 'characters' contained in word. If word
+         * is mapped, then this number may be less than or greater than the
+         * original length (breakIndex - startIndex). We compute and
+         * memoize thius length upon first invocation of this method.
+         */
+        private int getWordLength() {
+            if ( wordCharLength == -1 ) {
+                if ( foText.hasMapping ( startIndex, breakIndex ) ) {
+                    wordCharLength = foText.getMapping ( startIndex, breakIndex ).length();
+                } else {
+                    assert breakIndex >= startIndex;
+                    wordCharLength = breakIndex - startIndex;
+                }
+            }
+            return wordCharLength;
         }
 
         private void addToAreaIPD(MinOptMax idp) {
@@ -104,16 +127,16 @@ public class TextLayoutManager extends L
         }
 
         public String toString() {
-            return "AreaInfo["
-                    + "letterSpaceCount = " + letterSpaceCount
-                    + ", wordSpaceCount = " + wordSpaceCount
+            return super.toString() + "{"
+                    + "interval = [" + startIndex + "," + breakIndex + "]"
+                    + ", isSpace = " + isSpace
+                    + ", level = " + level
                     + ", areaIPD = " + areaIPD
-                    + ", startIndex = " + startIndex
-                    + ", breakIndex = " + breakIndex
+                    + ", letterSpaceCount = " + letterSpaceCount
+                    + ", wordSpaceCount = " + wordSpaceCount
                     + ", isHyphenated = " + isHyphenated
-                    + ", isSpace = " + isSpace
                     + ", font = " + font
-                    + "]";
+                    + "}";
         }
     }
 
@@ -149,7 +172,7 @@ public class TextLayoutManager extends L
      * be used to influence the start position of the first letter. The entry i+1 defines the
      * cursor advancement after the character i. A null entry means no special advancement.
      */
-    private final MinOptMax[] letterAdjustArray; //size = textArray.length + 1
+    private final MinOptMax[] letterSpaceAdjustArray; //size = textArray.length + 1
 
     /** Font used for the space between words. */
     private Font spaceFont = null;
@@ -192,7 +215,7 @@ public class TextLayoutManager extends L
      */
     public TextLayoutManager(FOText node) {
         foText = node;
-        letterAdjustArray = new MinOptMax[node.length() + 1];
+        letterSpaceAdjustArray = new MinOptMax[node.length() + 1];
         areaInfos = new ArrayList();
     }
 
@@ -269,7 +292,9 @@ public class TextLayoutManager extends L
             }
             if (tbpNext.getLeafPos() != -1) {
                 areaInfo = (AreaInfo) areaInfos.get(tbpNext.getLeafPos());
-                if (lastAreaInfo == null || areaInfo.font != lastAreaInfo.font) {
+                if (lastAreaInfo == null
+                    || ( areaInfo.font != lastAreaInfo.font )
+                    || ( areaInfo.level != lastAreaInfo.level ) ) {
                     if (lastAreaInfo != null) {
                         addAreaInfoAreas(lastAreaInfo, wordSpaceCount,
                                 letterSpaceCount, firstAreaInfoIndex,
@@ -291,6 +316,7 @@ public class TextLayoutManager extends L
             addAreaInfoAreas(lastAreaInfo, wordSpaceCount, letterSpaceCount, firstAreaInfoIndex,
                     lastAreaInfoIndex, realWidth, context);
         }
+
     }
 
     private void addAreaInfoAreas(AreaInfo areaInfo, int wordSpaceCount, int letterSpaceCount,
@@ -301,7 +327,7 @@ public class TextLayoutManager extends L
         // changes. However, it seems as if they should use the AreaInfo from
         // firstAreaInfoIndex.. lastAreaInfoIndex rather than just the last areaInfo.
         // This needs to be checked.
-        int textLength = areaInfo.getCharLength();
+        int textLength = areaInfo.getWordLength();
         if (areaInfo.letterSpaceCount == textLength && !areaInfo.isHyphenated
                 && context.isLastArea()) {
             // the line ends at a character like "/" or "-";
@@ -311,8 +337,8 @@ public class TextLayoutManager extends L
         }
 
         for (int i = areaInfo.startIndex; i < areaInfo.breakIndex; i++) {
-            MinOptMax letterAdjustment = letterAdjustArray[i + 1];
-            if (letterAdjustment != null && letterAdjustment.isElastic()) {
+            MinOptMax letterSpaceAdjustment = letterSpaceAdjustArray[i + 1];
+            if (letterSpaceAdjustment != null && letterSpaceAdjustment.isElastic()) {
                 letterSpaceCount++;
             }
         }
@@ -387,21 +413,27 @@ public class TextLayoutManager extends L
 
     private final class TextAreaBuilder {
 
-        private final MinOptMax width;
-        private final int adjust;
-        private final LayoutContext context;
-        private final int firstIndex;
-        private final int lastIndex;
-        private final boolean isLastArea;
-        private final Font font;
-
-        private int blockProgressionDimension;
-        private AreaInfo areaInfo;
-        private StringBuffer wordChars;
-        private int[] letterAdjust;
-        private int letterAdjustIndex;
-
-        private TextArea textArea;
+        // constructor initialized state
+        private final MinOptMax width;          // content ipd
+        private final int adjust;               // content ipd adjustment
+        private final LayoutContext context;    // layout context
+        private final int firstIndex;           // index of first AreaInfo
+        private final int lastIndex;            // index of last AreaInfo
+        private final boolean isLastArea;       // true if last inline area in line area
+        private final Font font;                // applicable font
+
+        // other, non-constructor state
+        private TextArea textArea;              // text area being constructed
+        private int blockProgressionDimension;  // calculated bpd
+        private AreaInfo areaInfo;              // current area info when iterating over words
+        private StringBuffer wordChars;         // current word's character buffer
+        private int[] letterSpaceAdjust;        // current word's letter space adjustments
+        private int letterSpaceAdjustIndex;     // last written letter space adjustment index
+        private int[] wordLevels;               // current word's bidi levels
+        private int wordLevelsIndex;            // last written bidi level index
+        private int wordIPD;                    // accumulated ipd of current word
+        private int[][] gposAdjustments;        // current word's glyph position adjustments
+        private int gposAdjustmentsIndex;       // last written glyph position adjustment index
 
         /**
          * Creates a new <code>TextAreaBuilder</code> which itself builds an inline word area. This
@@ -432,7 +464,7 @@ public class TextLayoutManager extends L
             calcBlockProgressionDimension();
             setBlockProgressionDimension();
             setBaselineOffset();
-            setOffset();
+            setBlockProgressionOffset();
             setText();
             TraitSetter.addFontTraits(textArea, font);
             textArea.addTrait(Trait.COLOR, foText.getColor());
@@ -471,11 +503,11 @@ public class TextLayoutManager extends L
             textArea.setBaselineOffset(font.getAscender());
         }
 
-        private void setOffset() {
+        private void setBlockProgressionOffset() {
             if (blockProgressionDimension == alignmentContext.getHeight()) {
-                textArea.setOffset(0);
+                textArea.setBlockProgressionOffset(0);
             } else {
-                textArea.setOffset(alignmentContext.getOffset());
+                textArea.setBlockProgressionOffset(alignmentContext.getOffset());
             }
         }
 
@@ -483,7 +515,7 @@ public class TextLayoutManager extends L
          * Sets the text of the TextArea, split into words and spaces.
          */
         private void setText() {
-            int wordStartIndex = -1;
+            int areaInfoIndex = -1;
             int wordCharLength = 0;
             for (int wordIndex = firstIndex; wordIndex <= lastIndex; wordIndex++) {
                 areaInfo = getAreaInfo(wordIndex);
@@ -491,15 +523,15 @@ public class TextLayoutManager extends L
                     addSpaces();
                 } else {
                     // areaInfo stores information about a word fragment
-                    if (wordStartIndex == -1) {
+                    if (areaInfoIndex == -1) {
                         // here starts a new word
-                        wordStartIndex = wordIndex;
+                        areaInfoIndex = wordIndex;
                         wordCharLength = 0;
                     }
-                    wordCharLength += areaInfo.getCharLength();
+                    wordCharLength += areaInfo.getWordLength();
                     if (isWordEnd(wordIndex)) {
-                        addWord(wordStartIndex, wordIndex, wordCharLength);
-                        wordStartIndex = -1;
+                        addWord(areaInfoIndex, wordIndex, wordCharLength);
+                        areaInfoIndex = -1;
                     }
                 }
             }
@@ -509,26 +541,78 @@ public class TextLayoutManager extends L
             return areaInfoIndex == lastIndex || getAreaInfo(areaInfoIndex + 1).isSpace;
         }
 
-        private void addWord(int startIndex, int endIndex, int charLength) {
+        /**
+         * Add word with fragments from STARTINDEX to ENDINDEX, where
+         * total length of (possibly mapped) word is CHARLENGTH.
+         * A word is composed from one or more word fragments, where each
+         * fragment corresponds to distinct instance in a sequence of
+         * area info instances starting at STARTINDEX continuing through (and
+         * including)  ENDINDEX.
+         * @param startIndex index of first area info of word to add
+         * @param endIndex index of last area info of word to add
+         * @param wordLength number of (mapped) characters in word
+         */
+        private void addWord(int startIndex, int endIndex, int wordLength) {
+            int blockProgressionOffset = 0;
+            boolean gposAdjusted = false;
             if (isHyphenated(endIndex)) {
-                charLength++;
+                // TODO may be problematic in some I18N contexts [GA]
+                wordLength++;
             }
-            initWord(charLength);
+            initWord(wordLength);
+            // iterate over word's fragments, adding word chars (with bidi
+            // levels), letter space adjustments, and glyph position adjustments
             for (int i = startIndex; i <= endIndex; i++) {
                 AreaInfo wordAreaInfo = getAreaInfo(i);
                 addWordChars(wordAreaInfo);
                 addLetterAdjust(wordAreaInfo);
+                if ( addGlyphPositionAdjustments(wordAreaInfo) ) {
+                    gposAdjusted = true;
+                }
             }
             if (isHyphenated(endIndex)) {
+                // TODO may be problematic in some I18N contexts [GA]
                 addHyphenationChar();
             }
-            textArea.addWord(wordChars.toString(), 0, letterAdjust);
+            if ( !gposAdjusted ) {
+                gposAdjustments = null;
+            }
+            textArea.addWord(wordChars.toString(), wordIPD, letterSpaceAdjust,
+                             getNonEmptyLevels(), gposAdjustments, blockProgressionOffset);
+        }
+
+        private int[] getNonEmptyLevels() {
+            if ( wordLevels != null ) {
+                assert wordLevelsIndex <= wordLevels.length;
+                boolean empty = true;
+                for ( int i = 0, n = wordLevelsIndex; i < n; i++ ) {
+                    if ( wordLevels [ i ] >= 0 ) {
+                        empty = false;
+                        break;
+                    }
+                }
+                return empty ? null : wordLevels;
+            } else {
+                return null;
+            }
         }
 
-        private void initWord(int charLength) {
-            wordChars = new StringBuffer(charLength);
-            letterAdjust = new int[charLength];
-            letterAdjustIndex = 0;
+        /**
+         * Fully allocate word character buffer, letter space adjustments
+         * array, bidi levels array, and glyph position adjustments array.
+         * based on full word length, including all (possibly mapped) fragments.
+         * @param wordLength length of word including all (possibly mapped) fragments
+         */
+        private void initWord(int wordLength) {
+            wordChars = new StringBuffer(wordLength);
+            letterSpaceAdjust = new int[wordLength];
+            letterSpaceAdjustIndex = 0;
+            wordLevels = new int[wordLength];
+            wordLevelsIndex = 0;
+            Arrays.fill ( wordLevels, -1 );
+            gposAdjustments = new int[wordLength][4];
+            gposAdjustmentsIndex = 0;
+            wordIPD = 0;
         }
 
         private boolean isHyphenated(int endIndex) {
@@ -537,27 +621,112 @@ public class TextLayoutManager extends L
 
         private void addHyphenationChar() {
             wordChars.append(foText.getCommonHyphenation().getHyphChar(font));
+            // [TBD] expand bidi word levels, letter space adjusts, gpos adjusts
+            // [TBD] [GA] problematic in bidi context... what is level of hyphen?
         }
 
+        /**
+         * Given a word area info associated with a word fragment,
+         * (1) concatenate (possibly mapped) word characters to word character buffer;
+         * (2) concatenante (possibly mapped) word bidi levels to levels buffer;
+         * (3) update word's IPD with optimal IPD of fragment.
+         * @param wordAreaInfo fragment info
+         */
         private void addWordChars(AreaInfo wordAreaInfo) {
-            for (int i = wordAreaInfo.startIndex; i < wordAreaInfo.breakIndex; i++) {
-                wordChars.append(foText.charAt(i));
+            int s = wordAreaInfo.startIndex;
+            int e = wordAreaInfo.breakIndex;
+            if ( foText.hasMapping ( s, e ) ) {
+                wordChars.append ( foText.getMapping ( s, e ) );
+                addWordLevels ( foText.getMappingBidiLevels ( s, e ) );
+            } else {
+                for (int i = s; i < e; i++) {
+                    wordChars.append(foText.charAt(i));
+                }
+                addWordLevels ( foText.getBidiLevels ( s, e ) );
             }
+            wordIPD += wordAreaInfo.areaIPD.getOpt();
         }
 
+        /**
+         * Given a (possibly null) bidi levels array associated with a word fragment,
+         * concatenante (possibly mapped) word bidi levels to levels buffer.
+         * @param levels bidi levels array or null
+         */
+        private void addWordLevels ( int[] levels ) {
+            int numLevels = ( levels != null ) ? levels.length : 0;
+            if ( numLevels > 0 ) {
+                int need = wordLevelsIndex + numLevels;
+                if ( need <= wordLevels.length ) {
+                    System.arraycopy ( levels, 0, wordLevels, wordLevelsIndex, numLevels );
+                } else {
+                    throw new IllegalStateException
+                        ( "word levels array too short: expect at least "
+                          + need + " entries, but has only " + wordLevels.length + " entries" );
+                }
+            }
+            wordLevelsIndex += numLevels;
+        }
+
+        /**
+         * Given a word area info associated with a word fragment,
+         * concatenate letter space adjustments for each (possibly mapped) character.
+         * @param wordAreaInfo fragment info
+         */
         private void addLetterAdjust(AreaInfo wordAreaInfo) {
             int letterSpaceCount = wordAreaInfo.letterSpaceCount;
-            for (int i = wordAreaInfo.startIndex; i < wordAreaInfo.breakIndex; i++) {
-                if (letterAdjustIndex > 0) {
-                    MinOptMax adj = letterAdjustArray[i];
-                    letterAdjust[letterAdjustIndex] = adj == null ? 0 : adj.getOpt();
+            int wordLength = wordAreaInfo.getWordLength();
+            int taAdjust = textArea.getTextLetterSpaceAdjust();
+            for ( int i = 0, n = wordLength; i < n; i++ ) {
+                int j = letterSpaceAdjustIndex + i;
+                if ( j > 0 ) {
+                    int k = wordAreaInfo.startIndex + i;
+                    MinOptMax adj = ( k < letterSpaceAdjustArray.length )
+                        ? letterSpaceAdjustArray [ k ] : null;
+                    letterSpaceAdjust [ j ] = ( adj == null ) ? 0 : adj.getOpt();
                 }
-                if (letterSpaceCount > 0) {
-                    letterAdjust[letterAdjustIndex] += textArea.getTextLetterSpaceAdjust();
+                if ( letterSpaceCount > 0 ) {
+                    letterSpaceAdjust [ j ] += taAdjust;
                     letterSpaceCount--;
                 }
-                letterAdjustIndex++;
             }
+            letterSpaceAdjustIndex += wordLength;
+        }
+
+        /**
+         * Given a word area info associated with a word fragment,
+         * concatenate glyph position adjustments for each (possibly mapped) character.
+         * @param wordAreaInfo fragment info
+         * @return true if an adjustment was non-zero
+         */
+        private boolean addGlyphPositionAdjustments(AreaInfo wordAreaInfo) {
+            boolean adjusted = false;
+            int[][] gpa = wordAreaInfo.gposAdjustments;
+            int numAdjusts = ( gpa != null ) ? gpa.length : 0;
+            int wordLength = wordAreaInfo.getWordLength();
+            if ( numAdjusts > 0 ) {
+                int need = gposAdjustmentsIndex + numAdjusts;
+                if ( need <= gposAdjustments.length ) {
+                    for ( int i = 0, n = wordLength, j = 0; i < n; i++ ) {
+                        if ( i < numAdjusts ) {
+                            int[] wpa1 = gposAdjustments [ gposAdjustmentsIndex + i ];
+                            int[] wpa2 = gpa [ j++ ];
+                            for ( int k = 0; k < 4; k++ ) {
+                                int a = wpa2 [ k ];
+                                if ( a != 0 ) {
+                                    wpa1 [ k ] += a; adjusted = true;
+                                }
+                            }
+                        }
+                    }
+                } else {
+                    throw new IllegalStateException
+                        ( "gpos adjustments array too short: expect at least "
+                          + need + " entries, but has only " + gposAdjustments.length
+                          + " entries" );
+                }
+            }
+            gposAdjustmentsIndex += wordLength;
+            return adjusted;
         }
 
         /**
@@ -566,13 +735,44 @@ public class TextLayoutManager extends L
          * Add the spaces - except zero-width spaces - to the TextArea.
          */
         private void addSpaces() {
+            int blockProgressionOffset = 0;
+            // [TBD] need to better handling of spaceIPD assignment, for now,
+            // divide the area info's allocated IPD evenly among the
+            // non-zero-width space characters
+            int numZeroWidthSpaces = 0;
             for (int i = areaInfo.startIndex; i < areaInfo.breakIndex; i++) {
                 char spaceChar = foText.charAt(i);
+                if (CharUtilities.isZeroWidthSpace(spaceChar)) {
+                    numZeroWidthSpaces++;
+                }
+            }
+            int numSpaces = areaInfo.breakIndex - areaInfo.startIndex - numZeroWidthSpaces;
+            int spaceIPD = areaInfo.areaIPD.getOpt() / ( ( numSpaces > 0 ) ? numSpaces : 1 );
+            // add space area children, one for each non-zero-width space character
+            for (int i = areaInfo.startIndex; i < areaInfo.breakIndex; i++) {
+                char spaceChar = foText.charAt(i);
+                int level = foText.bidiLevelAt(i);
                 if (!CharUtilities.isZeroWidthSpace(spaceChar)) {
-                    textArea.addSpace(spaceChar, 0, CharUtilities.isAdjustableSpace(spaceChar));
+                    textArea.addSpace
+                        ( spaceChar, spaceIPD,
+                          CharUtilities.isAdjustableSpace(spaceChar),
+                          blockProgressionOffset, level );
                 }
             }
         }
+
+    }
+
+    private void addAreaInfo ( AreaInfo ai ) {
+        addAreaInfo ( areaInfos.size(), ai );
+    }
+
+    private void addAreaInfo ( int index, AreaInfo ai ) {
+        areaInfos.add ( index, ai );
+    }
+
+    private void removeAreaInfo ( int index ) {
+        areaInfos.remove ( index );
     }
 
     private AreaInfo getAreaInfo(int index) {
@@ -580,10 +780,10 @@ public class TextLayoutManager extends L
     }
 
     private void addToLetterAdjust(int index, int width) {
-        if (letterAdjustArray[index] == null) {
-            letterAdjustArray[index] = MinOptMax.getInstance(width);
+        if (letterSpaceAdjustArray[index] == null) {
+            letterSpaceAdjustArray[index] = MinOptMax.getInstance(width);
         } else {
-            letterAdjustArray[index] = letterAdjustArray[index].plus(width);
+            letterSpaceAdjustArray[index] = letterSpaceAdjustArray[index].plus(width);
         }
     }
 
@@ -600,6 +800,7 @@ public class TextLayoutManager extends L
 
     /** {@inheritDoc} */
     public List getNextKnuthElements(final LayoutContext context, final int alignment) {
+
         lineStartBAP = context.getLineStartBorderAndPaddingWidth();
         lineEndBAP = context.getLineEndBorderAndPaddingWidth();
         alignmentContext = context.getAlignmentContext();
@@ -610,13 +811,19 @@ public class TextLayoutManager extends L
         AreaInfo prevAreaInfo = null;
         returnList.add(sequence);
 
+        if (LOG.isDebugEnabled()) {
+            LOG.debug ( "GK: [" + nextStart + "," + foText.length() + "]" );
+        }
         LineBreakStatus lineBreakStatus = new LineBreakStatus();
         thisStart = nextStart;
         boolean inWord = false;
         boolean inWhitespace = false;
         char ch = 0;
+        int level = -1;
+        int prevLevel = -1;
         while (nextStart < foText.length()) {
             ch = foText.charAt(nextStart);
+            level = foText.bidiLevelAt(nextStart);
             boolean breakOpportunity = false;
             byte breakAction = keepTogether
                     ? LineBreakStatus.PROHIBITED_BREAK
@@ -635,17 +842,29 @@ public class TextLayoutManager extends L
                 default:
                     TextLayoutManager.LOG.error("Unexpected breakAction: " + breakAction);
             }
+            if (LOG.isDebugEnabled()) {
+                LOG.debug ( "GK: {"
+                            + " index = " + nextStart
+                            + ", char = " + CharUtilities.charToNCRef ( ch )
+                            + ", level = " + level
+                            + ", levelPrev = " + prevLevel
+                            + ", inWord = " + inWord
+                            + ", inSpace = " + inWhitespace
+                            + "}" );
+            }
             if (inWord) {
-                if (breakOpportunity
-                        || TextLayoutManager.isSpace(ch)
-                        || CharUtilities.isExplicitBreak(ch)) {
+                if ( breakOpportunity
+                     || TextLayoutManager.isSpace(ch)
+                     || CharUtilities.isExplicitBreak(ch)
+                     || ( ( prevLevel != -1 ) && ( level != prevLevel ) ) ) {
                     // this.foText.charAt(lastIndex) == CharUtilities.SOFT_HYPHEN
                     prevAreaInfo = processWord(alignment, sequence, prevAreaInfo, ch,
-                            breakOpportunity, true);
+                            breakOpportunity, true, prevLevel);
                 }
             } else if (inWhitespace) {
                 if (ch != CharUtilities.SPACE || breakOpportunity) {
-                    prevAreaInfo = processWhitespace(alignment, sequence, breakOpportunity);
+                    prevAreaInfo = processWhitespace(alignment, sequence,
+                                                     breakOpportunity, prevLevel);
                 }
             } else {
                 if (areaInfo != null) {
@@ -665,14 +884,14 @@ public class TextLayoutManager extends L
                 // preserved space or non-breaking space:
                 // create the AreaInfo object
                 areaInfo = new AreaInfo(nextStart, nextStart + 1, 1, 0, wordSpaceIPD, false, true,
-                        breakOpportunity, spaceFont);
+                                        breakOpportunity, spaceFont, level, null);
                 thisStart = nextStart + 1;
             } else if (CharUtilities.isFixedWidthSpace(ch) || CharUtilities.isZeroWidthSpace(ch)) {
                 // create the AreaInfo object
                 Font font = FontSelector.selectFontForCharacterInText(ch, foText, this);
                 MinOptMax ipd = MinOptMax.getInstance(font.getCharWidth(ch));
                 areaInfo = new AreaInfo(nextStart, nextStart + 1, 0, 0, ipd, false, true,
-                        breakOpportunity, font);
+                                        breakOpportunity, font, level, null);
                 thisStart = nextStart + 1;
             } else if (CharUtilities.isExplicitBreak(ch)) {
                 //mandatory break-character: only advance index
@@ -682,14 +901,15 @@ public class TextLayoutManager extends L
             inWord = !TextLayoutManager.isSpace(ch) && !CharUtilities.isExplicitBreak(ch);
             inWhitespace = ch == CharUtilities.SPACE
                     && foText.getWhitespaceTreatment() != Constants.EN_PRESERVE;
+            prevLevel = level;
             nextStart++;
         }
 
         // Process any last elements
         if (inWord) {
-            processWord(alignment, sequence, prevAreaInfo, ch, false, false);
+            processWord(alignment, sequence, prevAreaInfo, ch, false, false, prevLevel);
         } else if (inWhitespace) {
-            processWhitespace(alignment, sequence, !keepTogether);
+            processWhitespace(alignment, sequence, !keepTogether, prevLevel);
         } else if (areaInfo != null) {
             processLeftoverAreaInfo(alignment, sequence, areaInfo,
                     ch == CharUtilities.ZERO_WIDTH_SPACE);
@@ -708,6 +928,8 @@ public class TextLayoutManager extends L
         } else {
             return returnList;
         }
+
+
     }
 
     private KnuthSequence processLinebreak(List returnList, KnuthSequence sequence) {
@@ -723,20 +945,27 @@ public class TextLayoutManager extends L
     private void processLeftoverAreaInfo(int alignment,
                                          KnuthSequence sequence, AreaInfo areaInfo,
                                          boolean breakOpportunityAfter) {
-        areaInfos.add(areaInfo);
+        addAreaInfo(areaInfo);
         areaInfo.breakOppAfter = breakOpportunityAfter;
         addElementsForASpace(sequence, alignment, areaInfo, areaInfos.size() - 1);
     }
 
     private AreaInfo processWhitespace(final int alignment,
-            final KnuthSequence sequence, final boolean breakOpportunity) {
+            final KnuthSequence sequence, final boolean breakOpportunity, int level) {
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug ( "PS: [" + thisStart + "," + nextStart + "]" );
+        }
+
         // End of whitespace
         // create the AreaInfo object
         assert nextStart >= thisStart;
-        AreaInfo areaInfo = new AreaInfo(thisStart, nextStart, nextStart - thisStart, 0,
-                wordSpaceIPD.mult(nextStart - thisStart), false, true, breakOpportunity, spaceFont);
+        AreaInfo areaInfo = new AreaInfo
+            ( thisStart, nextStart, nextStart - thisStart, 0,
+              wordSpaceIPD.mult(nextStart - thisStart),
+              false, true, breakOpportunity, spaceFont, level, null );
 
-        areaInfos.add(areaInfo);
+        addAreaInfo(areaInfo);
 
         // create the elements
         addElementsForASpace(sequence, alignment, areaInfo, areaInfos.size() - 1);
@@ -745,22 +974,136 @@ public class TextLayoutManager extends L
         return areaInfo;
     }
 
-    private AreaInfo processWord(final int alignment, final KnuthSequence sequence,
-            AreaInfo prevAreaInfo, final char ch, final boolean breakOpportunity,
-            final boolean checkEndsWithHyphen) {
+    private AreaInfo processWordMapping
+        ( int lastIndex, final Font font, AreaInfo prevAreaInfo, final char breakOpportunityChar,
+          final boolean endsWithHyphen, int level ) {
+        int s = this.thisStart; // start index of word in FOText character buffer
+        int e = lastIndex;      // end index of word in FOText character buffer
+        int nLS = 0;            // # of letter spaces
+        String script = foText.getScript();
+        String language = foText.getLanguage();
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug ( "PW: [" + thisStart + "," + lastIndex + "]: {"
+                        + " +M"
+                        + ", level = " + level
+                        + " }" );
+        }
+
+        // 1. extract unmapped character sequence
+        CharSequence ics = foText.subSequence ( s, e );
+
+        // 2. if script is not specified (by FO property) or it is specified as 'auto',
+        // then compute dominant script
+        if ( ( script == null ) || "auto".equals(script) ) {
+            script = CharScript.scriptTagFromCode ( CharScript.dominantScript ( ics ) );
+        }
+        if ( ( language == null ) || "none".equals(language) ) {
+            language = "dflt";
+        }
+
+        // 3. perform mapping of chars to glyphs ... to glyphs ... to chars
+        CharSequence mcs = font.performSubstitution ( ics, script, language );
+
+        // 4. compute glyph position adjustments on (substituted) characters
+        int[][] gpa;
+        if ( font.performsPositioning() ) {
+            // handle GPOS adjustments
+            gpa = font.performPositioning ( mcs, script, language );
+        } else if ( font.hasKerning() ) {
+            // handle standard (non-GPOS) kerning adjustments
+            gpa = getKerningAdjustments ( mcs, font );
+        } else {
+            gpa = null;
+        }
 
-        //Word boundary found, process widths and kerning
-        int lastIndex = nextStart;
-        while (lastIndex > 0 && foText.charAt(lastIndex - 1) == CharUtilities.SOFT_HYPHEN) {
-            lastIndex--;
+        // 5. reorder combining marks so that they precede (within the mapped char sequence) the
+        // base to which they are applied; N.B. position adjustments (gpa) are reordered in place
+        mcs = font.reorderCombiningMarks ( mcs, gpa, script, language );
+
+        // 6. if mapped sequence differs from input sequence, then memoize mapped sequence
+        if ( !CharUtilities.isSameSequence ( mcs, ics ) ) {
+            foText.addMapping ( s, e, mcs );
+        }
+
+        // 7. compute word ipd based on final position adjustments
+        MinOptMax ipd = MinOptMax.ZERO;
+        for ( int i = 0, n = mcs.length(); i < n; i++ ) {
+            int c = mcs.charAt ( i );
+            // TODO !BMP
+            int  w = font.getCharWidth ( c );
+            if ( w < 0 ) {
+                w = 0;
+            }
+            if ( gpa != null ) {
+                w += gpa [ i ] [ GlyphPositioningTable.Value.IDX_X_ADVANCE ];
+            }
+            ipd = ipd.plus ( w );
+        }
+
+        // [TBD] - handle letter spacing
+
+        return new AreaInfo
+            ( s, e, 0, nLS, ipd, endsWithHyphen, false,
+              breakOpportunityChar != 0, font, level, gpa );
+    }
+
+    /**
+     * Given a mapped character sequence MCS, obtain glyph position adjustments
+     * from the font's kerning data.
+     * @param mcs mapped character sequence
+     * @param font applicable font
+     * @return glyph position adjustments (or null if no kerning)
+     */
+    private int[][] getKerningAdjustments ( CharSequence mcs, final Font font ) {
+        int nc = mcs.length();
+        // extract kerning array
+        int[] ka = new int [ nc ]; // kerning array
+        for ( int i = 0, n = nc, cPrev = -1; i < n; i++ ) {
+            int c = mcs.charAt ( i );
+            // TODO !BMP
+            if ( cPrev >= 0 ) {
+                ka[i] = font.getKernValue ( cPrev, c );
+            }
+            cPrev = c;
+        }
+        // was there a non-zero kerning?
+        boolean hasKerning = false;
+        for ( int i = 0, n = nc; i < n; i++ ) {
+            if ( ka[i] != 0 ) {
+                hasKerning = true;
+                break;
+            }
         }
-        Font font = FontSelector
-            .selectFontForCharactersInText(foText, thisStart, lastIndex, foText, this);
+        // if non-zero kerning, then create and return glyph position adjustment array
+        if ( hasKerning ) {
+            int[][] gpa = new int [ nc ] [ 4 ];
+            for ( int i = 0, n = nc; i < n; i++ ) {
+                if ( i > 0 ) {
+                    gpa [ i - 1 ] [ GlyphPositioningTable.Value.IDX_X_ADVANCE ] = ka [ i ];
+                }
+            }
+            return gpa;
+        } else {
+            return null;
+        }
+    }
+
+    private AreaInfo processWordNoMapping(int lastIndex, final Font font, AreaInfo prevAreaInfo,
+            final char breakOpportunityChar, final boolean endsWithHyphen, int level) {
         boolean kerning = font.hasKerning();
         MinOptMax wordIPD = MinOptMax.ZERO;
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug ( "PW: [" + thisStart + "," + lastIndex + "]: {"
+                        + " -M"
+                        + ", level = " + level
+                        + " }" );
+        }
+
         for (int i = thisStart; i < lastIndex; i++) {
             char currentChar = foText.charAt(i);
-
+            
             //character width
             int charWidth = font.getCharWidth(currentChar);
             wordIPD = wordIPD.plus(charWidth);
@@ -782,14 +1125,12 @@ public class TextLayoutManager extends L
                 }
             }
         }
-        boolean endsWithHyphen = checkEndsWithHyphen
-                                 && foText.charAt(lastIndex) == CharUtilities.SOFT_HYPHEN;
         if (kerning
-                && breakOpportunity
-                && !TextLayoutManager.isSpace(ch)
+                && ( breakOpportunityChar != 0 )
+                && !TextLayoutManager.isSpace(breakOpportunityChar)
                 && lastIndex > 0
                 && endsWithHyphen) {
-            int kern = font.getKernValue(foText.charAt(lastIndex - 1), ch);
+            int kern = font.getKernValue(foText.charAt(lastIndex - 1), breakOpportunityChar);
             if (kern != 0) {
                 addToLetterAdjust(lastIndex, kern);
                 //TODO: add kern to wordIPD?
@@ -801,23 +1142,46 @@ public class TextLayoutManager extends L
         int letterSpaces = 0;
         if (wordLength != 0) {
             letterSpaces = wordLength - 1;
-            // if there is a break opportunity and the next one
+            // if there is a break opportunity and the next one (break character)
             // is not a space, it could be used as a line end;
             // add one more letter space, in case other text follows
-            if (breakOpportunity && !TextLayoutManager.isSpace(ch)) {
-                letterSpaces++;
+            if (( breakOpportunityChar != 0 ) && !TextLayoutManager.isSpace(breakOpportunityChar)) {
+                  letterSpaces++;
             }
         }
         assert letterSpaces >= 0;
         wordIPD = wordIPD.plus(letterSpaceIPD.mult(letterSpaces));
 
-        // create the AreaInfo object
-        AreaInfo areaInfo = new AreaInfo(thisStart, lastIndex, 0,
-                letterSpaces, wordIPD,
-                endsWithHyphen,
-                false, breakOpportunity, font);
+        // create and return the AreaInfo object
+        return new AreaInfo(thisStart, lastIndex, 0,
+                            letterSpaces, wordIPD,
+                            endsWithHyphen,
+                            false, breakOpportunityChar != 0, font, level, null);
+    }
+
+    private AreaInfo processWord(final int alignment, final KnuthSequence sequence,
+            AreaInfo prevAreaInfo, final char ch, final boolean breakOpportunity,
+            final boolean checkEndsWithHyphen, int level) {
+
+        //Word boundary found, process widths and kerning
+        int lastIndex = nextStart;
+        while (lastIndex > 0 && foText.charAt(lastIndex - 1) == CharUtilities.SOFT_HYPHEN) {
+            lastIndex--;
+        }
+        final boolean endsWithHyphen = checkEndsWithHyphen
+                && foText.charAt(lastIndex) == CharUtilities.SOFT_HYPHEN;
+        Font font = FontSelector.selectFontForCharactersInText
+            ( foText, thisStart, lastIndex, foText, this );
+        AreaInfo areaInfo;
+        if ( font.performsSubstitution() || font.performsPositioning() ) {
+            areaInfo = processWordMapping
+                ( lastIndex, font, prevAreaInfo, breakOpportunity ? ch : 0, endsWithHyphen, level );
+        } else {
+            areaInfo = processWordNoMapping
+                ( lastIndex, font, prevAreaInfo, breakOpportunity ? ch : 0, endsWithHyphen, level );
+        }
         prevAreaInfo = areaInfo;
-        areaInfos.add(areaInfo);
+        addAreaInfo(areaInfo);
         tempStart = nextStart;
 
         //add the elements
@@ -902,22 +1266,22 @@ public class TextLayoutManager extends L
                 newIPD = newIPD.plus(font.getCharWidth(ch));
                 //if (i > startIndex) {
                 if (i < stopIndex) {
-                    MinOptMax letterAdjust = letterAdjustArray[i + 1];
+                    MinOptMax letterSpaceAdjust = letterSpaceAdjustArray[i + 1];
                     if (i == stopIndex - 1 && hyphenFollows) {
                         //the letter adjust here needs to be handled further down during
                         //element generation because it depends on hyph/no-hyph condition
-                        letterAdjust = null;
+                        letterSpaceAdjust = null;
                     }
-                    if (letterAdjust != null) {
-                        newIPD = newIPD.plus(letterAdjust);
+                    if (letterSpaceAdjust != null) {
+                        newIPD = newIPD.plus(letterSpaceAdjust);
                     }
                 }
             }
 
             // add letter spaces
             boolean isWordEnd
-                = stopIndex == areaInfo.breakIndex
-                && areaInfo.letterSpaceCount < areaInfo.getCharLength();
+                = (stopIndex == areaInfo.breakIndex)
+                && (areaInfo.letterSpaceCount < areaInfo.getWordLength());
             int letterSpaceCount = isWordEnd ? stopIndex - startIndex - 1 : stopIndex - startIndex;
 
             assert letterSpaceCount >= 0;
@@ -925,8 +1289,11 @@ public class TextLayoutManager extends L
 
             if (!(nothingChanged && stopIndex == areaInfo.breakIndex && !hyphenFollows)) {
                 // the new AreaInfo object is not equal to the old one
-                changeList.add(new PendingChange(new AreaInfo(startIndex, stopIndex, 0,
-                        letterSpaceCount, newIPD, hyphenFollows, false, false, font),
+                changeList.add
+                    ( new PendingChange
+                      ( new AreaInfo(startIndex, stopIndex, 0,
+                                     letterSpaceCount, newIPD, hyphenFollows,
+                                     false, false, font, -1, null),
                         ((LeafPosition) pos).getLeafPos() + changeOffset));
                 nothingChanged = false;
             }
@@ -992,9 +1359,9 @@ public class TextLayoutManager extends L
                     areaInfosAdded++;
                     oldIndex = currChange.index;
                     changeIndex = currChange.index + areaInfosAdded - areaInfosRemoved;
-                    areaInfos.remove(changeIndex);
+                    removeAreaInfo(changeIndex);
                 }
-                areaInfos.add(changeIndex, currChange.areaInfo);
+                addAreaInfo(changeIndex, currChange.areaInfo);
             }
             changeList.clear();
         }
@@ -1035,7 +1402,7 @@ public class TextLayoutManager extends L
         int leafValue = ((LeafPosition) pos).getLeafPos() + changeOffset;
         if (leafValue != -1) {
             AreaInfo areaInfo = getAreaInfo(leafValue);
-            StringBuffer buffer = new StringBuffer(areaInfo.getCharLength());
+            StringBuffer buffer = new StringBuffer(areaInfo.getWordLength());
             for (int i = areaInfo.startIndex; i < areaInfo.breakIndex; i++) {
                 buffer.append(foText.charAt(i));
             }
@@ -1209,7 +1576,7 @@ public class TextLayoutManager extends L
             MinOptMax widthIfNoBreakOccurs = null;
             if (areaInfo.breakIndex < foText.length()) {
                 //Add in kerning in no-break condition
-                widthIfNoBreakOccurs = letterAdjustArray[areaInfo.breakIndex];
+                widthIfNoBreakOccurs = letterSpaceAdjustArray[areaInfo.breakIndex];
             }
             //if (areaInfo.breakIndex)
 
@@ -1325,4 +1692,14 @@ public class TextLayoutManager extends L
 
     }
 
+    /** {@inheritDoc} */
+    public String toString() {
+        return super.toString() + "{"
+            + "chars = \'"
+            + CharUtilities.toNCRefs ( foText.getCharSequence().toString() )
+            + "\'"
+            + ", len = " + foText.length()
+            + "}";
+    }
+
 }

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java?rev=1293736&r1=1293735&r2=1293736&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java Sun Feb 26 02:29:01 2012
@@ -120,8 +120,8 @@ public class ListBlockLayoutManager exte
     }
 
     /**
-     * The table area is a reference area that contains areas for
-     * columns, bodies, rows and the contents are in cells.
+     * A list block generates one or more normal block areas whose child areas are
+     * normal block areas returned by the children of fo:list-block. See XSL-FO 1.1 6.8.2.
      *
      * @param parentIter the position iterator
      * @param layoutContext the layout context for adding areas

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java?rev=1293736&r1=1293735&r2=1293736&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java Sun Feb 26 02:29:01 2012
@@ -45,7 +45,7 @@ public class ListItemContentLayoutManage
 
     private Block curBlockArea;
 
-    private int xoffset;
+    private int xOffset;
     private int itemIPD;
 
     /**
@@ -80,7 +80,7 @@ public class ListItemContentLayoutManage
      * @param off the x offset
      */
     public void setXOffset(int off) {
-        xoffset = off;
+        xOffset = off;
     }
 
     /**
@@ -175,7 +175,7 @@ public class ListItemContentLayoutManage
             curBlockArea = new Block();
             curBlockArea.setPositioning(Block.ABSOLUTE);
             // set position
-            curBlockArea.setXOffset(xoffset);
+            curBlockArea.setXOffset(xOffset);
             //TODO: Check - itemIPD never set?
             curBlockArea.setIPD(itemIPD);
             //curBlockArea.setHeight();

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java?rev=1293736&r1=1293735&r2=1293736&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java Sun Feb 26 02:29:01 2012
@@ -465,7 +465,6 @@ public class ListItemLayoutManager exten
 
     /**
      * Add the areas for the break points.
-     * This sets the offset of each cell as it is added.
      *
      * @param parentIter the position iterator
      * @param layoutContext the layout context for adding areas
@@ -507,12 +506,12 @@ public class ListItemLayoutManager exten
         int bodyFirstIndex = ((ListItemPosition) positionList.getFirst()).getBodyFirstIndex();
         int bodyLastIndex = ((ListItemPosition) positionList.getLast()).getBodyLastIndex();
 
-        //Determine previous break if any
+        //Determine previous break if any (in item label list)
         int previousBreak = ElementListUtils.determinePreviousBreak(labelList, labelFirstIndex);
         SpaceResolver.performConditionalsNotification(labelList,
                 labelFirstIndex, labelLastIndex, previousBreak);
 
-        //Determine previous break if any
+        //Determine previous break if any (in item body list)
         previousBreak = ElementListUtils.determinePreviousBreak(bodyList, bodyFirstIndex);
         SpaceResolver.performConditionalsNotification(bodyList,
                 bodyFirstIndex, bodyLastIndex, previousBreak);

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java?rev=1293736&r1=1293735&r2=1293736&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java Sun Feb 26 02:29:01 2012
@@ -33,6 +33,9 @@ import org.apache.fop.fo.expr.RelativeNu
 import org.apache.fop.fo.flow.table.Table;
 import org.apache.fop.fo.flow.table.TableColumn;
 import org.apache.fop.fo.properties.TableColLength;
+import org.apache.fop.traits.Direction;
+import org.apache.fop.traits.WritingModeTraits;
+import org.apache.fop.traits.WritingModeTraitsGetter;
 
 /**
  * Class holding a number of columns making up the column setup of a row.
@@ -43,6 +46,7 @@ public class ColumnSetup {
     private static Log log = LogFactory.getLog(ColumnSetup.class);
 
     private Table table;
+    private WritingModeTraitsGetter wmTraits;
     private List columns = new java.util.ArrayList();
     private List colWidths = new java.util.ArrayList();
 
@@ -53,7 +57,9 @@ public class ColumnSetup {
      * @param table the table to construct this column setup for
      */
     public ColumnSetup(Table table) {
+        assert table != null;
         this.table = table;
+        this.wmTraits = WritingModeTraits.getWritingModeTraitsGetter ( table );
         prepareColumns();
         initializeColumnWidths();
     }
@@ -232,11 +238,47 @@ public class ColumnSetup {
     }
 
     /**
+     * Determine the X offset of the indicated column, where this
+     * offset denotes the left edge of the column irrespective of writing
+     * mode. If writing mode's column progression direction is right-to-left,
+     * then the first column is the right-most column and the last column is
+     * the left-most column; otherwise, the first column is the left-most
+     * column.
      * @param col column index (1 is first column)
      * @param context the context for percentage based calculations
      * @return the X offset of the requested column
      */
     public int getXOffset(int col, PercentBaseContext context) {
+        // TODO handle vertical WMs [GA]
+        if ( (wmTraits != null) && (wmTraits.getColumnProgressionDirection() == Direction.RL) ) {
+            return getXOffsetRTL(col, context);
+        } else {
+            return getXOffsetLTR(col, context);
+        }
+    }
+
+    /*
+     * Determine x offset by summing widths of columns to left of specified
+     * column; i.e., those columns whose column numbers are greater than the
+     * specified column number.
+     */
+    private int getXOffsetRTL(int col, PercentBaseContext context) {
+        int xoffset = 0;
+        for (int i = col, nc = colWidths.size(); ++i < nc;) {
+            int effCol = i;
+            if (colWidths.get(effCol) != null) {
+                xoffset += ((Length) colWidths.get(effCol)).getValue(context);
+            }
+        }
+        return xoffset;
+    }
+
+    /*
+     * Determine x offset by summing widths of columns to left of specified
+     * column; i.e., those columns whose column numbers are less than the
+     * specified column number.
+     */
+    private int getXOffsetLTR(int col, PercentBaseContext context) {
         int xoffset = 0;
         for (int i = col; --i >= 0;) {
             int effCol;

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFTextUtil.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFTextUtil.java?rev=1293736&r1=1293735&r2=1293736&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFTextUtil.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFTextUtil.java Sun Feb 26 02:29:01 2012
@@ -81,8 +81,8 @@ public abstract class PDFTextUtil {
         sb.append(PDFNumber.doubleOut(lt[5], DEC));
     }
 
-    private void writeChar(char ch, StringBuffer sb) {
-        if (!useMultiByte) {
+    private static void writeChar(char ch, StringBuffer sb, boolean multibyte) {
+        if (!multibyte) {
             if (ch < 32 || ch > 127) {
                 sb.append("\\").append(Integer.toOctalString(ch));
             } else {
@@ -101,6 +101,10 @@ public abstract class PDFTextUtil {
         }
     }
 
+    private void writeChar(char ch, StringBuffer sb) {
+        writeChar ( ch, sb, useMultiByte );
+    }
+
     private void checkInTextObject() {
         if (!inTextObject) {
             throw new IllegalStateException("Not in text object");
@@ -245,24 +249,38 @@ public abstract class PDFTextUtil {
             bufTJ = new StringBuffer();
         }
         if (bufTJ.length() == 0) {
-            bufTJ.append("[").append(startText);
+            bufTJ.append("[");
+            bufTJ.append(startText);
         }
         writeChar(codepoint, bufTJ);
     }
 
     /**
      * Writes a glyph adjust value to the "TJ-Buffer".
+
+     * <p>Assumes the following:</p>
+     * <ol>
+     * <li>if buffer is currently empty, then this is the start of the array object
+     * that encodes the adjustment and character values, and, therfore, a LEFT
+     * SQUARE BRACKET '[' must be prepended; and
+     * </li>
+     * <li>otherwise (the buffer is
+     * not empty), then the last element written to the buffer was a mapped
+     * character, and, therefore, a terminating '&gt;' or ')' followed by a space
+     * must be appended to the buffer prior to appending the adjustment value.
+     * </li>
+     * </ol>
      * @param adjust the glyph adjust value in thousands of text unit space.
      */
     public void adjustGlyphTJ(double adjust) {
         if (bufTJ == null) {
             bufTJ = new StringBuffer();
         }
-        if (bufTJ.length() > 0) {
-            bufTJ.append(endText).append(" ");
-        }
         if (bufTJ.length() == 0) {
             bufTJ.append("[");
+        } else {
+            bufTJ.append(endText);
+            bufTJ.append(" ");
         }
         bufTJ.append(PDFNumber.doubleOut(adjust, DEC - 4));
         bufTJ.append(" ");
@@ -285,4 +303,31 @@ public abstract class PDFTextUtil {
         return bufTJ != null && bufTJ.length() > 0;
     }
 
+    /**
+     * Writes a "Td" command with specified x and y coordinates.
+     * @param x coordinate
+     * @param y coordinate
+     */
+    public void writeTd ( double x, double y ) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(PDFNumber.doubleOut(x, DEC));
+        sb.append(' ');
+        sb.append(PDFNumber.doubleOut(y, DEC));
+        sb.append ( " Td\n" );
+        write ( sb.toString() );
+    }
+
+    /**
+     * Writes a "Tj" command with specified character code.
+     * @param ch character code to write
+     */
+    public void writeTj ( char ch ) {
+        StringBuffer sb = new StringBuffer();
+        sb.append ( '<' );
+        writeChar ( ch, sb, true );
+        sb.append ( '>' );
+        sb.append ( " Tj\n" );
+        write ( sb.toString() );
+    }
+
 }



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