diff options
author | Peter Hancock <phancock@apache.org> | 2012-06-29 11:22:42 +0000 |
---|---|---|
committer | Peter Hancock <phancock@apache.org> | 2012-06-29 11:22:42 +0000 |
commit | 3e31e071ea47f37ef7f8749b324dfdfe76745d18 (patch) | |
tree | 7d3b646c521349634592b62556a8832ec082cc9c /src/java/org/apache/fop/render/intermediate | |
parent | ae8a5035235e1262e412e1eacd0eb3adfaef83ef (diff) | |
parent | 015538e0f11f031e3d7bd05db8c29e2a40365678 (diff) | |
download | xmlgraphics-fop-3e31e071ea47f37ef7f8749b324dfdfe76745d18.tar.gz xmlgraphics-fop-3e31e071ea47f37ef7f8749b324dfdfe76745d18.zip |
Merged trunk@1354651
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1355321 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop/render/intermediate')
21 files changed, 1244 insertions, 396 deletions
diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java b/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java index b156b6c3a..2b90e4a7b 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java @@ -19,6 +19,10 @@ package org.apache.fop.render.intermediate; +import java.util.Locale; + +import org.apache.fop.accessibility.DummyStructureTreeEventHandler; +import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.apps.FOUserAgent; /** @@ -53,6 +57,11 @@ public abstract class AbstractIFDocumentHandler implements IFDocumentHandler { } /** {@inheritDoc} */ + public StructureTreeEventHandler getStructureTreeEventHandler() { + return DummyStructureTreeEventHandler.INSTANCE; + } + + /** {@inheritDoc} */ public IFDocumentNavigationHandler getDocumentNavigationHandler() { return null; //By default, this is not supported } @@ -66,6 +75,10 @@ public abstract class AbstractIFDocumentHandler implements IFDocumentHandler { } /** {@inheritDoc} */ + public void setDocumentLocale(Locale locale) { + } + + /** {@inheritDoc} */ public void startDocumentHeader() throws IFException { //nop } @@ -104,5 +117,4 @@ public abstract class AbstractIFDocumentHandler implements IFDocumentHandler { public void endPageTrailer() throws IFException { //nop } - } diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java index ca907eb0d..9651cf446 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java @@ -46,6 +46,7 @@ import org.apache.xmlgraphics.image.loader.util.ImageUtil; import org.apache.fop.ResourceEventProducer; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.FopFactory; +import org.apache.fop.fo.Constants; import org.apache.fop.render.ImageHandler; import org.apache.fop.render.ImageHandlerRegistry; import org.apache.fop.render.ImageHandlerUtil; @@ -314,34 +315,60 @@ public abstract class AbstractIFPainter implements IFPainter { } /** {@inheritDoc} */ - public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after, - BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException { - if (before != null) { + public void drawBorderRect(Rectangle rect, BorderProps top, BorderProps bottom, + BorderProps left, BorderProps right, Color innerBackgroundColor) throws IFException { + if (top != null) { Rectangle b = new Rectangle( rect.x, rect.y, - rect.width, before.width); - fillRect(b, before.color); + rect.width, top.width); + fillRect(b, top.color); } - if (end != null) { + if (right != null) { Rectangle b = new Rectangle( - rect.x + rect.width - end.width, rect.y, - end.width, rect.height); - fillRect(b, end.color); + rect.x + rect.width - right.width, rect.y, + right.width, rect.height); + fillRect(b, right.color); } - if (after != null) { + if (bottom != null) { Rectangle b = new Rectangle( - rect.x, rect.y + rect.height - after.width, - rect.width, after.width); - fillRect(b, after.color); + rect.x, rect.y + rect.height - bottom.width, + rect.width, bottom.width); + fillRect(b, bottom.color); } - if (start != null) { + if (left != null) { Rectangle b = new Rectangle( rect.x, rect.y, - start.width, rect.height); - fillRect(b, start.color); + left.width, rect.height); + fillRect(b, left.color); } } + /** + * Indicates whether the given border segments (if present) have only solid borders, i.e. + * could be painted in a simplified fashion keeping the output file smaller. + * @param top the border segment on the top edge + * @param bottom the border segment on the bottom edge + * @param left the border segment on the left edge + * @param right the border segment on the right edge + * @return true if any border segment has a non-solid border style + */ + protected boolean hasOnlySolidBorders(BorderProps top, BorderProps bottom, + BorderProps left, BorderProps right) { + if (top != null && top.style != Constants.EN_SOLID) { + return false; + } + if (bottom != null && bottom.style != Constants.EN_SOLID) { + return false; + } + if (left != null && left.style != Constants.EN_SOLID) { + return false; + } + if (right != null && right.style != Constants.EN_SOLID) { + return false; + } + return true; + } + /** {@inheritDoc} */ public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) throws IFException { diff --git a/src/java/org/apache/fop/render/intermediate/BorderPainter.java b/src/java/org/apache/fop/render/intermediate/BorderPainter.java index 5cf50ecbb..0ff5c2036 100644 --- a/src/java/org/apache/fop/render/intermediate/BorderPainter.java +++ b/src/java/org/apache/fop/render/intermediate/BorderPainter.java @@ -36,36 +36,36 @@ public abstract class BorderPainter { public static final String ROUNDED_CORNERS = "fop.round-corners"; /** TODO Use a class to model border instead of an array - * convention index of before, end, after and start borders */ - protected static final int BEFORE = 0, END = 1, AFTER = 2, START = 3; + * convention index of top, bottom, right and left borders */ + protected static final int TOP = 0, RIGHT = 1, BOTTOM = 2, LEFT = 3; /** TODO Use a class to model border corners instead of an array - convention index of before_start, before_end, after_end and after_start border corners*/ - protected static final int BEFORE_START = 0, BEFORE_END = 1, AFTER_END = 2, AFTER_START = 3; + convention index of top-left, top-right, bottom-right and bottom-left border corners*/ + protected static final int TOP_LEFT = 0, TOP_RIGHT = 1, BOTTOM_RIGHT = 2, BOTTOM_LEFT = 3; /** * Draws borders. * @param borderRect the border rectangle - * @param bpsBefore the border specification on the before side - * @param bpsAfter the border specification on the after side - * @param bpsStart the border specification on the start side - * @param bpsEnd the border specification on the end side + * @param bpsTop the border specification on the top side + * @param bpsBottom the border specification on the bottom side + * @param bpsLeft the border specification on the left side + * @param bpsRight the border specification on the end side * @param innerBackgroundColor the inner background color * @throws IFException if an error occurs while drawing the borders */ public void drawBorders(Rectangle borderRect, // CSOK: MethodLength - BorderProps bpsBefore, BorderProps bpsAfter, - BorderProps bpsStart, BorderProps bpsEnd, Color innerBackgroundColor) + BorderProps bpsTop, BorderProps bpsBottom, + BorderProps bpsLeft, BorderProps bpsRight, Color innerBackgroundColor) throws IFException { try { if (isRoundedCornersSupported()) { - drawRoundedBorders(borderRect, bpsBefore, bpsAfter, - bpsStart, bpsEnd); + drawRoundedBorders(borderRect, bpsTop, bpsBottom, + bpsLeft, bpsRight); } else { - drawRectangularBorders(borderRect, bpsBefore, bpsAfter, - bpsStart, bpsEnd); + drawRectangularBorders(borderRect, bpsTop, bpsBottom, + bpsLeft, bpsRight); } } catch (IOException ioe) { @@ -80,61 +80,59 @@ public abstract class BorderPainter { /** * TODO merge with drawRoundedBorders()? * @param borderRect the border rectangle - * @param bpsBefore the border specification on the before side - * @param bpsAfter the border specification on the after side - * @param bpsStart the border specification on the start side - * @param bpsEnd the border specification on the end side + * @param bpsTop the border specification on the top side + * @param bpsBottom the border specification on the bottom side + * @param bpsLeft the border specification on the left side + * @param bpsRight the border specification on the end side * @throws IOException */ protected void drawRectangularBorders(Rectangle borderRect, - BorderProps bpsBefore, BorderProps bpsAfter, - BorderProps bpsStart, BorderProps bpsEnd) throws IOException { + BorderProps bpsTop, BorderProps bpsBottom, + BorderProps bpsLeft, BorderProps bpsRight) throws IOException { + + bpsTop = sanitizeBorderProps(bpsTop); + bpsBottom = sanitizeBorderProps(bpsBottom); + bpsLeft = sanitizeBorderProps(bpsLeft); + bpsRight = sanitizeBorderProps(bpsRight); - bpsBefore = sanitizeBorderProps(bpsBefore); - bpsAfter = sanitizeBorderProps(bpsAfter); - bpsStart = sanitizeBorderProps(bpsStart); - bpsEnd = sanitizeBorderProps(bpsEnd); int startx = borderRect.x; int starty = borderRect.y; int width = borderRect.width; int height = borderRect.height; boolean[] b = new boolean[] { - (bpsBefore != null), (bpsEnd != null), - (bpsAfter != null), (bpsStart != null)}; + (bpsTop != null), (bpsRight != null), + (bpsBottom != null), (bpsLeft != null)}; if (!b[0] && !b[1] && !b[2] && !b[3]) { return; } int[] bw = new int[] { - (b[BEFORE] ? bpsBefore.width : 0), - (b[END] ? bpsEnd.width : 0), - (b[AFTER] ? bpsAfter.width : 0), - (b[3] ? bpsStart.width : 0)}; + (b[0] ? bpsTop.width : 0), + (b[1] ? bpsRight.width : 0), + (b[2] ? bpsBottom.width : 0), + (b[3] ? bpsLeft.width : 0)}; int[] clipw = new int[] { - BorderProps.getClippedWidth(bpsBefore), - BorderProps.getClippedWidth(bpsEnd), - BorderProps.getClippedWidth(bpsAfter), - BorderProps.getClippedWidth(bpsStart)}; - starty += clipw[BEFORE]; - height -= clipw[BEFORE]; - height -= clipw[AFTER]; - startx += clipw[START]; - width -= clipw[START]; - width -= clipw[END]; + BorderProps.getClippedWidth(bpsTop), + BorderProps.getClippedWidth(bpsRight), + BorderProps.getClippedWidth(bpsBottom), + BorderProps.getClippedWidth(bpsLeft)}; + starty += clipw[0]; + height -= clipw[0]; + height -= clipw[2]; + startx += clipw[3]; + width -= clipw[3]; + width -= clipw[1]; boolean[] slant = new boolean[] { - (b[START] && b[BEFORE]), - (b[BEFORE] && b[END]), - (b[END] && b[AFTER]), - (b[AFTER] && b[START])}; - if (bpsBefore != null) { + (b[3] && b[0]), (b[0] && b[1]), (b[1] && b[2]), (b[2] && b[3])}; + if (bpsTop != null) { int sx1 = startx; - int sx2 = (slant[BEFORE_START] ? sx1 + bw[START] - clipw[START] : sx1); + int sx2 = (slant[TOP_LEFT] ? sx1 + bw[LEFT] - clipw[LEFT] : sx1); int ex1 = startx + width; - int ex2 = (slant[BEFORE_END] ? ex1 - bw[END] + clipw[END] : ex1); - int outery = starty - clipw[BEFORE]; - int clipy = outery + clipw[BEFORE]; - int innery = outery + bw[BEFORE]; + int ex2 = (slant[TOP_RIGHT] ? ex1 - bw[RIGHT] + clipw[RIGHT] : ex1); + int outery = starty - clipw[TOP]; + int clipy = outery + clipw[TOP]; + int innery = outery + bw[TOP]; saveGraphicsState(); moveTo(sx1, clipy); @@ -142,12 +140,12 @@ public abstract class BorderPainter { int sx1a = sx1; int ex1a = ex1; - if (bpsBefore.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) { - sx1a -= clipw[START]; + if (bpsTop.mode == BorderProps.COLLAPSE_OUTER) { + if (bpsLeft != null && bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { + sx1a -= clipw[3]; } - if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { - ex1a += clipw[END]; + if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) { + ex1a += clipw[1]; } lineTo(sx1a, outery); lineTo(ex1a, outery); @@ -158,28 +156,28 @@ public abstract class BorderPainter { closePath(); clip(); drawBorderLine(sx1a, outery, ex1a, innery, true, true, - bpsBefore.style, bpsBefore.color); + bpsTop.style, bpsTop.color); restoreGraphicsState(); } - if (bpsEnd != null) { + if (bpsRight != null) { int sy1 = starty; - int sy2 = (slant[BEFORE_END] ? sy1 + bw[BEFORE] - clipw[BEFORE] : sy1); + int sy2 = (slant[TOP_RIGHT] ? sy1 + bw[TOP] - clipw[TOP] : sy1); int ey1 = starty + height; - int ey2 = (slant[AFTER_END] ? ey1 - bw[AFTER] + clipw[AFTER] : ey1); - int outerx = startx + width + clipw[END]; - int clipx = outerx - clipw[END]; - int innerx = outerx - bw[END]; + int ey2 = (slant[BOTTOM_RIGHT] ? ey1 - bw[BOTTOM] + clipw[BOTTOM] : ey1); + int outerx = startx + width + clipw[RIGHT]; + int clipx = outerx - clipw[RIGHT]; + int innerx = outerx - bw[RIGHT]; saveGraphicsState(); moveTo(clipx, sy1); int sy1a = sy1; int ey1a = ey1; - if (bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) { - sy1a -= clipw[BEFORE]; + if (bpsRight.mode == BorderProps.COLLAPSE_OUTER) { + if (bpsTop != null && bpsTop.mode == BorderProps.COLLAPSE_OUTER) { + sy1a -= clipw[TOP]; } - if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) { - ey1a += clipw[AFTER]; + if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { + ey1a += clipw[BOTTOM]; } lineTo(outerx, sy1a); lineTo(outerx, ey1a); @@ -189,28 +187,29 @@ public abstract class BorderPainter { lineTo(innerx, sy2); closePath(); clip(); - drawBorderLine(innerx, sy1a, outerx, ey1a, false, false, bpsEnd.style, bpsEnd.color); + drawBorderLine(innerx, sy1a, outerx, ey1a, false, false, + bpsRight.style, bpsRight.color); restoreGraphicsState(); } - if (bpsAfter != null) { + if (bpsBottom != null) { int sx1 = startx; - int sx2 = (slant[AFTER_START] ? sx1 + bw[START] - clipw[START] : sx1); + int sx2 = (slant[BOTTOM_LEFT] ? sx1 + bw[LEFT] - clipw[LEFT] : sx1); int ex1 = startx + width; - int ex2 = (slant[AFTER_END] ? ex1 - bw[END] + clipw[END] : ex1); - int outery = starty + height + clipw[AFTER]; - int clipy = outery - clipw[AFTER]; - int innery = outery - bw[AFTER]; + int ex2 = (slant[BOTTOM_RIGHT] ? ex1 - bw[RIGHT] + clipw[RIGHT] : ex1); + int outery = starty + height + clipw[BOTTOM]; + int clipy = outery - clipw[BOTTOM]; + int innery = outery - bw[BOTTOM]; saveGraphicsState(); moveTo(ex1, clipy); int sx1a = sx1; int ex1a = ex1; - if (bpsAfter.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) { - sx1a -= clipw[START]; + if (bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { + if (bpsLeft != null && bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { + sx1a -= clipw[LEFT]; } - if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { - ex1a += clipw[END]; + if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) { + ex1a += clipw[RIGHT]; } lineTo(ex1a, outery); lineTo(sx1a, outery); @@ -220,17 +219,18 @@ public abstract class BorderPainter { lineTo(ex2, innery); closePath(); clip(); - drawBorderLine(sx1a, innery, ex1a, outery, true, false, bpsAfter.style, bpsAfter.color); + drawBorderLine(sx1a, innery, ex1a, outery, true, false, + bpsBottom.style, bpsBottom.color); restoreGraphicsState(); } - if (bpsStart != null) { + if (bpsLeft != null) { int sy1 = starty; - int sy2 = (slant[BEFORE_START] ? sy1 + bw[BEFORE] - clipw[BEFORE] : sy1); + int sy2 = (slant[TOP_LEFT] ? sy1 + bw[TOP] - clipw[TOP] : sy1); int ey1 = sy1 + height; - int ey2 = (slant[AFTER_START] ? ey1 - bw[AFTER] + clipw[AFTER] : ey1); - int outerx = startx - clipw[START]; - int clipx = outerx + clipw[START]; - int innerx = outerx + bw[START]; + int ey2 = (slant[BOTTOM_LEFT] ? ey1 - bw[BOTTOM] + clipw[BOTTOM] : ey1); + int outerx = startx - clipw[LEFT]; + int clipx = outerx + clipw[LEFT]; + int innerx = outerx + bw[LEFT]; saveGraphicsState(); @@ -238,12 +238,12 @@ public abstract class BorderPainter { int sy1a = sy1; int ey1a = ey1; - if (bpsStart.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) { - sy1a -= clipw[BEFORE]; + if (bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { + if (bpsTop != null && bpsTop.mode == BorderProps.COLLAPSE_OUTER) { + sy1a -= clipw[TOP]; } - if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) { - ey1a += clipw[AFTER]; + if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { + ey1a += clipw[BOTTOM]; } lineTo(outerx, ey1a); lineTo(outerx, sy1a); @@ -253,7 +253,7 @@ public abstract class BorderPainter { lineTo(innerx, ey2); closePath(); clip(); - drawBorderLine(outerx, sy1a, innerx, ey1a, false, true, bpsStart.style, bpsStart.color); + drawBorderLine(outerx, sy1a, innerx, ey1a, false, true, bpsLeft.style, bpsLeft.color); restoreGraphicsState(); } } @@ -278,14 +278,14 @@ public abstract class BorderPainter { boolean[] b = new boolean[] { (bpsBefore != null), (bpsEnd != null), (bpsAfter != null), (bpsStart != null)}; - if (!b[BEFORE] && !b[END] && !b[AFTER] && !b[START]) { + if (!b[TOP] && !b[RIGHT] && !b[BOTTOM] && !b[LEFT]) { return; } int[] bw = new int[] { - (b[BEFORE] ? bpsBefore.width : 0), - (b[END] ? bpsEnd.width : 0), - (b[AFTER] ? bpsAfter.width : 0), - (b[START] ? bpsStart.width : 0)}; + (b[TOP] ? bpsBefore.width : 0), + (b[RIGHT] ? bpsEnd.width : 0), + (b[BOTTOM] ? bpsAfter.width : 0), + (b[LEFT] ? bpsStart.width : 0)}; int[] clipw = new int[] { BorderProps.getClippedWidth(bpsBefore), @@ -293,26 +293,26 @@ public abstract class BorderPainter { BorderProps.getClippedWidth(bpsAfter), BorderProps.getClippedWidth(bpsStart)}; - final int startx = borderRect.x + clipw[START]; - final int starty = borderRect.y + clipw[BEFORE]; - final int width = borderRect.width - clipw[START] - clipw[END]; - final int height = borderRect.height - clipw[BEFORE] - clipw[AFTER]; + final int startx = borderRect.x + clipw[LEFT]; + final int starty = borderRect.y + clipw[TOP]; + final int width = borderRect.width - clipw[LEFT] - clipw[RIGHT]; + final int height = borderRect.height - clipw[TOP] - clipw[BOTTOM]; boolean[] slant = new boolean[] { - (b[START] && b[BEFORE]), (b[BEFORE] && b[END]), - (b[END] && b[AFTER]), (b[START] && b[AFTER])}; + (b[LEFT] && b[TOP]), (b[TOP] && b[RIGHT]), + (b[RIGHT] && b[BOTTOM]), (b[LEFT] && b[BOTTOM])}; //Determine scale factor if any adjacent elliptic corners overlap double esf = cornerScaleFactor(width, height, bpsBefore, bpsAfter, bpsStart, bpsEnd); if (bpsBefore != null) { //Let x increase in the START->END direction - final int sx2 = (slant[BEFORE_START] ? bw[START] - clipw[START] : 0); + final int sx2 = (slant[TOP_LEFT] ? bw[LEFT] - clipw[LEFT] : 0); final int ex1 = width; - final int ex2 = (slant[BEFORE_END] ? ex1 - bw[END] + clipw[END] : ex1); - final int outery = -clipw[BEFORE]; - final int innery = outery + bw[BEFORE]; - final int clipy = outery + clipw[BEFORE]; + final int ex2 = (slant[TOP_RIGHT] ? ex1 - bw[RIGHT] + clipw[RIGHT] : ex1); + final int outery = -clipw[TOP]; + final int innery = outery + bw[TOP]; + final int clipy = outery + clipw[TOP]; final int ellipseSBW = bpsStart == null ? 0 : (int)(esf * bpsStart.getRadiusStart()); final int ellipseSBH = (int)(esf * bpsBefore.getRadiusStart()); final int ellipseSBX = ellipseSBW; @@ -325,7 +325,7 @@ public abstract class BorderPainter { saveGraphicsState(); translateCoordinates(startx, starty); drawBorderSegment( sx2, ex1, ex2, outery, innery, - clipw[START], clipw[END], + clipw[LEFT], clipw[RIGHT], ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH, ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH, bpsBefore, bpsStart, bpsEnd @@ -335,12 +335,12 @@ public abstract class BorderPainter { if (bpsStart != null) { //Let x increase in the AFTER->BEFORE direction - final int sx2 = (slant[AFTER_START] ? bw[AFTER] - clipw[AFTER] : 0); + final int sx2 = (slant[BOTTOM_LEFT] ? bw[BOTTOM] - clipw[BOTTOM] : 0); final int ex1 = height; - final int ex2 = (slant[BEFORE_START] ? ex1 - bw[BEFORE] + clipw[BEFORE] : ex1); - final int outery = -clipw[START]; - final int innery = outery + bw[START]; - final int clipy = outery + clipw[START]; + final int ex2 = (slant[TOP_LEFT] ? ex1 - bw[TOP] + clipw[TOP] : ex1); + final int outery = -clipw[LEFT]; + final int innery = outery + bw[LEFT]; + final int clipy = outery + clipw[LEFT]; final int ellipseSBW = bpsAfter == null ? 0 : (int)(esf * bpsAfter.getRadiusStart()); final int ellipseSBH = (int)(esf * bpsStart.getRadiusEnd()); final int ellipseSBX = ellipseSBW; @@ -354,7 +354,7 @@ public abstract class BorderPainter { translateCoordinates(startx, starty + height); rotateCoordinates(Math.PI * 3d / 2d); drawBorderSegment( sx2, ex1, ex2, outery, innery, - clipw[AFTER], clipw[BEFORE], + clipw[BOTTOM], clipw[TOP], ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH, ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH, bpsStart, bpsAfter, bpsBefore @@ -366,12 +366,12 @@ public abstract class BorderPainter { if (bpsAfter != null) { //Let x increase in the START->END direction - final int sx2 = (slant[AFTER_START] ? bw[START] - clipw[START] : 0); + final int sx2 = (slant[BOTTOM_LEFT] ? bw[LEFT] - clipw[LEFT] : 0); final int ex1 = width; - final int ex2 = (slant[AFTER_END] ? ex1 - bw[END] + clipw[END] : ex1); - final int outery = -clipw[AFTER]; - final int innery = outery + bw[AFTER]; - final int clipy = outery + clipw[AFTER]; + final int ex2 = (slant[BOTTOM_RIGHT] ? ex1 - bw[RIGHT] + clipw[RIGHT] : ex1); + final int outery = -clipw[BOTTOM]; + final int innery = outery + bw[BOTTOM]; + final int clipy = outery + clipw[BOTTOM]; final int ellipseSBW = bpsStart == null ? 0 : (int)(esf * bpsStart.getRadiusEnd()); final int ellipseSBH = (int)(esf * bpsAfter.getRadiusStart()); final int ellipseSBX = ellipseSBW; @@ -385,7 +385,7 @@ public abstract class BorderPainter { translateCoordinates(startx, starty + height); scaleCoordinates(1, -1); drawBorderSegment( sx2, ex1, ex2, outery, innery, - clipw[START], clipw[END], + clipw[LEFT], clipw[RIGHT], ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH, ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH, bpsAfter, bpsStart, bpsEnd @@ -395,12 +395,12 @@ public abstract class BorderPainter { if (bpsEnd != null) { //Let x increase in the BEFORE-> AFTER direction - final int sx2 = (slant[BEFORE_END] ? bw[BEFORE] - clipw[BEFORE] : 0); + final int sx2 = (slant[TOP_RIGHT] ? bw[TOP] - clipw[TOP] : 0); final int ex1 = height; - final int ex2 = (slant[AFTER_END] ? ex1 - bw[AFTER] + clipw[AFTER] : ex1); - final int outery = -clipw[END]; - final int innery = outery + bw[END]; - final int clipy = outery + clipw[END]; + final int ex2 = (slant[BOTTOM_RIGHT] ? ex1 - bw[BOTTOM] + clipw[BOTTOM] : ex1); + final int outery = -clipw[RIGHT]; + final int innery = outery + bw[RIGHT]; + final int clipy = outery + clipw[RIGHT]; final int ellipseSBW = bpsBefore == null ? 0 : (int)(esf * bpsBefore.getRadiusEnd()); final int ellipseSBH = (int)(esf * bpsEnd.getRadiusStart()); final int ellipseSBX = ellipseSBW; @@ -414,7 +414,7 @@ public abstract class BorderPainter { translateCoordinates(startx + width, starty); rotateCoordinates(Math.PI / 2d); drawBorderSegment( sx2, ex1, ex2, outery, innery, - clipw[BEFORE], clipw[AFTER], + clipw[TOP], clipw[BOTTOM], ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH, ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH, bpsEnd, bpsBefore, bpsAfter diff --git a/src/java/org/apache/fop/render/intermediate/EventProducingFilter.java b/src/java/org/apache/fop/render/intermediate/EventProducingFilter.java new file mode 100644 index 000000000..36f518d9a --- /dev/null +++ b/src/java/org/apache/fop/render/intermediate/EventProducingFilter.java @@ -0,0 +1,54 @@ +/* + * 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.render.intermediate; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.render.RendererEventProducer; +import org.apache.fop.render.intermediate.util.IFDocumentHandlerProxy; + +/** + * A filter that uses the Event Notification System to broadcast IF events. + * + */ +public class EventProducingFilter extends IFDocumentHandlerProxy { + + private int pageNumberEnded; + + private FOUserAgent userAgent; + + /** + * Constructor + * @param ifDocumentHandler the IFDocumentHandler to filter + * @param userAgent the FOUerAgent + */ + public EventProducingFilter(IFDocumentHandler ifDocumentHandler, FOUserAgent userAgent) { + super(ifDocumentHandler); + this.userAgent = userAgent; + } + + @Override + public void endPage() throws IFException { + super.endPage(); + pageNumberEnded++; + RendererEventProducer.Provider.get(userAgent.getEventBroadcaster()) + .endPage(this, pageNumberEnded); + } + +} diff --git a/src/java/org/apache/fop/render/intermediate/IFConstants.java b/src/java/org/apache/fop/render/intermediate/IFConstants.java index 34fe2bd2c..c7bf13e31 100644 --- a/src/java/org/apache/fop/render/intermediate/IFConstants.java +++ b/src/java/org/apache/fop/render/intermediate/IFConstants.java @@ -39,6 +39,8 @@ public interface IFConstants extends XMLConstants { String EL_HEADER = "header"; /** element name trailer */ String EL_TRAILER = "trailer"; + /** element name locale */ + String EL_LOCALE = "locale"; /** element name page-sequence */ String EL_PAGE_SEQUENCE = "page-sequence"; /** element name page */ @@ -67,6 +69,8 @@ public interface IFConstants extends XMLConstants { String EL_FONT = "font"; /** element name text */ String EL_TEXT = "text"; + /** element name id */ + String EL_ID = "id"; /** Parent element of the logical structure tree. */ String EL_STRUCTURE_TREE = "structure-tree"; } diff --git a/src/java/org/apache/fop/render/intermediate/IFContext.java b/src/java/org/apache/fop/render/intermediate/IFContext.java index 804b353c1..681b996e4 100644 --- a/src/java/org/apache/fop/render/intermediate/IFContext.java +++ b/src/java/org/apache/fop/render/intermediate/IFContext.java @@ -25,6 +25,7 @@ import java.util.Map; import org.apache.xmlgraphics.util.QName; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOUserAgent; /** @@ -46,7 +47,9 @@ public class IFContext { private Locale language; - private String structurePointer; + private StructureTreeElement structureTreeElement; + + private String id = ""; /** * Main constructor. @@ -130,29 +133,50 @@ public class IFContext { } /** - * Sets the structure pointer for the following painted marks. This method is used when - * accessibility features are enabled. - * @param ptr the structure pointer + * Sets the structure tree element to which the subsequently painted marks + * will correspond. This method is used when accessibility features are + * enabled. + * + * @param structureTreeElement the structure tree element + */ + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; + } + + /** + * Resets the current structure tree element. + * @see #setStructureTreeElement(StructureTreeElement) + */ + public void resetStructureTreeElement() { + setStructureTreeElement(null); + } + + /** + * Returns the current structure tree element. + * @return the structure tree element (or null if no element is active) + * @see #setStructureTreeElement(StructureTreeElement) */ - public void setStructurePointer(String ptr) { - this.structurePointer = ptr; + public StructureTreeElement getStructureTreeElement() { + return this.structureTreeElement; } /** - * Resets the current structure pointer. - * @see #setStructurePointer(String) + * Sets the ID of the object enclosing the content that will follow. + * + * @param id the ID of the nearest ancestor object for which the id property was set */ - public void resetStructurePointer() { - setStructurePointer(null); + void setID(String id) { + assert id != null; + this.id = id; } /** - * Returns the current structure pointer. - * @return the structure pointer (or null if no pointer is active) - * @see #setStructurePointer(String) + * Returns the ID of the object enclosing the current content. + * + * @return the ID of the nearest ancestor object for which the id property was set */ - public String getStructurePointer() { - return this.structurePointer; + String getID() { + return id; } } diff --git a/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java b/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java index af1451fe3..6cb8f2795 100644 --- a/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java +++ b/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java @@ -20,9 +20,11 @@ package org.apache.fop.render.intermediate; import java.awt.Dimension; +import java.util.Locale; import javax.xml.transform.Result; +import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.fonts.FontInfo; /** @@ -32,6 +34,7 @@ import org.apache.fop.fonts.FontInfo; * <p> * <pre> * startDocument() + * [setDocumentLocale()] * startDocumentHeader() * [handleExtension()]* * endDocumentHeader() @@ -118,6 +121,11 @@ public interface IFDocumentHandler { IFDocumentHandlerConfigurator getConfigurator(); /** + * @return the structure tree builder + */ + StructureTreeEventHandler getStructureTreeEventHandler(); + + /** * Returns a document navigation handler if this feature is supported. * @return the document navigation handler or null if not supported */ @@ -152,6 +160,11 @@ public interface IFDocumentHandler { void endDocument() throws IFException; /** + * @param locale Locale of the document. + */ + void setDocumentLocale(Locale locale); + + /** * Indicates the start of the document header. This method is called right after the * {@link #startDocument()} method. Extensions sent to this painter between * {@link #startDocumentHeader()} and {@link #endDocumentHeader()} apply to the document as @@ -261,7 +274,4 @@ public interface IFDocumentHandler { * @throws IFException if an error occurs while handling this event */ void handleExtensionObject(Object extension) throws IFException; - - //TODO Prototype the following: - //ContentHandler handleExtension() throws Exception } diff --git a/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java b/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java index 6e431e513..8c3f998a9 100644 --- a/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java +++ b/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java @@ -22,7 +22,7 @@ package org.apache.fop.render.intermediate; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.geom.AffineTransform; -import java.util.List; +import java.util.ArrayList; import org.apache.xmlgraphics.java2d.GraphicContext; @@ -33,7 +33,7 @@ public class IFGraphicContext extends GraphicContext { private static final AffineTransform[] EMPTY_TRANSFORM_ARRAY = new AffineTransform[0]; - private List groupList = new java.util.ArrayList(); + private ArrayList groupList = new ArrayList(); /** * Default constructor. @@ -48,17 +48,20 @@ public class IFGraphicContext extends GraphicContext { */ protected IFGraphicContext(IFGraphicContext graphicContext) { super(graphicContext); - //We don't clone groupDepth! + // N.B. do not perform deep copy on groupList; doing so causes + // a junit regression... have not investigated cause... [GA] + // groupList = (ArrayList) graphicContext.groupList.clone(); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ public Object clone() { - return new IFGraphicContext(this); + return new IFGraphicContext ( this ); } /** @param group a group */ public void pushGroup(Group group) { - //this.groupDepth++; this.groupList.add(group); for (int i = 0, c = group.getTransforms().length; i < c; i++) { transform(group.getTransforms()[i]); diff --git a/src/java/org/apache/fop/render/intermediate/IFPainter.java b/src/java/org/apache/fop/render/intermediate/IFPainter.java index 8d92d1ca9..5d2beb65c 100644 --- a/src/java/org/apache/fop/render/intermediate/IFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/IFPainter.java @@ -151,12 +151,14 @@ public interface IFPainter { * @param y Y-coordinate of the starting point of the text * @param letterSpacing additional spacing between characters (may be 0) * @param wordSpacing additional spacing between words (may be 0) - * @param dx an array of adjustment values for each character in X-direction (may be null) + * @param dp an array of 4-tuples, expressing [X,Y] placment + * adjustments and [X,Y] advancement adjustments, in that order (may be null); if + * not null, then adjustments.length must be the same as text.length() * @param text the text * @throws IFException if an error occurs while handling this event */ void drawText(int x, int y, int letterSpacing, int wordSpacing, - int[] dx, String text) throws IFException; + int[][] dp, String text) throws IFException; /** * Restricts the current clipping region with the given rectangle. @@ -206,16 +208,16 @@ public interface IFPainter { * Draws a border rectangle. The border segments are specified through {@link BorderProps} * instances. * @param rect the rectangle's coordinates and extent - * @param before the border segment on the before-side (top) - * @param after the border segment on the after-side (bottom) - * @param start the border segment on the start-side (left) - * @param end the border segment on the end-side (right) + * @param top the border segment on the top edge + * @param bottom the border segment on the bottom edge + * @param left the border segment on the left edge + * @param right the border segment on the right edge * @param innerBackgroundColor the color of the inner background * @throws IFException if an error occurs while handling this event */ void drawBorderRect(Rectangle rect, - BorderProps before, BorderProps after, - BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException; + BorderProps top, BorderProps bottom, + BorderProps left, BorderProps right, Color innerBackgroundColor) throws IFException; /** * Draws a line. NOTE: Currently, only horizontal lines are implemented! diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java index 0f143018d..293487ac2 100644 --- a/src/java/org/apache/fop/render/intermediate/IFParser.java +++ b/src/java/org/apache/fop/render/intermediate/IFParser.java @@ -24,6 +24,8 @@ import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.AffineTransform; +import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -47,11 +49,13 @@ import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.util.QName; import org.apache.fop.accessibility.AccessibilityEventProducer; -import org.apache.fop.accessibility.StructureTreeBuilder; +import org.apache.fop.accessibility.StructureTreeElement; +import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fo.ElementMapping; import org.apache.fop.fo.ElementMappingRegistry; import org.apache.fop.fo.expr.PropertyException; +import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.render.intermediate.extensions.DocumentNavigationExtensionConstants; import org.apache.fop.render.intermediate.extensions.DocumentNavigationHandler; import org.apache.fop.traits.BorderProps; @@ -61,7 +65,7 @@ import org.apache.fop.util.ContentHandlerFactory; import org.apache.fop.util.ContentHandlerFactoryRegistry; import org.apache.fop.util.DOMBuilderContentHandlerFactory; import org.apache.fop.util.DefaultErrorListener; -import org.apache.fop.util.DelegatingContentHandler; +import org.apache.fop.util.LanguageTags; import org.apache.fop.util.XMLUtil; /** @@ -71,12 +75,12 @@ import org.apache.fop.util.XMLUtil; public class IFParser implements IFConstants { /** Logger instance */ - protected static Log log = LogFactory.getLog(IFParser.class); + protected static final Log log = LogFactory.getLog(IFParser.class); private static SAXTransformerFactory tFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); - private static Set handledNamespaces = new java.util.HashSet(); + private static Set<String> handledNamespaces = new java.util.HashSet<String>(); static { handledNamespaces.add(XMLNS_NAMESPACE_URI); @@ -132,7 +136,7 @@ public class IFParser implements IFConstants { private static class Handler extends DefaultHandler { - private Map elementHandlers = new java.util.HashMap(); + private Map<String, ElementHandler> elementHandlers = new HashMap<String, ElementHandler>(); private IFDocumentHandler documentHandler; private IFPainter painter; @@ -152,24 +156,65 @@ public class IFParser implements IFConstants { private ContentHandler navParser; - private StructureTreeBuilder structureTreeBuilder; - - private ContentHandler structureTreeBuilderWrapper; + private StructureTreeHandler structureTreeHandler; private Attributes pageSequenceAttributes; - private final class StructureTreeBuilderWrapper extends DelegatingContentHandler { + private Map<String, StructureTreeElement> structureTreeElements + = new HashMap<String, StructureTreeElement>(); - private StructureTreeBuilderWrapper() - throws SAXException { - super(structureTreeBuilder.getHandlerForNextPageSequence()); + private final class StructureTreeHandler extends DefaultHandler { + + private final Locale pageSequenceLanguage; + + private final StructureTreeEventHandler structureTreeEventHandler; + + private StructureTreeHandler(StructureTreeEventHandler structureTreeEventHandler, + Locale pageSequenceLanguage) throws SAXException { + this.pageSequenceLanguage = pageSequenceLanguage; + this.structureTreeEventHandler = structureTreeEventHandler; + } + + void startStructureTree(String type) { + structureTreeEventHandler.startPageSequence(pageSequenceLanguage, type); } public void endDocument() throws SAXException { - super.endDocument(); startIFElement(EL_PAGE_SEQUENCE, pageSequenceAttributes); pageSequenceAttributes = null; } + + @Override + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { + if (!"structure-tree".equals(localName)) { + if (localName.equals("marked-content")) { + localName = "#PCDATA"; + } + String structID = attributes.getValue(InternalElementMapping.URI, + InternalElementMapping.STRUCT_ID); + if (structID == null) { + structureTreeEventHandler.startNode(localName, attributes); + } else if (localName.equals("external-graphic") + || localName.equals("instream-foreign-object")) { + StructureTreeElement structureTreeElement + = structureTreeEventHandler.startImageNode(localName, attributes); + structureTreeElements.put(structID, structureTreeElement); + } else { + StructureTreeElement structureTreeElement = structureTreeEventHandler + .startReferencedNode(localName, attributes); + structureTreeElements.put(structID, structureTreeElement); + } + } + } + + @Override + public void endElement(String uri, String localName, String arqNameg2) + throws SAXException { + if (!"structure-tree".equals(localName)) { + structureTreeEventHandler.endNode(localName); + } + } } public Handler(IFDocumentHandler documentHandler, FOUserAgent userAgent, @@ -179,6 +224,7 @@ public class IFParser implements IFConstants { this.elementMappingRegistry = elementMappingRegistry; elementHandlers.put(EL_DOCUMENT, new DocumentHandler()); elementHandlers.put(EL_HEADER, new DocumentHeaderHandler()); + elementHandlers.put(EL_LOCALE, new LocaleHandler()); elementHandlers.put(EL_TRAILER, new DocumentTrailerHandler()); elementHandlers.put(EL_PAGE_SEQUENCE, new PageSequenceHandler()); elementHandlers.put(EL_PAGE, new PageHandler()); @@ -188,6 +234,7 @@ public class IFParser implements IFConstants { //Page content elementHandlers.put(EL_VIEWPORT, new ViewportHandler()); elementHandlers.put(EL_GROUP, new GroupHandler()); + elementHandlers.put(EL_ID, new IDHandler()); elementHandlers.put(EL_FONT, new FontHandler()); elementHandlers.put(EL_TEXT, new TextHandler()); elementHandlers.put(EL_CLIP_RECT, new ClipRectHandler()); @@ -195,14 +242,9 @@ public class IFParser implements IFConstants { elementHandlers.put(EL_LINE, new LineHandler()); elementHandlers.put(EL_BORDER_RECT, new BorderRectHandler()); elementHandlers.put(EL_IMAGE, new ImageHandler()); - - if (userAgent.isAccessibilityEnabled()) { - structureTreeBuilder = new StructureTreeBuilder(tFactory); - userAgent.setStructureTree(structureTreeBuilder.getStructureTree()); - } } - private void establishForeignAttributes(Map foreignAttributes) { + private void establishForeignAttributes(Map<QName, String> foreignAttributes) { documentHandler.getContext().setForeignAttributes(foreignAttributes); } @@ -210,14 +252,6 @@ public class IFParser implements IFConstants { documentHandler.getContext().resetForeignAttributes(); } - private void establishStructurePointer(String ptr) { - documentHandler.getContext().setStructurePointer(ptr); - } - - private void resetStructurePointer() { - documentHandler.getContext().resetStructurePointer(); - } - /** {@inheritDoc} */ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { @@ -229,10 +263,15 @@ public class IFParser implements IFConstants { if (NAMESPACE.equals(uri)) { if (localName.equals(EL_PAGE_SEQUENCE) && userAgent.isAccessibilityEnabled()) { pageSequenceAttributes = new AttributesImpl(attributes); - structureTreeBuilderWrapper = new StructureTreeBuilderWrapper(); + Locale language = getLanguage(attributes); + structureTreeHandler = new StructureTreeHandler( + userAgent.getStructureTreeEventHandler(), language); + } else if (localName.equals(EL_STRUCTURE_TREE)) { if (userAgent.isAccessibilityEnabled()) { - delegate = structureTreeBuilderWrapper; + String type = attributes.getValue("type"); + structureTreeHandler.startStructureTree(type); + delegate = structureTreeHandler; } else { /* Delegate to a handler that does nothing */ delegate = new DefaultHandler(); @@ -258,7 +297,8 @@ public class IFParser implements IFConstants { } else if (DocumentNavigationExtensionConstants.NAMESPACE.equals(uri)) { if (this.navParser == null) { this.navParser = new DocumentNavigationHandler( - this.documentHandler.getDocumentNavigationHandler()); + this.documentHandler.getDocumentNavigationHandler(), + structureTreeElements); } delegate = this.navParser; delegateDepth++; @@ -297,10 +337,15 @@ public class IFParser implements IFConstants { } } + private static Locale getLanguage(Attributes attributes) { + String xmllang = attributes.getValue(XML_NAMESPACE, "lang"); + return (xmllang == null) ? null : LanguageTags.toLocale(xmllang); + } + private boolean startIFElement(String localName, Attributes attributes) throws SAXException { lastAttributes = new AttributesImpl(attributes); - ElementHandler elementHandler = (ElementHandler)elementHandlers.get(localName); + ElementHandler elementHandler = elementHandlers.get(localName); content.setLength(0); ignoreCharacters = true; if (elementHandler != null) { @@ -346,7 +391,7 @@ public class IFParser implements IFConstants { } } else { if (NAMESPACE.equals(uri)) { - ElementHandler elementHandler = (ElementHandler)elementHandlers.get(localName); + ElementHandler elementHandler = elementHandlers.get(localName); if (elementHandler != null) { try { elementHandler.endElement(); @@ -366,7 +411,7 @@ public class IFParser implements IFConstants { // ============== Element handlers for the intermediate format ============= - private static interface ElementHandler { + private interface ElementHandler { void startElement(Attributes attributes) throws IFException, SAXException; void endElement() throws IFException; boolean ignoreCharacters(); @@ -411,6 +456,12 @@ public class IFParser implements IFConstants { } + private class LocaleHandler extends AbstractElementHandler { + public void startElement(Attributes attributes) throws IFException { + documentHandler.setDocumentLocale(getLanguage(attributes)); + } + } + private class DocumentTrailerHandler extends AbstractElementHandler { public void startElement(Attributes attributes) throws IFException { @@ -427,12 +478,11 @@ public class IFParser implements IFConstants { public void startElement(Attributes attributes) throws IFException { String id = attributes.getValue("id"); - String xmllang = attributes.getValue(XML_NAMESPACE, "lang"); - if (xmllang != null) { - documentHandler.getContext().setLanguage( - XMLUtil.convertRFC3066ToLocale(xmllang)); + Locale language = getLanguage(attributes); + if (language != null) { + documentHandler.getContext().setLanguage(language); } - Map foreignAttributes = getForeignAttributes(lastAttributes); + Map<QName, String> foreignAttributes = getForeignAttributes(lastAttributes); establishForeignAttributes(foreignAttributes); documentHandler.startPageSequence(id); resetForeignAttributes(); @@ -453,7 +503,7 @@ public class IFParser implements IFConstants { String pageMasterName = attributes.getValue("page-master-name"); int width = Integer.parseInt(attributes.getValue("width")); int height = Integer.parseInt(attributes.getValue("height")); - Map foreignAttributes = getForeignAttributes(lastAttributes); + Map<QName, String> foreignAttributes = getForeignAttributes(lastAttributes); establishForeignAttributes(foreignAttributes); documentHandler.startPage(index, name, pageMasterName, new Dimension(width, height)); @@ -486,6 +536,7 @@ public class IFParser implements IFConstants { public void endElement() throws IFException { painter = null; + documentHandler.getContext().setID(""); documentHandler.endPageContent(); } @@ -536,6 +587,16 @@ public class IFParser implements IFConstants { } + private class IDHandler extends AbstractElementHandler { + + @Override + public void startElement(Attributes attributes) throws IFException, SAXException { + String id = attributes.getValue("name"); + documentHandler.getContext().setID(id); + } + + } + private class FontHandler extends AbstractElementHandler { public void startElement(Attributes attributes) throws IFException { @@ -565,9 +626,15 @@ public class IFParser implements IFConstants { s = lastAttributes.getValue("word-spacing"); int wordSpacing = (s != null ? Integer.parseInt(s) : 0); int[] dx = XMLUtil.getAttributeAsIntArray(lastAttributes, "dx"); - setStructurePointer(lastAttributes); - painter.drawText(x, y, letterSpacing, wordSpacing, dx, content.toString()); - resetStructurePointer(); + int[][] dp = XMLUtil.getAttributeAsPositionAdjustments(lastAttributes, "dp"); + // if only DX present, then convert DX to DP; otherwise use only DP, + // effectively ignoring DX + if ( ( dp == null ) && ( dx != null ) ) { + dp = IFUtil.convertDXToDP ( dx ); + } + establishStructureTreeElement(lastAttributes); + painter.drawText(x, y, letterSpacing, wordSpacing, dp, content.toString()); + resetStructureTreeElement(); } public boolean ignoreCharacters() { @@ -658,7 +725,7 @@ public class IFParser implements IFConstants { } - private static final String[] SIDES = new String[] {"before", "after", "start", "end"}; + private static final String[] SIDES = new String[] {"top", "bottom", "left", "right"}; private class BorderRectHandler extends AbstractElementHandler { @@ -699,9 +766,9 @@ public class IFParser implements IFConstants { int y = Integer.parseInt(lastAttributes.getValue("y")); int width = Integer.parseInt(lastAttributes.getValue("width")); int height = Integer.parseInt(lastAttributes.getValue("height")); - Map foreignAttributes = getForeignAttributes(lastAttributes); + Map<QName, String> foreignAttributes = getForeignAttributes(lastAttributes); establishForeignAttributes(foreignAttributes); - setStructurePointer(lastAttributes); + establishStructureTreeElement(lastAttributes); if (foreignObject != null) { painter.drawImage(foreignObject, new Rectangle(x, y, width, height)); @@ -715,7 +782,7 @@ public class IFParser implements IFConstants { painter.drawImage(uri, new Rectangle(x, y, width, height)); } resetForeignAttributes(); - resetStructurePointer(); + resetStructureTreeElement(); inForeignObject = false; } @@ -751,8 +818,8 @@ public class IFParser implements IFConstants { } } - private static Map getForeignAttributes(Attributes atts) { - Map foreignAttributes = null; + private static Map<QName, String> getForeignAttributes(Attributes atts) { + Map<QName, String> foreignAttributes = null; for (int i = 0, c = atts.getLength(); i < c; i++) { String ns = atts.getURI(i); if (ns.length() > 0) { @@ -760,7 +827,7 @@ public class IFParser implements IFConstants { continue; } if (foreignAttributes == null) { - foreignAttributes = new java.util.HashMap(); + foreignAttributes = new java.util.HashMap<QName, String>(); } QName qname = new QName(ns, atts.getQName(i)); foreignAttributes.put(qname, atts.getValue(i)); @@ -769,13 +836,20 @@ public class IFParser implements IFConstants { return foreignAttributes; } - private void setStructurePointer(Attributes attributes) { - String ptr = attributes.getValue("ptr"); - if (ptr != null && ptr.length() > 0) { - establishStructurePointer(ptr); + private void establishStructureTreeElement(Attributes attributes) { + String structRef = attributes.getValue(InternalElementMapping.URI, + InternalElementMapping.STRUCT_REF); + if (structRef != null && structRef.length() > 0) { + assert structureTreeElements.containsKey(structRef); + StructureTreeElement structureTreeElement = structureTreeElements.get(structRef); + documentHandler.getContext().setStructureTreeElement(structureTreeElement); } } + private void resetStructureTreeElement() { + documentHandler.getContext().resetStructureTreeElement(); + } + /** {@inheritDoc} */ public void characters(char[] ch, int start, int length) throws SAXException { if (delegate != null) { diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java index 1d20da519..45b0c3a93 100644 --- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java +++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java @@ -37,7 +37,6 @@ import java.util.Stack; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; - import org.xml.sax.SAXException; import org.apache.batik.parser.AWTTransformProducer; @@ -51,7 +50,9 @@ import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter; import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema; import org.apache.fop.Version; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.MimeConstants; import org.apache.fop.area.Area; import org.apache.fop.area.AreaTreeObject; @@ -71,10 +72,10 @@ import org.apache.fop.area.inline.ForeignObject; import org.apache.fop.area.inline.Image; import org.apache.fop.area.inline.InlineArea; import org.apache.fop.area.inline.InlineParent; +import org.apache.fop.area.inline.InlineViewport; import org.apache.fop.area.inline.Leader; import org.apache.fop.area.inline.SpaceArea; import org.apache.fop.area.inline.TextArea; -import org.apache.fop.area.inline.Viewport; import org.apache.fop.area.inline.WordArea; import org.apache.fop.datatypes.URISpecification; import org.apache.fop.fo.extensions.ExtensionAttachment; @@ -110,7 +111,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { //if optimizations can be done to avoid int->float->int conversions. /** logging instance */ - protected static Log log = LogFactory.getLog(IFRenderer.class); + protected static final Log log = LogFactory.getLog(IFRenderer.class); /** XML MIME type */ public static final String IF_MIME_TYPE = MimeConstants.MIME_FOP_IF; @@ -154,10 +155,15 @@ public class IFRenderer extends AbstractPathOrientedRenderer { private TextUtil textUtil = new TextUtil(); + private Stack<String> ids = new Stack<String>(); + /** * Main constructor + * + * @param userAgent the user agent that contains configuration details. This cannot be null. */ - public IFRenderer() { + public IFRenderer(FOUserAgent userAgent) { + super(userAgent); } /** {@inheritDoc} */ @@ -226,7 +232,11 @@ public class IFRenderer extends AbstractPathOrientedRenderer { */ protected IFDocumentHandler createDefaultDocumentHandler() { IFSerializer serializer = new IFSerializer(); - serializer.setContext(new IFContext(getUserAgent())); + FOUserAgent userAgent = getUserAgent(); + serializer.setContext(new IFContext(userAgent)); + if (userAgent.isAccessibilityEnabled()) { + userAgent.setStructureTreeEventHandler(serializer.getStructureTreeEventHandler()); + } return serializer; } @@ -293,6 +303,11 @@ public class IFRenderer extends AbstractPathOrientedRenderer { log.debug("Rendering finished."); } + @Override + public void setDocumentLocale(Locale locale) { + documentHandler.setDocumentLocale(locale); + } + /** {@inheritDoc} */ public void processOffDocumentItem(OffDocumentItem odi) { if (odi instanceof DestinationData) { @@ -473,7 +488,8 @@ public class IFRenderer extends AbstractPathOrientedRenderer { if (hasDocumentNavigation() && id != null) { int extraMarginBefore = 5000; // millipoints int ipp = currentIPPosition; - int bpp = currentBPPosition + inlineArea.getOffset() - extraMarginBefore; + int bpp = currentBPPosition + + inlineArea.getBlockProgressionOffset() - extraMarginBefore; saveAbsolutePosition(id, ipp, bpp); } } @@ -618,12 +634,12 @@ public class IFRenderer extends AbstractPathOrientedRenderer { documentHandler.getContext().resetForeignAttributes(); } - private void establishStructurePointer(String ptr) { - documentHandler.getContext().setStructurePointer(ptr); + private void establishStructureTreeElement(StructureTreeElement structureTreeElement) { + documentHandler.getContext().setStructureTreeElement(structureTreeElement); } private void resetStructurePointer() { - documentHandler.getContext().resetStructurePointer(); + documentHandler.getContext().resetStructureTreeElement(); } /** {@inheritDoc} */ @@ -788,18 +804,11 @@ public class IFRenderer extends AbstractPathOrientedRenderer { contentRectTransform.translate(borderPaddingStart, borderPaddingBefore); concatenateTransformationMatrixMpt(contentRectTransform, false); - //Clipping - Rectangle clipRect = null; - if (bv.getClip()) { - clipRect = new Rectangle(0, 0, dim.width, dim.height); - //clipRect(0f, 0f, width, height); - } - //saveGraphicsState(); //Set up coordinate system for content rectangle AffineTransform contentTransform = ctm.toAffineTransform(); //concatenateTransformationMatrixMpt(contentTransform); - startViewport(contentTransform, clipRect); + startViewport(contentTransform, bv.getClipRectangle()); currentIPPosition = 0; currentBPPosition = 0; @@ -831,13 +840,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { //Now adjust for border/padding currentBPPosition += borderPaddingBefore; - Rectangle2D clippingRect = null; - if (bv.getClip()) { - clippingRect = new Rectangle(currentIPPosition, currentBPPosition, - bv.getIPD(), bv.getBPD()); - } - - startVParea(ctm, clippingRect); + startVParea(ctm, bv.getClipRectangle()); currentIPPosition = 0; currentBPPosition = 0; renderBlocks(bv, children); @@ -852,30 +855,26 @@ public class IFRenderer extends AbstractPathOrientedRenderer { } /** {@inheritDoc} */ - public void renderViewport(Viewport viewport) { - String ptr = (String) viewport.getTrait(Trait.PTR); - establishStructurePointer(ptr); + public void renderInlineViewport(InlineViewport viewport) { + StructureTreeElement structElem + = (StructureTreeElement) viewport.getTrait(Trait.STRUCTURE_TREE_ELEMENT); + establishStructureTreeElement(structElem); + pushdID(viewport); Dimension dim = new Dimension(viewport.getIPD(), viewport.getBPD()); viewportDimensionStack.push(dim); - super.renderViewport(viewport); + super.renderInlineViewport(viewport); viewportDimensionStack.pop(); resetStructurePointer(); + popID(viewport); } /** {@inheritDoc} */ - protected void startVParea(CTM ctm, Rectangle2D clippingRect) { + protected void startVParea(CTM ctm, Rectangle clippingRect) { if (log.isTraceEnabled()) { log.trace("startVParea() ctm=" + ctm + ", clippingRect=" + clippingRect); } AffineTransform at = new AffineTransform(ctm.toArray()); - Rectangle clipRect = null; - if (clippingRect != null) { - clipRect = new Rectangle( - (int)clippingRect.getMinX() - currentIPPosition, - (int)clippingRect.getMinY() - currentBPPosition, - (int)clippingRect.getWidth(), (int)clippingRect.getHeight()); - } - startViewport(at, clipRect); + startViewport(at, clippingRect); if (log.isTraceEnabled()) { log.trace("startVPArea: " + at + " --> " + graphicContext.getTransform()); } @@ -909,7 +908,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer { /** {@inheritDoc} */ protected void renderInlineArea(InlineArea inlineArea) { saveInlinePosIfTargetable(inlineArea); + pushdID(inlineArea); super.renderInlineArea(inlineArea); + popID(inlineArea); } /** {@inheritDoc} */ @@ -917,10 +918,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer { // stuff we only need if a link must be created: Rectangle ipRect = null; AbstractAction action = null; - String ptr = (String) ip.getTrait(Trait.PTR); // used for accessibility // make sure the rect is determined *before* calling super! int ipp = currentIPPosition; - int bpp = currentBPPosition + ip.getOffset(); + int bpp = currentBPPosition + ip.getBlockProgressionOffset(); ipRect = new Rectangle(ipp, bpp, ip.getIPD(), ip.getBPD()); AffineTransform transform = graphicContext.getTransform(); ipRect = transform.createTransformedShape(ipRect).getBounds(); @@ -961,7 +961,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer { // warn if link trait found but not allowed, else create link if (linkTraitFound) { - action.setStructurePointer(ptr); // used for accessibility + StructureTreeElement structElem + = (StructureTreeElement) ip.getTrait(Trait.STRUCTURE_TREE_ELEMENT); + action.setStructureTreeElement(structElem); Link link = new Link(action, ipRect); this.deferredLinks.add(link); } @@ -973,7 +975,25 @@ public class IFRenderer extends AbstractPathOrientedRenderer { log.trace("renderBlock() " + block); } saveBlockPosIfTargetable(block); + pushdID(block); super.renderBlock(block); + popID(block); + } + + private void pushdID(Area area) { + String prodID = (String) area.getTrait(Trait.PROD_ID); + if (prodID != null) { + ids.push(prodID); + documentHandler.getContext().setID(prodID); + } + } + + private void popID(Area area) { + String prodID = (String) area.getTrait(Trait.PROD_ID); + if (prodID != null) { + ids.pop(); + documentHandler.getContext().setID(ids.empty() ? "" : ids.peek()); + } } private Typeface getTypeface(String fontName) { @@ -996,8 +1016,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer { String fontName = getInternalFontNameForArea(text); int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue(); - String ptr = (String)text.getTrait(Trait.PTR); // used for accessibility - establishStructurePointer(ptr); + StructureTreeElement structElem + = (StructureTreeElement) text.getTrait(Trait.STRUCTURE_TREE_ELEMENT); + establishStructureTreeElement(structElem); // This assumes that *all* CIDFonts use a /ToUnicode mapping Typeface tf = getTypeface(fontName); @@ -1011,7 +1032,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { } int rx = currentIPPosition + text.getBorderAndPaddingWidthStart(); - int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset(); + int bl = currentBPPosition + text.getBlockProgressionOffset() + text.getBaselineOffset(); textUtil.flush(); textUtil.setStartPosition(rx, bl); textUtil.setSpacing(text.getTextLetterSpaceAdjust(), text.getTextWordSpaceAdjust()); @@ -1027,7 +1048,12 @@ public class IFRenderer extends AbstractPathOrientedRenderer { Font font = getFontFromArea(word.getParentArea()); String s = word.getWord(); - renderText(s, word.getLetterAdjustArray(), + int[][] dp = word.getGlyphPositionAdjustments(); + if ( dp == null ) { + dp = IFUtil.convertDXToDP ( word.getLetterAdjustArray() ); + } + + renderText(s, dp, word.isReversed(), font, (AbstractTextArea)word.getParentArea()); super.renderWord(word); @@ -1039,7 +1065,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { String s = space.getSpace(); AbstractTextArea textArea = (AbstractTextArea)space.getParentArea(); - renderText(s, null, font, textArea); + renderText(s, null, false, font, textArea); if (textUtil.combined && space.isAdjustable()) { //Used for justified text, for example @@ -1052,24 +1078,32 @@ public class IFRenderer extends AbstractPathOrientedRenderer { super.renderSpace(space); } + private void renderText(String s, + int[][] dp, boolean reversed, + Font font, AbstractTextArea parentArea) { + if ( ( dp == null ) || IFUtil.isDPOnlyDX ( dp ) ) { + int[] dx = IFUtil.convertDPToDX ( dp ); + renderTextWithAdjustments ( s, dx, reversed, font, parentArea ); + } else { + renderTextWithAdjustments ( s, dp, reversed, font, parentArea ); + } + } + /** - * Does low-level rendering of text. + * Does low-level rendering of text using DX only position adjustments. * @param s text to render - * @param letterAdjust an array of widths for letter adjustment (may be null) + * @param dx an array of widths for letter adjustment (may be null) + * @param reversed if true then text has been reversed (from logical order) * @param font to font in use * @param parentArea the parent text area to retrieve certain traits from */ - protected void renderText(String s, - int[] letterAdjust, - Font font, AbstractTextArea parentArea) { + private void renderTextWithAdjustments(String s, + int[] dx, boolean reversed, + Font font, AbstractTextArea parentArea) { int l = s.length(); if (l == 0) { return; } - - if (letterAdjust != null) { - textUtil.adjust(letterAdjust[0]); - } for (int i = 0; i < l; i++) { char ch = s.charAt(i); textUtil.addChar(ch); @@ -1078,46 +1112,84 @@ public class IFRenderer extends AbstractPathOrientedRenderer { int tls = (i < l - 1 ? parentArea.getTextLetterSpaceAdjust() : 0); glyphAdjust += tls; } - if (letterAdjust != null && i < l - 1) { - glyphAdjust += letterAdjust[i + 1]; + if (dx != null && i < l) { + glyphAdjust += dx[i]; } - textUtil.adjust(glyphAdjust); } } + /** + * Does low-level rendering of text using generalized position adjustments. + * @param s text to render + * @param dp an array of 4-tuples, expressing [X,Y] placment + * adjustments and [X,Y] advancement adjustments, in that order (may be null) + * @param reversed if true then text has been reversed (from logical order) + * @param font to font in use + * @param parentArea the parent text area to retrieve certain traits from + */ + private void renderTextWithAdjustments(String s, + int[][] dp, boolean reversed, + Font font, AbstractTextArea parentArea) { + assert !textUtil.combined; + for ( int i = 0, n = s.length(); i < n; i++ ) { + textUtil.addChar ( s.charAt ( i ) ); + if ( dp != null ) { + textUtil.adjust ( dp[i] ); + } + } + } + private class TextUtil { private static final int INITIAL_BUFFER_SIZE = 16; - private int[] dx = new int[INITIAL_BUFFER_SIZE]; - private int lastDXPos = 0; + private int[][] dp = new int[INITIAL_BUFFER_SIZE][4]; + // private int lastDPPos = 0; // TBD - not yet used private final StringBuffer text = new StringBuffer(); - private int startx, starty; - private int tls, tws; + private int startx; + private int starty; + private int tls; + private int tws; private final boolean combined = false; void addChar(char ch) { text.append(ch); } - void adjust(int adjust) { - if (adjust != 0) { + void adjust(int dx) { + adjust ( new int[] { + dx, // xPlaAdjust + 0, // yPlaAdjust + dx, // xAdvAdjust + 0 // yAdvAdjust + } ); + } + + void adjust(int[] pa) { + if ( !IFUtil.isPAIdentity ( pa ) ) { int idx = text.length(); - if (idx > dx.length - 1) { - int newSize = Math.max(dx.length, idx + 1) + INITIAL_BUFFER_SIZE; - int[] newDX = new int[newSize]; - System.arraycopy(dx, 0, newDX, 0, dx.length); - dx = newDX; + if (idx > dp.length - 1) { + int newSize = Math.max(dp.length, idx + 1) + INITIAL_BUFFER_SIZE; + int[][] newDP = new int[newSize][]; + // reuse prior PA[0]...PA[dp.length-1] + System.arraycopy(dp, 0, newDP, 0, dp.length); + // populate new PA[dp.length]...PA[newDP.length-1] + for ( int i = dp.length, n = newDP.length; i < n; i++ ) { + newDP[i] = new int[4]; + } + dp = newDP; } - dx[idx] += adjust; - lastDXPos = idx; + IFUtil.adjustPA ( dp[idx - 1], pa ); + // lastDPPos = idx; } } void reset() { if (text.length() > 0) { text.setLength(0); - Arrays.fill(dx, 0); - lastDXPos = 0; + for ( int i = 0, n = dp.length; i < n; i++ ) { + Arrays.fill(dp[i], 0); + } + // lastDPPos = 0; } } @@ -1134,16 +1206,12 @@ public class IFRenderer extends AbstractPathOrientedRenderer { void flush() { if (text.length() > 0) { try { - int[] effDX = null; - if (lastDXPos > 0) { - int size = lastDXPos + 1; - effDX = new int[size]; - System.arraycopy(dx, 0, effDX, 0, size); - } if (combined) { - painter.drawText(startx, starty, 0, 0, effDX, text.toString()); + painter.drawText(startx, starty, 0, 0, + trimAdjustments ( dp, text.length() ), text.toString()); } else { - painter.drawText(startx, starty, tls, tws, effDX, text.toString()); + painter.drawText(startx, starty, tls, tws, + trimAdjustments ( dp, text.length() ), text.toString()); } } catch (IFException e) { handleIFException(e); @@ -1151,6 +1219,38 @@ public class IFRenderer extends AbstractPathOrientedRenderer { reset(); } } + + /** + * Trim adjustments array <code>dp</code> to be no greater length than + * text length, and where trailing all-zero entries are removed. + * @param dp a position adjustments array (or null) + * @param textLength the length of the associated text + * @return either the original value of <code>dp</code> or a copy + * of its first N significant adjustment entries, such that N is + * no greater than text length, and the last entry has a non-zero + * adjustment. + */ + private int[][] trimAdjustments ( int[][] dp, int textLength ) { + if ( dp != null ) { + int tl = textLength; + int pl = dp.length; + int i = ( tl < pl ) ? tl : pl; + while ( i > 0 ) { + int[] pa = dp [ i - 1 ]; + if ( !IFUtil.isPAIdentity ( pa ) ) { + break; + } else { + i--; + } + } + if ( i == 0 ) { + dp = null; + } else if ( i < pl ) { + dp = IFUtil.copyDP ( dp, 0, i ); + } + } + return dp; + } } /** {@inheritDoc} */ @@ -1200,7 +1300,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { int style = area.getRuleStyle(); int ruleThickness = area.getRuleThickness(); int startx = currentIPPosition + area.getBorderAndPaddingWidthStart(); - int starty = currentBPPosition + area.getOffset() + (ruleThickness / 2); + int starty = currentBPPosition + area.getBlockProgressionOffset() + (ruleThickness / 2); int endx = currentIPPosition + area.getBorderAndPaddingWidthStart() + area.getIPD(); @@ -1268,12 +1368,21 @@ public class IFRenderer extends AbstractPathOrientedRenderer { float startx, float starty, float width, float height, BorderProps bpsBefore, BorderProps bpsAfter, - BorderProps bpsStart, BorderProps bpsEnd, Color innerBackgroundColor) { - //TODO lose scale? + BorderProps bpsStart, BorderProps bpsEnd, int level, Color innerBackgroundColor) { Rectangle rect = toMillipointRectangle(startx, starty, width, height); try { - painter.drawBorderRect(rect, bpsBefore, bpsAfter, bpsStart, bpsEnd, - innerBackgroundColor); + BorderProps bpsTop = bpsBefore; + BorderProps bpsBottom = bpsAfter; + BorderProps bpsLeft; + BorderProps bpsRight; + if ( ( level == -1 ) || ( ( level & 1 ) == 0 ) ) { + bpsLeft = bpsStart; + bpsRight = bpsEnd; + } else { + bpsLeft = bpsEnd; + bpsRight = bpsStart; + } + painter.drawBorderRect(rect, bpsTop, bpsBottom, bpsLeft, bpsRight, innerBackgroundColor); } catch (IFException ife) { handleIFException(ife); } diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index 4af90faed..4c6781706 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -31,19 +31,18 @@ import java.util.Locale; import java.util.Map; import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; import org.apache.xmlgraphics.util.QName; import org.apache.xmlgraphics.util.XMLizable; -import org.apache.fop.accessibility.StructureTree; +import org.apache.fop.accessibility.StructureTreeEventHandler; +import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.fonts.FontInfo; import org.apache.fop.render.PrintRendererConfigurator; import org.apache.fop.render.RenderingContext; +import org.apache.fop.render.intermediate.IFStructureTreeBuilder.IFStructureTreeElement; import org.apache.fop.render.intermediate.extensions.AbstractAction; import org.apache.fop.render.intermediate.extensions.Bookmark; import org.apache.fop.render.intermediate.extensions.BookmarkTree; @@ -54,28 +53,38 @@ import org.apache.fop.traits.BorderProps; import org.apache.fop.traits.RuleStyle; import org.apache.fop.util.ColorUtil; import org.apache.fop.util.DOM2SAX; +import org.apache.fop.util.LanguageTags; import org.apache.fop.util.XMLConstants; import org.apache.fop.util.XMLUtil; + /** * IFPainter implementation that serializes the intermediate format to XML. */ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler implements IFConstants, IFPainter, IFDocumentNavigationHandler { + /** + * Intermediate Format (IF) version, used to express an @version attribute + * in the root element of the IF document, the initial value of which + * is set to '2.0' to signify that something preceded it (but didn't + * happen to be marked as such), and that this version is not necessarily + * backwards compatible with the unmarked (<2.0) version. + */ + public static final String VERSION = "2.0"; + private IFDocumentHandler mimicHandler; private int pageSequenceIndex; // used for accessibility /** Holds the intermediate format state */ private IFState state; - /** - * Default constructor. - */ - public IFSerializer() { - } + private String currentID = ""; + + private IFStructureTreeBuilder structureTreeBuilder; /** {@inheritDoc} */ + @Override protected String getMainNamespace() { return NAMESPACE; } @@ -102,6 +111,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public IFDocumentNavigationHandler getDocumentNavigationHandler() { return this; } @@ -146,7 +156,16 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } } + @Override + public StructureTreeEventHandler getStructureTreeEventHandler() { + if (structureTreeBuilder == null) { + structureTreeBuilder = new IFStructureTreeBuilder(); + } + return structureTreeBuilder; + } + /** {@inheritDoc} */ + @Override public void startDocument() throws IFException { super.startDocument(); try { @@ -155,13 +174,31 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler handler.startPrefixMapping(XLINK_PREFIX, XLINK_NAMESPACE); handler.startPrefixMapping(DocumentNavigationExtensionConstants.PREFIX, DocumentNavigationExtensionConstants.NAMESPACE); - handler.startElement(EL_DOCUMENT); + handler.startPrefixMapping(InternalElementMapping.STANDARD_PREFIX, + InternalElementMapping.URI); + AttributesImpl atts = new AttributesImpl(); + addAttribute(atts, "version", VERSION); + handler.startElement(EL_DOCUMENT, atts); } catch (SAXException e) { throw new IFException("SAX error in startDocument()", e); } } + @Override + public void setDocumentLocale(Locale locale) { + AttributesImpl atts = new AttributesImpl(); + atts.addAttribute(XML_NAMESPACE, "lang", "xml:lang", XMLUtil.CDATA, + LanguageTags.toLanguageTag(locale)); + try { + handler.startElement(EL_LOCALE, atts); + handler.endElement(EL_LOCALE); + } catch (SAXException e) { + throw new RuntimeException("Unable to create the " + EL_LOCALE + " element.", e); + } + } + /** {@inheritDoc} */ + @Override public void startDocumentHeader() throws IFException { try { handler.startElement(EL_HEADER); @@ -171,6 +208,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void endDocumentHeader() throws IFException { try { handler.endElement(EL_HEADER); @@ -180,6 +218,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void startDocumentTrailer() throws IFException { try { handler.startElement(EL_TRAILER); @@ -189,6 +228,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void endDocumentTrailer() throws IFException { try { handler.endElement(EL_TRAILER); @@ -218,20 +258,14 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler Locale lang = getContext().getLanguage(); if (lang != null) { atts.addAttribute(XML_NAMESPACE, "lang", "xml:lang", XMLUtil.CDATA, - XMLUtil.toRFC3066(lang)); + LanguageTags.toLanguageTag(lang)); } XMLUtil.addAttribute(atts, XMLConstants.XML_SPACE, "preserve"); addForeignAttributes(atts); handler.startElement(EL_PAGE_SEQUENCE, atts); if (this.getUserAgent().isAccessibilityEnabled()) { - StructureTree structureTree = getUserAgent().getStructureTree(); - handler.startElement(EL_STRUCTURE_TREE); // add structure tree - NodeList nodes = structureTree.getPageSequence(pageSequenceIndex++); - for (int i = 0, n = nodes.getLength(); i < n; i++) { - Node node = nodes.item(i); - new DOM2SAX(handler).writeFragment(node); - } - handler.endElement(EL_STRUCTURE_TREE); + assert (structureTreeBuilder != null); + structureTreeBuilder.replayEventsForPageSequence(handler, pageSequenceIndex++); } } catch (SAXException e) { throw new IFException("SAX error in startPageSequence()", e); @@ -241,6 +275,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler /** {@inheritDoc} */ public void endPageSequence() throws IFException { try { + handler.endElement(EL_PAGE_SEQUENCE); } catch (SAXException e) { throw new IFException("SAX error in endPageSequence()", e); @@ -265,6 +300,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void startPageHeader() throws IFException { try { handler.startElement(EL_PAGE_HEADER); @@ -274,6 +310,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void endPageHeader() throws IFException { try { handler.endElement(EL_PAGE_HEADER); @@ -297,6 +334,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler public void endPageContent() throws IFException { try { this.state = null; + currentID = ""; handler.endElement(EL_PAGE_CONTENT); } catch (SAXException e) { throw new IFException("SAX error in endPageContent()", e); @@ -304,6 +342,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void startPageTrailer() throws IFException { try { handler.startElement(EL_PAGE_TRAILER); @@ -313,6 +352,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void endPageTrailer() throws IFException { try { commitNavigation(); @@ -406,6 +446,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler /** {@inheritDoc} */ public void drawImage(String uri, Rectangle rect) throws IFException { try { + addID(); AttributesImpl atts = new AttributesImpl(); addAttribute(atts, XLINK_HREF, uri); addAttribute(atts, "x", Integer.toString(rect.x)); @@ -413,7 +454,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler addAttribute(atts, "width", Integer.toString(rect.width)); addAttribute(atts, "height", Integer.toString(rect.height)); addForeignAttributes(atts); - addStructurePointerAttribute(atts); + addStructureReference(atts); handler.element(EL_IMAGE, atts); } catch (SAXException e) { throw new IFException("SAX error in startGroup()", e); @@ -434,13 +475,14 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler /** {@inheritDoc} */ public void drawImage(Document doc, Rectangle rect) throws IFException { try { + addID(); AttributesImpl atts = new AttributesImpl(); addAttribute(atts, "x", Integer.toString(rect.x)); addAttribute(atts, "y", Integer.toString(rect.y)); addAttribute(atts, "width", Integer.toString(rect.width)); addAttribute(atts, "height", Integer.toString(rect.height)); addForeignAttributes(atts); - addStructurePointerAttribute(atts); + addStructureReference(atts); handler.startElement(EL_IMAGE, atts); new DOM2SAX(handler).writeDocument(doc, true); handler.endElement(EL_IMAGE); @@ -550,9 +592,9 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ - public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after, - BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException { - if (before == null && after == null && start == null && end == null) { + public void drawBorderRect(Rectangle rect, BorderProps top, BorderProps bottom, + BorderProps left, BorderProps right, Color innerBackgroundColor) throws IFException { + if (top == null && bottom == null && left == null && right == null) { return; } try { @@ -561,17 +603,17 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler addAttribute(atts, "y", Integer.toString(rect.y)); addAttribute(atts, "width", Integer.toString(rect.width)); addAttribute(atts, "height", Integer.toString(rect.height)); - if (before != null) { - addAttribute(atts, "before", before.toString()); + if (top != null) { + addAttribute(atts, "top", top.toString()); } - if (after != null) { - addAttribute(atts, "after", after.toString()); + if (bottom != null) { + addAttribute(atts, "bottom", bottom.toString()); } - if (start != null) { - addAttribute(atts, "start", start.toString()); + if (left != null) { + addAttribute(atts, "left", left.toString()); } - if (end != null) { - addAttribute(atts, "end", end.toString()); + if (right != null) { + addAttribute(atts, "right", right.toString()); } if (innerBackgroundColor != null) { @@ -589,6 +631,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) throws IFException { try { + addID(); AttributesImpl atts = new AttributesImpl(); addAttribute(atts, "x1", Integer.toString(start.x)); addAttribute(atts, "y1", Integer.toString(start.y)); @@ -605,8 +648,9 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler /** {@inheritDoc} */ public void drawText(int x, int y, int letterSpacing, int wordSpacing, - int[] dx, String text) throws IFException { + int[][] dp, String text) throws IFException { try { + addID(); AttributesImpl atts = new AttributesImpl(); addAttribute(atts, "x", Integer.toString(x)); addAttribute(atts, "y", Integer.toString(y)); @@ -616,10 +660,19 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler if (wordSpacing != 0) { addAttribute(atts, "word-spacing", Integer.toString(wordSpacing)); } - if (dx != null) { - addAttribute(atts, "dx", IFUtil.toString(dx)); + if (dp != null) { + if ( IFUtil.isDPIdentity(dp) ) { + // don't add dx or dp attribute + } else if ( IFUtil.isDPOnlyDX(dp) ) { + // add dx attribute only + int[] dx = IFUtil.convertDPToDX(dp); + addAttribute(atts, "dx", IFUtil.toString(dx)); + } else { + // add dp attribute only + addAttribute(atts, "dp", XMLUtil.encodePositionAdjustments(dp)); + } } - addStructurePointerAttribute(atts); + addStructureReference(atts); handler.startElement(EL_TEXT, atts); char[] chars = text.toCharArray(); handler.characters(chars, 0, chars.length); @@ -671,7 +724,8 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } } if (color != null) { - changed = !color.equals(state.getTextColor()); + changed = !org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor( + color, state.getTextColor()); if (changed) { state.setTextColor(color); addAttribute(atts, "color", toString(color)); @@ -718,14 +772,32 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler XMLUtil.addAttribute(atts, localName, value); } - private void addStructurePointerAttribute(AttributesImpl atts) { - String ptr = getContext().getStructurePointer(); - if (ptr != null) { - addAttribute(atts, "ptr", ptr); + private void addStructureReference(AttributesImpl atts) { + IFStructureTreeElement structureTreeElement + = (IFStructureTreeElement) getContext().getStructureTreeElement(); + if (structureTreeElement != null) { + addStructRefAttribute(atts, structureTreeElement.getId()); } } - // ---=== IFDocumentNavigationHandler ===--- + private void addStructRefAttribute(AttributesImpl atts, String id) { + atts.addAttribute(InternalElementMapping.URI, + InternalElementMapping.STRUCT_REF, + InternalElementMapping.STANDARD_PREFIX + ":" + InternalElementMapping.STRUCT_REF, + XMLConstants.CDATA, + id); + } + + private void addID() throws SAXException { + String id = getContext().getID(); + if (!currentID.equals(id)) { + AttributesImpl atts = new AttributesImpl(); + addAttribute(atts, "name", id); + handler.startElement(EL_ID, atts); + handler.endElement(EL_ID); + currentID = id; + } + } private Map incompleteActions = new java.util.HashMap(); private List completeActions = new java.util.LinkedList(); @@ -763,7 +835,9 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler Iterator iter = tree.getBookmarks().iterator(); while (iter.hasNext()) { Bookmark b = (Bookmark)iter.next(); - serializeBookmark(b); + if (b.getAction() != null) { + serializeBookmark(b); + } } handler.endElement(DocumentNavigationExtensionConstants.BOOKMARK_TREE); } catch (SAXException e) { @@ -783,10 +857,11 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler Iterator iter = bookmark.getChildBookmarks().iterator(); while (iter.hasNext()) { Bookmark b = (Bookmark)iter.next(); - serializeBookmark(b); + if (b.getAction() != null) { + serializeBookmark(b); + } } handler.endElement(DocumentNavigationExtensionConstants.BOOKMARK); - } /** {@inheritDoc} */ @@ -797,7 +872,8 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler atts.addAttribute(null, "rect", "rect", XMLConstants.CDATA, IFUtil.toString(link.getTargetRect())); if (getUserAgent().isAccessibilityEnabled()) { - addAttribute(atts, "ptr", link.getAction().getStructurePointer()); + addStructRefAttribute(atts, + ((IFStructureTreeElement) link.getAction().getStructureTreeElement()).getId()); } try { handler.startElement(DocumentNavigationExtensionConstants.LINK, atts); @@ -843,12 +919,8 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ - public boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter, - BorderProps bpsStart, BorderProps bpsEnd) { + public boolean isBackgroundRequired(BorderProps bpsTop, BorderProps bpsBottom, + BorderProps bpsLeft, BorderProps bpsRight) { return true; } - - - - } diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializerMaker.java b/src/java/org/apache/fop/render/intermediate/IFSerializerMaker.java index 699fd0549..c0060ab8e 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializerMaker.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializerMaker.java @@ -31,6 +31,9 @@ public class IFSerializerMaker extends AbstractIFDocumentHandlerMaker { public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) { IFSerializer handler = new IFSerializer(); handler.setContext(new IFContext(ua)); + if (ua.isAccessibilityEnabled()) { + ua.setStructureTreeEventHandler(handler.getStructureTreeEventHandler()); + } return handler; } diff --git a/src/java/org/apache/fop/render/intermediate/IFState.java b/src/java/org/apache/fop/render/intermediate/IFState.java index c13382192..4d8325d3e 100644 --- a/src/java/org/apache/fop/render/intermediate/IFState.java +++ b/src/java/org/apache/fop/render/intermediate/IFState.java @@ -21,6 +21,8 @@ package org.apache.fop.render.intermediate; import java.awt.Color; +import org.apache.xmlgraphics.java2d.color.ColorUtil; + /** a state class for intermediate format data */ public final class IFState { @@ -184,7 +186,7 @@ public final class IFState { * @param color the new text color */ public void setTextColor(Color color) { - if (!color.equals(this.textColor)) { + if (!ColorUtil.isSameColor(color, this.textColor)) { this.fontChanged = true; } this.textColor = color; diff --git a/src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java b/src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java new file mode 100644 index 000000000..9ba9afd81 --- /dev/null +++ b/src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java @@ -0,0 +1,239 @@ +/* + * 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.render.intermediate; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; +import org.xml.sax.helpers.DefaultHandler; + +import org.apache.fop.accessibility.StructureTree2SAXEventAdapter; +import org.apache.fop.accessibility.StructureTreeElement; +import org.apache.fop.accessibility.StructureTreeEventHandler; +import org.apache.fop.fo.extensions.InternalElementMapping; +import org.apache.fop.util.XMLUtil; + +/** + * Saves structure tree events as SAX events in order to replay them when it's + * time to stream the structure tree to the output. + */ +final class IFStructureTreeBuilder implements StructureTreeEventHandler { + + static final class IFStructureTreeElement implements StructureTreeElement { + + private final String id; + + IFStructureTreeElement() { + this.id = null; + } + + IFStructureTreeElement(String id) { + this.id = id; + } + + public String getId() { + return id; + } + } + + /** A SAX handler that records events to replay them later. */ + static class SAXEventRecorder extends DefaultHandler { + + private final List<SAXEventRecorder.Event> events = new ArrayList<SAXEventRecorder.Event>(); + + private abstract static class Event { + abstract void replay(ContentHandler handler) throws SAXException; + } + + private abstract static class Element extends SAXEventRecorder.Event { + + protected final String uri; + protected final String localName; + protected final String qName; + + private Element(String uri, String localName, String qName) { + this.uri = uri; + this.localName = localName; + this.qName = qName; + } + } + + private static final class StartElement extends SAXEventRecorder.Element { + + private final Attributes attributes; + + private StartElement(String uri, String localName, String qName, + Attributes attributes) { + super(uri, localName, qName); + this.attributes = attributes; + } + + @Override + void replay(ContentHandler handler) throws SAXException { + handler.startElement(uri, localName, qName, attributes); + } + } + + private static final class EndElement extends SAXEventRecorder.Element { + + private EndElement(String uri, String localName, String qName) { + super(uri, localName, qName); + } + + @Override + void replay(ContentHandler handler) throws SAXException { + handler.endElement(uri, localName, qName); + } + } + + private static final class StartPrefixMapping extends SAXEventRecorder.Event { + + private final String prefix; + private final String uri; + + private StartPrefixMapping(String prefix, String uri) { + this.prefix = prefix; + this.uri = uri; + } + + @Override + void replay(ContentHandler handler) throws SAXException { + handler.startPrefixMapping(prefix, uri); + } + } + + private static final class EndPrefixMapping extends SAXEventRecorder.Event { + + private final String prefix; + + private EndPrefixMapping(String prefix) { + this.prefix = prefix; + } + + @Override + void replay(ContentHandler handler) throws SAXException { + handler.endPrefixMapping(prefix); + } + } + + @Override + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { + events.add(new StartElement(uri, localName, qName, attributes)); + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + events.add(new EndElement(uri, localName, qName)); + } + + @Override + public void startPrefixMapping(String prefix, String uri) throws SAXException { + events.add(new StartPrefixMapping(prefix, uri)); + } + + @Override + public void endPrefixMapping(String prefix) throws SAXException { + events.add(new EndPrefixMapping(prefix)); + } + + /** + * Replays the recorded events. + * + * @param handler {@code ContentHandler} to replay events on + */ + public void replay(ContentHandler handler) throws SAXException { + for (SAXEventRecorder.Event e : events) { + e.replay(handler); + } + } + } + + private StructureTreeEventHandler delegate; + + private final List<SAXEventRecorder> pageSequenceEventRecorders + = new ArrayList<SAXEventRecorder>(); + + private int idCounter; + + /** + * Replay SAX events for a page sequence. + * @param handler The handler that receives SAX events + * @param pageSequenceIndex The index of the page sequence + * @throws SAXException + */ + public void replayEventsForPageSequence(ContentHandler handler, + int pageSequenceIndex) throws SAXException { + pageSequenceEventRecorders.get(pageSequenceIndex).replay(handler); + } + + public void startPageSequence(Locale locale, String role) { + SAXEventRecorder eventRecorder = new SAXEventRecorder(); + pageSequenceEventRecorders.add(eventRecorder); + delegate = StructureTree2SAXEventAdapter.newInstance(eventRecorder); + delegate.startPageSequence(locale, role); + } + + public void endPageSequence() { + delegate.endPageSequence(); + } + + public StructureTreeElement startNode(String name, Attributes attributes) { + delegate.startNode(name, attributes); + return new IFStructureTreeElement(); + } + + public void endNode(String name) { + delegate.endNode(name); + } + + public StructureTreeElement startImageNode(String name, Attributes attributes) { + String id = getNextID(); + AttributesImpl atts = addIDAttribute(attributes, id); + delegate.startImageNode(name, atts); + return new IFStructureTreeElement(id); + } + + public StructureTreeElement startReferencedNode(String name, Attributes attributes) { + String id = getNextID(); + AttributesImpl atts = addIDAttribute(attributes, id); + delegate.startReferencedNode(name, atts); + return new IFStructureTreeElement(id); + } + + private String getNextID() { + return Integer.toHexString(idCounter++); + } + + private AttributesImpl addIDAttribute(Attributes attributes, String id) { + AttributesImpl atts = new AttributesImpl(attributes); + atts.addAttribute(InternalElementMapping.URI, + InternalElementMapping.STRUCT_ID, + InternalElementMapping.STANDARD_PREFIX + ":" + InternalElementMapping.STRUCT_ID, + XMLUtil.CDATA, + id); + return atts; + } +} diff --git a/src/java/org/apache/fop/render/intermediate/IFUtil.java b/src/java/org/apache/fop/render/intermediate/IFUtil.java index 1867b0294..c4f681936 100644 --- a/src/java/org/apache/fop/render/intermediate/IFUtil.java +++ b/src/java/org/apache/fop/render/intermediate/IFUtil.java @@ -22,10 +22,10 @@ package org.apache.fop.render.intermediate; import java.awt.Rectangle; import java.awt.geom.AffineTransform; +import org.apache.xmlgraphics.util.DoubleFormatUtil; + import org.apache.fop.apps.FOPException; -import org.apache.fop.apps.FopFactory; import org.apache.fop.fonts.FontInfo; -import org.apache.fop.util.DecimalFormatCache; /** * Utility functions for the intermediate format. @@ -41,7 +41,9 @@ public final class IFUtil { //See http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2.3 value = 0.0; } - return DecimalFormatCache.getDecimalFormat(6).format(value); + StringBuffer buf = new StringBuffer(); + DoubleFormatUtil.formatDouble(value, 6, 6, buf); + return buf.toString(); } /** @@ -145,8 +147,8 @@ public final class IFUtil { /** * Sets up the fonts on a document handler. If the document handler provides a configurator - * object the configuration from the {@link FopFactory} will be used. Otherwise, - * a default font configuration will be set up. + * object the configuration from the {@link org.apache.fop.apps.FopFactory} will be used. + * Otherwise, a default font configuration will be set up. * @param documentHandler the document handler * @param fontInfo the font info object (may be null) * @throws FOPException if an error occurs while setting up the fonts @@ -173,8 +175,8 @@ public final class IFUtil { /** * Sets up the fonts on a document handler. If the document handler provides a configurator - * object the configuration from the {@link FopFactory} will be used. Otherwise, - * a default font configuration will be set up. + * object the configuration from the {@link org.apache.fop.apps.FopFactory} will be used. + * Otherwise, a default font configuration will be set up. * @param documentHandler the document handler * @throws FOPException if an error occurs while setting up the fonts */ @@ -199,4 +201,183 @@ public final class IFUtil { return documentHandler.getMimeType(); } + /** + * Convert the general gpos 'dp' adjustments to the older 'dx' adjustments. + * This utility method is used to provide backward compatibility in implementations + * of IFPainter that have not yet been upgraded to the general position adjustments format. + * @param dp an array of 4-tuples, expressing [X,Y] placment + * adjustments and [X,Y] advancement adjustments, in that order (may be null) + * @param count if <code>dp</code> is not null, then a count of dp values to convert + * @return if <code>dp</code> is not null, then an array of adjustments to the current + * x position prior to rendering individual glyphs; otherwise, null + */ + public static int[] convertDPToDX ( int[][] dp, int count ) { + int[] dx; + if ( dp != null ) { + dx = new int [ count ]; + for ( int i = 0, n = count; i < n; i++ ) { + dx [ i ] = dp [ i ] [ 0 ]; // xPlaAdjust[i] + } + } else { + dx = null; + } + return dx; + } + + /** + * Convert the general gpos 'dp' adjustments to the older 'dx' adjustments. + * This utility method is used to provide backward compatibility in implementations + * of IFPainter that have not yet been upgraded to the general position adjustments format. + * @param dp an array of 4-tuples, expressing [X,Y] placment + * adjustments and [X,Y] advancement adjustments, in that order (may be null) + * @return if <code>dp</code> is not null, then an array of adjustments to the current + * x position prior to rendering individual glyphs; otherwise, null + */ + public static int[] convertDPToDX ( int[][] dp ) { + return convertDPToDX ( dp, ( dp != null ) ? dp.length : 0 ); + } + + /** + * Convert the general gpos 'dp' adjustments to the older 'dx' adjustments. + * This utility method is used to provide backward compatibility in implementations + * of IFPainter that have not yet been upgraded to the general position adjustments format. + * @param dx an array of adjustments to the current x position prior to rendering + * individual glyphs or null + * @param count if <code>dx</code> is not null, then a count of dx values to convert + * @return if <code>dx</code> is not null, then an array of 4-tuples, expressing [X,Y] + * placment adjustments and [X,Y] advancement adjustments, in that order; otherwise, null + */ + public static int[][] convertDXToDP ( int[] dx, int count ) { + int[][] dp; + if ( dx != null ) { + dp = new int [ count ] [ 4 ]; + for ( int i = 0, n = count; i < n; i++ ) { + int[] pa = dp [ i ]; + int d = dx [ i ]; + pa [ 0 ] = d; // xPlaAdjust[i] + pa [ 2 ] = d; // xAdvAdjust[i] + } + } else { + dp = null; + } + return dp; + } + + /** + * Convert the general gpos 'dp' adjustments to the older 'dx' adjustments. + * This utility method is used to provide backward compatibility in implementations + * of IFPainter that have not yet been upgraded to the general position adjustments format. + * @param dx an array of adjustments to the current x position prior to rendering + * individual glyphs or null + * @return if <code>dx</code> is not null, then an array of 4-tuples, expressing [X,Y] + * placment adjustments and [X,Y] advancement adjustments, in that order; otherwise, null + */ + public static int[][] convertDXToDP ( int[] dx ) { + return convertDXToDP ( dx, ( dx != null ) ? dx.length : 0 ); + } + + /** + * Determine if position adjustment is the identity adjustment, i.e., no non-zero adjustment. + * @param pa a 4-tuple, expressing [X,Y] placment and [X,Y] advance adjuustments (may be null) + * @return true if <code>dp</code> is null or contains no non-zero adjustment + */ + public static boolean isPAIdentity ( int[] pa ) { + if ( pa == null ) { + return true; + } else { + for ( int k = 0; k < 4; k++ ) { + if ( pa[k] != 0 ) { + return false; + } + } + return true; + } + } + + /** + * Determine if position adjustments is the identity adjustment, i.e., no non-zero adjustment. + * @param dp an array of 4-tuples, expressing [X,Y] placment + * adjustments and [X,Y] advancement adjustments, in that order (may be null) + * @return true if <code>dp</code> is null or contains no non-zero adjustment + */ + public static boolean isDPIdentity ( int[][] dp ) { + if ( dp == null ) { + return true; + } else { + for ( int i = 0, n = dp.length; i < n; i++ ) { + if ( !isPAIdentity ( dp[i] ) ) { + return false; + } + } + return true; + } + } + + /** + * Determine if position adjustments comprises only DX adjustments as encoded by + * {@link #convertDPToDX}. Note that if given a set of all all zero position + * adjustments, both this method and {@link #isDPIdentity} will return true; + * however, this method may return true when {@link #isDPIdentity} returns false. + * @param dp an array of 4-tuples, expressing [X,Y] placment + * adjustments and [X,Y] advancement adjustments, in that order (may be null) + * @return true if <code>dp</code> is not null and contains only xPlaAdjust + * and xAdvAdjust values consistent with the output of {@link #convertDPToDX}. + */ + public static boolean isDPOnlyDX ( int[][] dp ) { + if ( dp == null ) { + return false; + } else { + for ( int i = 0, n = dp.length; i < n; i++ ) { + int[] pa = dp[i]; + if ( pa[0] != pa[2] ) { + return false; + } + } + return true; + } + } + + /** + * Adjust a position adjustments array. If both <code>paDst</code> and <code>paSrc</code> are + * non-null, then <code>paSrc[i]</code> is added to <code>paDst[i]</code>. + * @param paDst a 4-tuple, expressing [X,Y] placment + * and [X,Y] advance adjuustments (may be null) + * @param paSrc a 4-tuple, expressing [X,Y] placment + * and [X,Y] advance adjuustments (may be null) + */ + public static void adjustPA ( int[] paDst, int[] paSrc ) { + if ( ( paDst != null ) && ( paSrc != null ) ) { + assert paDst.length == 4; + assert paSrc.length == 4; + for ( int i = 0; i < 4; i++ ) { + paDst[i] += paSrc[i]; + } + } + } + + /** + * Copy entries from position adjustments. + * @param dp an array of 4-tuples, expressing [X,Y] placment + * adjustments and [X,Y] advancement adjustments, in that order + * @param offset starting offset from which to copy + * @param count number of entries to copy + * @return a deep copy of the count position adjustment entries start at + * offset + */ + public static int[][] copyDP ( int[][] dp, int offset, int count ) { + if ( ( dp == null ) || ( offset > dp.length ) || ( ( offset + count ) > dp.length ) ) { + throw new IllegalArgumentException(); + } else { + int[][] dpNew = new int [ count ] [ 4 ]; + for ( int i = 0, n = count; i < n; i++ ) { + int[] paDst = dpNew [ i ]; + int[] paSrc = dp [ i + offset ]; + for ( int k = 0; k < 4; k++ ) { + paDst [ k ] = paSrc [ k ]; + } + } + return dpNew; + } + } + } diff --git a/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java b/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java index 340b2e068..a2595d320 100644 --- a/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java +++ b/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java @@ -21,13 +21,15 @@ package org.apache.fop.render.intermediate.extensions; import org.apache.xmlgraphics.util.XMLizable; +import org.apache.fop.accessibility.StructureTreeElement; + /** * Abstract base class for document actions, like "go-to" actions with absolute page coordinates. */ public abstract class AbstractAction implements XMLizable { private String id; - private String structurePointer; + private StructureTreeElement structureTreeElement; /** * Sets an ID to make the action referencable. @@ -47,18 +49,18 @@ public abstract class AbstractAction implements XMLizable { /** * Sets the structure element corresponding to this action. - * @param structurePointer a reference to the structure element + * @param structureTreeElement a reference to the structure element */ - public void setStructurePointer(String structurePointer) { - this.structurePointer = structurePointer; + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; } /** * Returns the structure element corresponding to this action. * @return the reference to the structure element */ - public String getStructurePointer() { - return structurePointer; + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; } /** diff --git a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java index 5ca480f4a..c174a74a9 100644 --- a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java +++ b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java @@ -21,6 +21,7 @@ package org.apache.fop.render.intermediate.extensions; import java.awt.Point; import java.awt.Rectangle; +import java.util.Map; import java.util.Stack; import org.xml.sax.Attributes; @@ -30,6 +31,8 @@ import org.xml.sax.helpers.DefaultHandler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.accessibility.StructureTreeElement; +import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.render.intermediate.IFDocumentNavigationHandler; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.util.XMLUtil; @@ -41,21 +44,27 @@ public class DocumentNavigationHandler extends DefaultHandler implements DocumentNavigationExtensionConstants { /** Logger instance */ - protected static Log log = LogFactory.getLog(DocumentNavigationHandler.class); + protected static final Log log = LogFactory.getLog(DocumentNavigationHandler.class); private StringBuffer content = new StringBuffer(); private Stack objectStack = new Stack(); private IFDocumentNavigationHandler navHandler; - private String structurePointer; + private StructureTreeElement structureTreeElement; + + private Map<String, StructureTreeElement> structureTreeElements; /** * Main constructor. * @param navHandler the navigation handler that will receive the events + * @param structureTreeElements the elements representing the structure of the document */ - public DocumentNavigationHandler(IFDocumentNavigationHandler navHandler) { + public DocumentNavigationHandler(IFDocumentNavigationHandler navHandler, + Map<String, StructureTreeElement> structureTreeElements) { this.navHandler = navHandler; + assert structureTreeElements != null; + this.structureTreeElements = structureTreeElements; } /** {@inheritDoc} */ @@ -98,7 +107,8 @@ public class DocumentNavigationHandler extends DefaultHandler throw new SAXException(localName + " must be the root element!"); } Rectangle targetRect = XMLUtil.getAttributeAsRectangle(attributes, "rect"); - structurePointer = attributes.getValue("ptr"); + structureTreeElement = structureTreeElements.get(attributes.getValue( + InternalElementMapping.URI, InternalElementMapping.STRUCT_REF)); Link link = new Link(null, targetRect); objectStack.push(link); } else if (GOTO_XY.getLocalName().equals(localName)) { @@ -121,8 +131,8 @@ public class DocumentNavigationHandler extends DefaultHandler } action = new GoToXYAction(id, pageIndex, location); } - if (structurePointer != null) { - action.setStructurePointer(structurePointer); + if (structureTreeElement != null) { + action.setStructureTreeElement(structureTreeElement); } objectStack.push(action); } else if (GOTO_URI.getLocalName().equals(localName)) { @@ -134,8 +144,8 @@ public class DocumentNavigationHandler extends DefaultHandler if (id != null) { action.setID(id); } - if (structurePointer != null) { - action.setStructurePointer(structurePointer); + if (structureTreeElement != null) { + action.setStructureTreeElement(structureTreeElement); } objectStack.push(action); } else { diff --git a/src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java b/src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java index a2b4f31b6..4bd548c73 100644 --- a/src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java +++ b/src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java @@ -74,7 +74,7 @@ public class GoToXYAction extends AbstractAction implements DocumentNavigationEx * <p> * This function will always return a valid value for safety. Use * {@link #isComplete()} to check if the link is actually complete. - * + * * @return the page index (0-based) */ public int getPageIndex() { @@ -90,7 +90,7 @@ public class GoToXYAction extends AbstractAction implements DocumentNavigationEx * <p> * This function will always return a valid value for safety. Use * {@link #isComplete()} to check if the link is actually complete. - * + * * @return the target location (coordinates in millipoints) */ public Point getTargetLocation() { @@ -112,7 +112,7 @@ public class GoToXYAction extends AbstractAction implements DocumentNavigationEx private boolean isCompleteExceptTargetLocation() { return (getPageIndex() >= 0); } - + /** {@inheritDoc} */ public boolean isComplete() { return this.isCompleteExceptTargetLocation() && (this.targetLocation != null); @@ -147,9 +147,9 @@ public class GoToXYAction extends AbstractAction implements DocumentNavigationEx atts.addAttribute(null, "id", "id", XMLUtil.CDATA, getID()); atts.addAttribute(null, "page-index", "page-index", XMLUtil.CDATA, Integer.toString(pageIndex)); - atts.addAttribute(null, "x", "x", XMLUtil.CDATA, + atts.addAttribute(null, "x", "x", XMLUtil.CDATA, Integer.toString(reportedTargetLocation.x)); - atts.addAttribute(null, "y", "y", XMLUtil.CDATA, + atts.addAttribute(null, "y", "y", XMLUtil.CDATA, Integer.toString(reportedTargetLocation.y)); } else { atts.addAttribute(null, "idref", "idref", XMLUtil.CDATA, getID()); diff --git a/src/java/org/apache/fop/render/intermediate/util/IFConcatenator.java b/src/java/org/apache/fop/render/intermediate/util/IFConcatenator.java index 4b0a3fe68..71ee7a0b0 100644 --- a/src/java/org/apache/fop/render/intermediate/util/IFConcatenator.java +++ b/src/java/org/apache/fop/render/intermediate/util/IFConcatenator.java @@ -19,7 +19,6 @@ package org.apache.fop.render.intermediate.util; - import java.awt.Dimension; import javax.xml.transform.Source; @@ -39,12 +38,17 @@ import org.apache.fop.render.intermediate.IFParser; * <p> * Note: This class will filter/ignore any document navigation events. Support for this may be * added later. + * <p> + * Note: document-level extensions will only be transferred from the first document passed in. + * If you need to merge extensions from all the concatenated documents, you may have to merge + * these manually on the XML level, for example using XSLT. */ public class IFConcatenator { private IFDocumentHandler targetHandler; private int nextPageIndex = 0; + private boolean inFirstDocument = true; /** * Creates a new IF concatenator. @@ -163,14 +167,17 @@ public class IFConcatenator { /** {@inheritDoc} */ public void endDocument() throws IFException { //ignore + inFirstDocument = false; } /** {@inheritDoc} */ public void handleExtensionObject(Object extension) throws IFException { - if (inPageSequence) { + if (inPageSequence || inFirstDocument) { //Only pass through when inside page-sequence + //or for the first document (for document-level extensions). super.handleExtensionObject(extension); } + //Note:Extensions from non-first documents are ignored! } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/intermediate/util/IFDocumentHandlerProxy.java b/src/java/org/apache/fop/render/intermediate/util/IFDocumentHandlerProxy.java index 3d89e812e..dde744180 100644 --- a/src/java/org/apache/fop/render/intermediate/util/IFDocumentHandlerProxy.java +++ b/src/java/org/apache/fop/render/intermediate/util/IFDocumentHandlerProxy.java @@ -20,9 +20,11 @@ package org.apache.fop.render.intermediate.util; import java.awt.Dimension; +import java.util.Locale; import javax.xml.transform.Result; +import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.fonts.FontInfo; import org.apache.fop.render.intermediate.IFContext; import org.apache.fop.render.intermediate.IFDocumentHandler; @@ -94,6 +96,11 @@ public class IFDocumentHandlerProxy implements IFDocumentHandler { } /** {@inheritDoc} */ + public StructureTreeEventHandler getStructureTreeEventHandler() { + return this.delegate.getStructureTreeEventHandler(); + } + + /** {@inheritDoc} */ public void setResult(Result result) throws IFException { this.delegate.setResult(result); } @@ -104,6 +111,12 @@ public class IFDocumentHandlerProxy implements IFDocumentHandler { } /** {@inheritDoc} */ + public void setDocumentLocale(Locale locale) { + this.delegate.setDocumentLocale(locale); + + } + + /** {@inheritDoc} */ public void startDocumentHeader() throws IFException { this.delegate.startDocumentHeader(); } @@ -184,4 +197,4 @@ public class IFDocumentHandlerProxy implements IFDocumentHandler { this.delegate.handleExtensionObject(extension); } -}
\ No newline at end of file +} |