diff options
author | Peter Hancock <phancock@apache.org> | 2012-10-08 09:24:03 +0000 |
---|---|---|
committer | Peter Hancock <phancock@apache.org> | 2012-10-08 09:24:03 +0000 |
commit | e079e8eb1900e3287f28a34a6053b62ae4b241be (patch) | |
tree | b9e15118900d547c1d70ba2a933569c1f50d9057 /src/java/org | |
parent | af57408966c6e9a525475f52bdc96add31cb0e51 (diff) | |
download | xmlgraphics-fop-e079e8eb1900e3287f28a34a6053b62ae4b241be.tar.gz xmlgraphics-fop-e079e8eb1900e3287f28a34a6053b62ae4b241be.zip |
Increased unit test coverage for the Painters
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1395490 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org')
18 files changed, 2329 insertions, 1931 deletions
diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java index 3648f7091..b96283a4e 100644 --- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java +++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java @@ -80,19 +80,19 @@ public final class TraitSetter { addBorderTrait(area, bpProps, isNotFirst, CommonBorderPaddingBackground.START, - BorderProps.SEPARATE, Trait.BORDER_START, context); + BorderProps.Mode.SEPARATE, Trait.BORDER_START, context); addBorderTrait(area, bpProps, isNotLast, CommonBorderPaddingBackground.END, - BorderProps.SEPARATE, Trait.BORDER_END, context); + BorderProps.Mode.SEPARATE, Trait.BORDER_END, context); addBorderTrait(area, bpProps, false, CommonBorderPaddingBackground.BEFORE, - BorderProps.SEPARATE, Trait.BORDER_BEFORE, context); + BorderProps.Mode.SEPARATE, Trait.BORDER_BEFORE, context); addBorderTrait(area, bpProps, false, CommonBorderPaddingBackground.AFTER, - BorderProps.SEPARATE, Trait.BORDER_AFTER, context); + BorderProps.Mode.SEPARATE, Trait.BORDER_AFTER, context); } /* @@ -104,18 +104,14 @@ public final class TraitSetter { */ private static void addBorderTrait(Area area, CommonBorderPaddingBackground bpProps, - boolean bDiscard, int iSide, int mode, - Integer oTrait, PercentBaseContext context) { + boolean bDiscard, int iSide, BorderProps.Mode mode, + Integer traitCode, PercentBaseContext context) { int iBP = bpProps.getBorderWidth(iSide, bDiscard); int radiusStart = bpProps.getBorderRadiusStart(iSide, bDiscard, context); int radiusEnd = bpProps.getBorderRadiusEnd(iSide, bDiscard, context); if (iBP > 0 || radiusStart > 0 || radiusEnd > 0) { - BorderProps bps = new BorderProps(bpProps.getBorderStyle(iSide), - iBP, bpProps.getBorderColor(iSide), - mode); - bps.setRadiusStart(radiusStart); - bps.setRadiusEnd(radiusEnd); - area.addTrait(oTrait, bps); + area.addTrait(traitCode, new BorderProps(bpProps.getBorderStyle(iSide), iBP, radiusStart, radiusEnd, + bpProps.getBorderColor(iSide), mode)); } } @@ -273,14 +269,8 @@ public final class TraitSetter { int radiusStart = bordProps.getBorderRadiusStart(side, false, context); int radiusEnd = bordProps.getBorderRadiusEnd(side, false, context); if (width != 0 || radiusStart != 0 || radiusEnd != 0) { - BorderProps bps; - bps = new BorderProps(bordProps.getBorderStyle(side), - width, - bordProps.getBorderColor(side), - BorderProps.SEPARATE); - bps.setRadiusStart(radiusStart); - bps.setRadiusEnd(radiusEnd); - return bps; + return new BorderProps(bordProps.getBorderStyle(side), width, radiusStart, radiusEnd, + bordProps.getBorderColor(side), BorderProps.Mode.SEPARATE); } else { return null; } @@ -290,8 +280,8 @@ public final class TraitSetter { assert borderInfo != null; int width = borderInfo.getRetainedWidth(); if (width != 0) { - return new BorderProps(borderInfo.getStyle(), width, borderInfo.getColor(), - (outer ? BorderProps.COLLAPSE_OUTER : BorderProps.COLLAPSE_INNER)); + return BorderProps.makeRectangular(borderInfo.getStyle(), width, borderInfo.getColor(), + (outer ? BorderProps.Mode.COLLAPSE_OUTER : BorderProps.Mode.COLLAPSE_INNER)); } else { return null; } diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java index f810d20c5..5e9b785d2 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java @@ -597,9 +597,9 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager blocks[i][j].addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE); blocks[i][j].setPositioning(Block.ABSOLUTE); } - blocks[i][j].addTrait(side, new BorderProps(border.getStyle(), + blocks[i][j].addTrait(side, BorderProps.makeRectangular(border.getStyle(), border.getRetainedWidth(), border.getColor(), - outer ? BorderProps.COLLAPSE_OUTER : BorderProps.COLLAPSE_INNER)); + outer ? BorderProps.Mode.COLLAPSE_OUTER : BorderProps.Mode.COLLAPSE_INNER)); } private static void adjustXOffset(Block block, int amount) { diff --git a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java index c2bb56dcf..c16113de3 100644 --- a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java +++ b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java @@ -429,11 +429,12 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { moveTo(sx1, clipy); float sx1a = sx1; float ex1a = ex1; - if (bpsTop.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsLeft != null && bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { + + if (isCollapseOuter(bpsTop)) { + if (isCollapseOuter(bpsLeft)) { sx1a -= clipw[LEFT]; } - if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsRight)) { ex1a += clipw[RIGHT]; } lineTo(sx1a, outery); @@ -463,11 +464,11 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { moveTo(clipx, sy1); float sy1a = sy1; float ey1a = ey1; - if (bpsRight.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsTop != null && bpsTop.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsRight)) { + if (isCollapseOuter(bpsTop)) { sy1a -= clipw[TOP]; } - if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsBottom)) { ey1a += clipw[BOTTOM]; } lineTo(outerx, sy1a); @@ -497,11 +498,11 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { moveTo(ex1, clipy); float sx1a = sx1; float ex1a = ex1; - if (bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsLeft != null && bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsBottom)) { + if (isCollapseOuter(bpsLeft)) { sx1a -= clipw[LEFT]; } - if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsRight)) { ex1a += clipw[RIGHT]; } lineTo(ex1a, outery); @@ -531,11 +532,11 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { moveTo(clipx, ey1); float sy1a = sy1; float ey1a = ey1; - if (bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsTop != null && bpsTop.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsLeft)) { + if (isCollapseOuter(bpsTop)) { sy1a -= clipw[TOP]; } - if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsBottom)) { ey1a += clipw[BOTTOM]; } lineTo(outerx, ey1a); @@ -551,6 +552,10 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { } } + private boolean isCollapseOuter(BorderProps bp) { + return bp != null && bp.isCollapseOuter(); + } + /** * Common method to render the background and borders for any inline area. * The all borders and padding are drawn outside the specified area. diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java index cf97494fa..36afbb9e0 100644 --- a/src/java/org/apache/fop/render/afp/AFPPainter.java +++ b/src/java/org/apache/fop/render/afp/AFPPainter.java @@ -38,9 +38,6 @@ import java.util.Map; import org.w3c.dom.Document; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import org.apache.xmlgraphics.image.loader.Image; import org.apache.xmlgraphics.image.loader.ImageException; import org.apache.xmlgraphics.image.loader.ImageInfo; @@ -76,6 +73,7 @@ import org.apache.fop.render.ImageHandlerUtil; import org.apache.fop.render.RenderingContext; import org.apache.fop.render.intermediate.AbstractIFPainter; import org.apache.fop.render.intermediate.BorderPainter; +import org.apache.fop.render.intermediate.GraphicsPainter; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFState; import org.apache.fop.render.intermediate.IFUtil; @@ -88,15 +86,12 @@ import org.apache.fop.util.CharUtilities; */ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { - - - - //** logging instance */ - private static Log log = LogFactory.getLog(AFPPainter.class); - private static final int X = 0; + private static final int Y = 1; + private final GraphicsPainter graphicsPainter; + /** the border painter */ private final AFPBorderPainterAdapter borderPainter; /** the rectangle painter */ @@ -114,9 +109,9 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { public AFPPainter(AFPDocumentHandler documentHandler) { super(documentHandler); this.state = IFState.create(); - - this.borderPainter = new AFPBorderPainterAdapter( - new AFPBorderPainter(getPaintingState(), getDataStream()), this, documentHandler); + this.graphicsPainter = new AFPGraphicsPainter( + new AFPBorderPainter(getPaintingState(), getDataStream())); + this.borderPainter = new AFPBorderPainterAdapter(graphicsPainter, this, documentHandler); this.rectanglePainter = documentHandler.createRectanglePainter(); this.unitConv = getPaintingState().getUnitConverter(); this.eventProducer = AFPEventProducer.Provider.get(getUserAgent().getEventBroadcaster()); @@ -142,7 +137,7 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { /** {@inheritDoc} */ public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect) - throws IFException { + throws IFException { //AFP doesn't support clipping, so we treat viewport like a group //this is the same code as for startGroup() try { @@ -248,10 +243,10 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { /** {@inheritDoc} */ protected void drawImage(Image image, Rectangle rect, RenderingContext context, boolean convert, Map additionalHints) - throws IOException, ImageException { + throws IOException, ImageException { - AFPRenderingContext afpContext = (AFPRenderingContext)context; + AFPRenderingContext afpContext = (AFPRenderingContext) context; AFPResourceInfo resourceInfo = AFPImageHandler.createResourceInformation( image.getInfo().getOriginalURI(), @@ -292,7 +287,7 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { } if (rect.width != 0 && rect.height != 0) { if (fill instanceof Color) { - getPaintingState().setColor((Color)fill); + getPaintingState().setColor((Color) fill); } else { throw new UnsupportedOperationException("Non-Color paints NYI"); } @@ -315,13 +310,82 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { } + private static final class AFPGraphicsPainter implements GraphicsPainter { + + private final AFPBorderPainter graphicsPainter; + + private AFPGraphicsPainter(AFPBorderPainter delegate) { + this.graphicsPainter = delegate; + } + + public void drawBorderLine(int x1, int y1, int x2, int y2, + boolean horz, boolean startOrBefore, int style, Color color) + throws IOException { + BorderPaintingInfo borderPaintInfo = new BorderPaintingInfo( + toPoints(x1), toPoints(y1), toPoints(x2), toPoints(y2), + horz, style, color); + graphicsPainter.paint(borderPaintInfo); + } + + private float toPoints(int mpt) { + return mpt / 1000f; + } + + public void drawLine(Point start, Point end, int width, + Color color, RuleStyle style) throws IOException { + if (start.y != end.y) { + //TODO Support arbitrary lines if necessary + throw new UnsupportedOperationException("Can only deal with horizontal lines right now"); + } + //Simply delegates to drawBorderLine() as AFP line painting is not very sophisticated. + int halfWidth = width / 2; + drawBorderLine(start.x, start.y - halfWidth, end.x, start.y + halfWidth, + true, true, style.getEnumValue(), color); + } + + public void moveTo(int x, int y) throws IOException { + } + + public void lineTo(int x, int y) throws IOException { + } + + public void arcTo(double startAngle, double endAngle, int cx, int cy, + int width, int height) throws IOException { + } + + public void rotateCoordinates(double angle) throws IOException { + throw new UnsupportedOperationException("Cannot handle coordinate rotation"); + } + + public void translateCoordinates(int xTranslate, int yTranslate) throws IOException { + throw new UnsupportedOperationException("Cannot handle coordinate translation"); + } + + public void scaleCoordinates(float xScale, float yScale) throws IOException { + throw new UnsupportedOperationException("Cannot handle coordinate scaling"); + } + + public void closePath() throws IOException { + } + + public void clip() throws IOException { + } + + public void saveGraphicsState() throws IOException { + } + + public void restoreGraphicsState() throws IOException { + } + + + } + //TODO Try to resolve the name-clash between the AFPBorderPainter in the afp package //and this one. Not done for now to avoid a lot of re-implementation and code duplication. - private static class AFPBorderPainterAdapter extends BorderPainter { private final class BorderImagePainter implements Graphics2DImagePainter { - private final double esf; + private final double cornerCorrectionFactor; private final Rectangle borderRect; private final BorderProps bpsStart; private final BorderProps bpsEnd; @@ -331,11 +395,11 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { private final Color innerBackgroundColor; /* TODO represent border related parameters in a class */ - private BorderImagePainter(double esf, Rectangle borderRect, + private BorderImagePainter(double cornerCorrectionFactor, Rectangle borderRect, BorderProps bpsStart, BorderProps bpsEnd, BorderProps bpsBefore, BorderProps bpsAfter, boolean[] roundCorner, Color innerBackgroundColor) { - this.esf = esf; + this.cornerCorrectionFactor = cornerCorrectionFactor; this.borderRect = borderRect; this.bpsStart = bpsStart; this.bpsBefore = bpsBefore; @@ -350,13 +414,12 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { //background Area background = new Area(area); Area cornerRegion = new Area(); - Area[] cornerBorder - = new Area[]{new Area(), new Area(), new Area(), new Area()}; + Area[] cornerBorder = new Area[]{new Area(), new Area(), new Area(), new Area()}; if (roundCorner[TOP_LEFT]) { AffineTransform transform = new AffineTransform(); - int beforeRadius = (int)(esf * bpsBefore.getRadiusStart()); - int startRadius = (int)(esf * bpsStart.getRadiusStart()); + int beforeRadius = (int)(cornerCorrectionFactor * bpsBefore.getRadiusStart()); + int startRadius = (int)(cornerCorrectionFactor * bpsStart.getRadiusStart()); int beforeWidth = bpsBefore.width; int startWidth = bpsStart.width; @@ -380,8 +443,8 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { AffineTransform transform = new AffineTransform(-1, 0, 0, 1, borderRect.width, 0); - int beforeRadius = (int)(esf * bpsBefore.getRadiusEnd()); - int startRadius = (int)(esf * bpsEnd.getRadiusStart()); + int beforeRadius = (int)(cornerCorrectionFactor * bpsBefore.getRadiusEnd()); + int startRadius = (int)(cornerCorrectionFactor * bpsEnd.getRadiusStart()); int beforeWidth = bpsBefore.width; int startWidth = bpsEnd.width; @@ -405,8 +468,8 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { AffineTransform transform = new AffineTransform(-1, 0, 0, -1, borderRect.width, borderRect.height); - int beforeRadius = (int)(esf * bpsAfter.getRadiusEnd()); - int startRadius = (int)(esf * bpsEnd.getRadiusEnd()); + int beforeRadius = (int)(cornerCorrectionFactor * bpsAfter.getRadiusEnd()); + int startRadius = (int)(cornerCorrectionFactor * bpsEnd.getRadiusEnd()); int beforeWidth = bpsAfter.width; int startWidth = bpsEnd.width; @@ -429,8 +492,8 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { AffineTransform transform = new AffineTransform(1, 0, 0, -1, 0, borderRect.height); - int beforeRadius = (int)(esf * bpsAfter.getRadiusStart()); - int startRadius = (int)(esf * bpsStart.getRadiusEnd()); + int beforeRadius = (int)(cornerCorrectionFactor * bpsAfter.getRadiusStart()); + int startRadius = (int)(cornerCorrectionFactor * bpsStart.getRadiusEnd()); int beforeWidth = bpsAfter.width; int startWidth = bpsStart.width; @@ -499,8 +562,7 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { borderPath.lineTo( borderRect.width - (bpsEnd == null ? 0 : bpsEnd.width), borderRect.height - bpsAfter.width); - borderPath.lineTo( - bpsStart == null ? 0 : bpsStart.width, + borderPath.lineTo(bpsStart == null ? 0 : bpsStart.width, borderRect.height - bpsAfter.width); Area border = new Area(borderPath); @@ -536,13 +598,12 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { } } - private AFPBorderPainter delegate; private final AFPPainter painter; private final AFPDocumentHandler documentHandler; - public AFPBorderPainterAdapter(AFPBorderPainter borderPainter, AFPPainter painter, + public AFPBorderPainterAdapter(GraphicsPainter graphicsPainter, AFPPainter painter, AFPDocumentHandler documentHandler) { - this.delegate = borderPainter; + super(graphicsPainter); this.painter = painter; this.documentHandler = documentHandler; } @@ -559,55 +620,49 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { return !hasRoundedCorners(bpsBefore, bpsAfter, bpsStart, bpsEnd); } - private boolean hasRoundedCorners( final BorderProps bpsBefore, final BorderProps bpsAfter, + private boolean hasRoundedCorners(final BorderProps bpsBefore, final BorderProps bpsAfter, final BorderProps bpsStart, final BorderProps bpsEnd) { return ((bpsStart == null ? false : bpsStart.getRadiusStart() > 0) - && (bpsBefore == null ? false : bpsBefore.getRadiusStart() > 0)) - || ((bpsBefore == null ? false : bpsBefore.getRadiusEnd() > 0) - && (bpsEnd == null ? false : bpsEnd.getRadiusStart() > 0)) - || ((bpsEnd == null ? false : bpsEnd.getRadiusEnd() > 0) - && (bpsAfter == null ? false : bpsAfter.getRadiusEnd() > 0)) - || ((bpsAfter == null ? false : bpsAfter.getRadiusStart() > 0) - && (bpsStart == null ? false : bpsStart.getRadiusEnd() > 0)); + && (bpsBefore == null ? false : bpsBefore.getRadiusStart() > 0)) + || ((bpsBefore == null ? false : bpsBefore.getRadiusEnd() > 0) + && (bpsEnd == null ? false : bpsEnd.getRadiusStart() > 0)) + || ((bpsEnd == null ? false : bpsEnd.getRadiusEnd() > 0) + && (bpsAfter == null ? false : bpsAfter.getRadiusEnd() > 0)) + || ((bpsAfter == null ? false : bpsAfter.getRadiusStart() > 0) + && (bpsStart == null ? false : bpsStart.getRadiusEnd() > 0)); } private void drawRoundedCorners(final Rectangle borderRect, final BorderProps bpsBefore, final BorderProps bpsAfter, final BorderProps bpsStart, final BorderProps bpsEnd, - final Color innerBackgroundColor) - throws IFException { - - - final double esf = cornerScaleFactor(borderRect.width, borderRect.height, - bpsBefore, bpsAfter, - bpsStart, bpsEnd); - + final Color innerBackgroundColor) throws IFException { + final double cornerCorrectionFactor = calculateCornerCorrectionFactor(borderRect.width, + borderRect.height, bpsBefore, bpsAfter, bpsStart, bpsEnd); final boolean[] roundCorner = new boolean[]{ bpsBefore != null && bpsStart != null - && bpsBefore.getRadiusStart() > 0 - && bpsStart.getRadiusStart() > 0 - && bpsBefore.mode != BorderProps.COLLAPSE_OUTER - && bpsStart.mode != BorderProps.COLLAPSE_OUTER, - bpsEnd != null && bpsBefore != null - && bpsEnd.getRadiusStart() > 0 - && bpsBefore.getRadiusEnd() > 0 - && bpsEnd.mode != BorderProps.COLLAPSE_OUTER - && bpsBefore.mode != BorderProps.COLLAPSE_OUTER, - bpsEnd != null && bpsAfter != null - && bpsEnd.getRadiusEnd() > 0 - && bpsAfter.getRadiusEnd() > 0 - && bpsEnd.mode != BorderProps.COLLAPSE_OUTER - && bpsAfter.mode != BorderProps.COLLAPSE_OUTER, - bpsStart != null && bpsAfter != null - && bpsStart.getRadiusEnd() > 0 - && bpsAfter.getRadiusStart() > 0 - && bpsStart.mode != BorderProps.COLLAPSE_OUTER - && bpsAfter.mode != BorderProps.COLLAPSE_OUTER - }; - + && bpsBefore.getRadiusStart() > 0 + && bpsStart.getRadiusStart() > 0 + && isNotCollapseOuter(bpsBefore) + && isNotCollapseOuter(bpsStart), + bpsEnd != null && bpsBefore != null + && bpsEnd.getRadiusStart() > 0 + && bpsBefore.getRadiusEnd() > 0 + && isNotCollapseOuter(bpsEnd) + && isNotCollapseOuter(bpsBefore), + bpsEnd != null && bpsAfter != null + && bpsEnd.getRadiusEnd() > 0 + && bpsAfter.getRadiusEnd() > 0 + && isNotCollapseOuter(bpsEnd) + && isNotCollapseOuter(bpsAfter), + bpsStart != null && bpsAfter != null + && bpsStart.getRadiusEnd() > 0 + && bpsAfter.getRadiusStart() > 0 + && isNotCollapseOuter(bpsStart) + && isNotCollapseOuter(bpsAfter) + }; if (!roundCorner[TOP_LEFT] && !roundCorner[TOP_RIGHT] - && !roundCorner[BOTTOM_RIGHT] && !roundCorner[BOTTOM_LEFT]) { + && !roundCorner[BOTTOM_RIGHT] && !roundCorner[BOTTOM_LEFT]) { try { drawRectangularBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); } catch (IOException ioe) { @@ -627,16 +682,19 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { name = documentHandler.cacheRoundedCorner(areaKey); - painter = new BorderImagePainter(esf, borderRect, + painter = new BorderImagePainter(cornerCorrectionFactor, borderRect, bpsStart, bpsEnd, bpsBefore, bpsAfter, roundCorner, innerBackgroundColor); } paintCornersAsBitmap(painter, borderRect, name); } + private boolean isNotCollapseOuter(BorderProps bp) { + return !bp.isCollapseOuter(); + } private Area makeCornerClip(final int beforeRadius, final int startRadius, - final AffineTransform transform) { + final AffineTransform transform) { Rectangle clipR = new Rectangle(0, 0, startRadius, beforeRadius); @@ -678,7 +736,7 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { GeneralPath cut = new GeneralPath(); cut.moveTo(0, 0); - float borderWidthRatio = ((float)beforeWidth) / startWidth; + float borderWidthRatio = ((float) beforeWidth) / startWidth; if (beforeWidth * startRadius > startWidth * beforeRadius) { cut.lineTo(startRadius, borderWidthRatio * startRadius); cut.lineTo(startRadius, 0); @@ -718,7 +776,7 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { GeneralPath cut = new GeneralPath(); cut.moveTo(0, 0); - float borderWidthRatio = ((float)beforeWidth) / startWidth; + float borderWidthRatio = ((float) beforeWidth) / startWidth; if (beforeWidth * startRadius > startWidth * beforeRadius) { cut.lineTo(startRadius, borderWidthRatio * startRadius); cut.lineTo(startRadius, 0); @@ -735,7 +793,7 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { private String makeKey(Rectangle area, BorderProps beforeProps, BorderProps endProps, BorderProps afterProps, BorderProps startProps, - Color innerBackgroundColor) { + Color innerBackgroundColor) { return hash(new StringBuffer() .append(area.width) @@ -770,9 +828,9 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; for (int idx = 0; idx < 6; ++idx) { - byte b = result[idx]; - sb.append(digits[(b & 0xf0) >> 4]); - sb.append(digits[b & 0x0f]); + byte b = result[idx]; + sb.append(digits[(b & 0xf0) >> 4]); + sb.append(digits[b & 0x0f]); } return sb.toString(); } @@ -816,107 +874,19 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { } } - @Override - protected void clip() throws IOException { - //not supported by AFP - } - - @Override - protected void closePath() throws IOException { - //used for clipping only, so not implemented - } - - @Override - protected void moveTo(int x, int y) throws IOException { - //used for clipping only, so not implemented - } - - @Override - protected void lineTo(int x, int y) throws IOException { - //used for clipping only, so not implemented - } - - @Override - protected void saveGraphicsState() throws IOException { - //used for clipping only, so not implemented - } - - @Override - protected void restoreGraphicsState() throws IOException { - //used for clipping only, so not implemented - } - - private float toPoints(int mpt) { - return mpt / 1000f; - } - - @Override - protected void drawBorderLine( // CSOK: ParameterNumber - int x1, int y1, int x2, int y2, boolean horz, - boolean startOrBefore, int style, Color color) throws IOException { - BorderPaintingInfo borderPaintInfo = new BorderPaintingInfo( - toPoints(x1), toPoints(y1), toPoints(x2), toPoints(y2), - horz, style, color); - delegate.paint(borderPaintInfo); - } - - @Override - public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) - throws IOException { - if (start.y != end.y) { - //TODO Support arbitrary lines if necessary - throw new UnsupportedOperationException( - "Can only deal with horizontal lines right now"); - } - - //Simply delegates to drawBorderLine() as AFP line painting is not very sophisticated. - int halfWidth = width / 2; - drawBorderLine(start.x, start.y - halfWidth, end.x, start.y + halfWidth, - true, true, style.getEnumValue(), color); - } - - protected void arcTo(double startAngle, double endAngle, int cx, int cy, int width, int height) throws IOException { - throw new UnsupportedOperationException( - "Can only deal with horizontal lines right now"); - - } - + throw new UnsupportedOperationException("Can only deal with horizontal lines right now"); - protected void changeCoords(double a, double b, double c, double d, double e, double f) { - throw new UnsupportedOperationException( - "Can only deal with horizontal lines right now"); - } - - protected void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) - throws IOException { - throw new UnsupportedOperationException( - "Cannot handle cubic Bezier"); - } - - protected void rotateCoordinates(double angle) throws IOException { - throw new UnsupportedOperationException( - "Cannot handle coordinate rotation"); - } - - protected void scaleCoordinates(float xScale, float yScale) throws IOException { - throw new UnsupportedOperationException( - "Cannot handle coordinate scaling"); - } - - protected void translateCoordinates(int xTranslate, int yTranslate) throws IOException { - throw new UnsupportedOperationException( - "Cannot handle coordinate translation"); } } /** {@inheritDoc} */ @Override public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) - throws IFException { + throws IFException { try { - this.borderPainter.drawLine(start, end, width, color, style); + this.graphicsPainter.drawLine(start, end, width, color, style); } catch (IOException ioe) { throw new IFException("I/O error in drawLine()", ioe); } @@ -936,14 +906,14 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { // register font as necessary Map<String, Typeface> fontMetricMap = getFontInfo().getFonts(); - final AFPFont afpFont = (AFPFont)fontMetricMap.get(fontKey); + final AFPFont afpFont = (AFPFont) fontMetricMap.get(fontKey); final Font font = getFontInfo().getFontInstance(triplet, fontSize); AFPPageFonts pageFonts = getPaintingState().getPageFonts(); AFPFontAttributes fontAttributes = pageFonts.registerFont(fontKey, afpFont, fontSize); final int fontReference = fontAttributes.getFontReference(); - final int[] coords = unitConv.mpts2units(new float[] {x, y} ); + final int[] coords = unitConv.mpts2units(new float[] {x, y}); final CharacterSet charSet = afpFont.getCharacterSet(fontSize); @@ -967,7 +937,7 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { builder.absoluteMoveInline(p.x); builder.setExtendedTextColor(state.getTextColor()); - builder.setCodedFont((byte)fontReference); + builder.setCodedFont((byte) fontReference); int l = text.length(); int[] dx = IFUtil.convertDPToDX ( dp ); @@ -1106,15 +1076,12 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { /** {@inheritDoc} */ public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) throws IFException { - - //not supported by AFP } /** {@inheritDoc} */ public boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) { - return borderPainter.isBackgroundRequired( bpsBefore, bpsAfter, - bpsStart, bpsEnd); + return borderPainter.isBackgroundRequired(bpsBefore, bpsAfter, bpsStart, bpsEnd); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformer.java b/src/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformer.java new file mode 100644 index 000000000..112808f62 --- /dev/null +++ b/src/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformer.java @@ -0,0 +1,102 @@ +/* + * 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.io.IOException; + +public class ArcToBezierCurveTransformer { + + private final BezierCurvePainter bezierCurvePainter; + + public ArcToBezierCurveTransformer(BezierCurvePainter bezierCurvePainter) { + this.bezierCurvePainter = bezierCurvePainter; + } + + /** + * Draws an arc on the ellipse centered at (cx, cy) with width width and height height + * from start angle startAngle (with respect to the x-axis counter-clockwise) + * to the end angle endAngle. + * The ellipses major axis are assumed to coincide with the coordinate axis. + * The current position MUST coincide with the starting position on the ellipse. + * @param startAngle the start angle + * @param endAngle the end angle + * @param cx the x coordinate of the ellipse center + * @param cy the y coordinate of the ellipse center + * @param width the extent of the ellipse in the x direction + * @param height the extent of the ellipse in the y direction + * @throws IOException if an I/O error occurs + */ + public void arcTo(final double startAngle, final double endAngle, final int cx, final int cy, + final int width, final int height) throws IOException { + + // Implementation follows http://www.spaceroots.org/documents/ellipse/ - + // Drawing an elliptical arc using polylines, quadratic or cubic Bézier curves + // L. Maisonobe, July 21, 2003 + + // Scaling the coordinate system to represent the ellipse as a circle: + final double etaStart = Math.atan(Math.tan(startAngle) * width / height) + + quadrant(startAngle); + final double etaEnd = Math.atan(Math.tan(endAngle) * width / height) + + quadrant(endAngle); + + final double sinStart = Math.sin(etaStart); + final double cosStart = Math.cos(etaStart); + final double sinEnd = Math.sin(etaEnd); + final double cosEnd = Math.cos(etaEnd); + + final double p0x = cx + cosStart * width; + final double p0y = cy + sinStart * height; + final double p3x = cx + cosEnd * width; + final double p3y = cy + sinEnd * height; + + double etaDiff = Math.abs(etaEnd - etaStart); + double tan = Math.tan((etaDiff) / 2d); + final double alpha = Math.sin(etaDiff) * (Math.sqrt(4d + 3d * tan * tan) - 1d) / 3d; + + int order = etaEnd > etaStart ? 1 : -1; + + // p1 = p0 + alpha*(-sin(startAngle), cos(startAngle)) + final double p1x = p0x - alpha * sinStart * width * order; + final double p1y = p0y + alpha * cosStart * height * order; + + // p1 = p3 + alpha*(sin(endAngle), -cos(endAngle)) + final double p2x = p3x + alpha * sinEnd * width * order; + final double p2y = p3y - alpha * cosEnd * height * order; + + //Draw the curve in original coordinate system + bezierCurvePainter.cubicBezierTo((int) p1x, (int) p1y, (int) p2x, (int) p2y, (int) p3x, (int) p3y); + } + + private double quadrant(double angle) { + if (angle <= Math.PI) { + if (angle <= Math.PI / 2d) { + return 0; + } else { + return Math.PI; + } + } else { + if (angle > Math.PI * 3d / 2d) { + return 2d * Math.PI; + } else { + return Math.PI; + } + } + } +} diff --git a/src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java b/src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java new file mode 100644 index 000000000..92eb334e0 --- /dev/null +++ b/src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java @@ -0,0 +1,37 @@ +/* + * 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.io.IOException; + +public interface BezierCurvePainter { + /** + * Draw a cubic bezier from current position to (p3x, p3y) using the control points + * (p1x, p1y) and (p2x, p2y) + * @param p1x x coordinate of the first control point + * @param p1y y coordinate of the first control point + * @param p2x x coordinate of the second control point + * @param p2y y coordinate of the second control point + * @param p3x x coordinate of the end point + * @param p3y y coordinate of the end point + * @throws IOException if an I/O error occurs + */ + void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) throws IOException; +}
\ No newline at end of file diff --git a/src/java/org/apache/fop/render/intermediate/BorderPainter.java b/src/java/org/apache/fop/render/intermediate/BorderPainter.java index 4104f924c..51dcf5905 100644 --- a/src/java/org/apache/fop/render/intermediate/BorderPainter.java +++ b/src/java/org/apache/fop/render/intermediate/BorderPainter.java @@ -20,17 +20,15 @@ package org.apache.fop.render.intermediate; import java.awt.Color; -import java.awt.Point; import java.awt.Rectangle; import java.io.IOException; import org.apache.fop.traits.BorderProps; -import org.apache.fop.traits.RuleStyle; /** * This is an abstract base class for handling border painting. */ -public abstract class BorderPainter { +public class BorderPainter { /** TODO remove before integration*/ public static final String ROUNDED_CORNERS = "fop.round-corners"; @@ -42,6 +40,11 @@ public abstract class BorderPainter { 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; + private final GraphicsPainter graphicsPainter; + + public BorderPainter(GraphicsPainter graphicsPainter) { + this.graphicsPainter = graphicsPainter; + } /** * Draws borders. @@ -65,7 +68,7 @@ public abstract class BorderPainter { } private BorderProps sanitizeBorderProps(BorderProps bps) { - return bps == null ? bps : bps.width == 0 ? (BorderProps)null : bps; + return bps == null ? bps : bps.width == 0 ? (BorderProps) null : bps; } /** @@ -134,11 +137,11 @@ public abstract class BorderPainter { int sx1a = sx1; int ex1a = ex1; - if (bpsTop.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsLeft != null && bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsTop)) { + if (isCollapseOuter(bpsLeft)) { sx1a -= clipw[LEFT]; } - if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsRight)) { ex1a += clipw[RIGHT]; } lineTo(sx1a, outery); @@ -165,11 +168,12 @@ public abstract class BorderPainter { moveTo(clipx, sy1); int sy1a = sy1; int ey1a = ey1; - if (bpsRight.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsTop != null && bpsTop.mode == BorderProps.COLLAPSE_OUTER) { + + if (isCollapseOuter(bpsRight)) { + if (isCollapseOuter(bpsTop)) { sy1a -= clipw[TOP]; } - if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsBottom)) { ey1a += clipw[BOTTOM]; } lineTo(outerx, sy1a); @@ -196,11 +200,11 @@ public abstract class BorderPainter { moveTo(ex1, clipy); int sx1a = sx1; int ex1a = ex1; - if (bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsLeft != null && bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsBottom)) { + if (isCollapseOuter(bpsLeft)) { sx1a -= clipw[LEFT]; } - if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsRight)) { ex1a += clipw[RIGHT]; } lineTo(ex1a, outery); @@ -230,11 +234,11 @@ public abstract class BorderPainter { int sy1a = sy1; int ey1a = ey1; - if (bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsTop != null && bpsTop.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsLeft)) { + if (isCollapseOuter(bpsTop)) { sy1a -= clipw[TOP]; } - if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsBottom)) { ey1a += clipw[BOTTOM]; } lineTo(outerx, ey1a); @@ -250,6 +254,10 @@ public abstract class BorderPainter { } } + private boolean isCollapseOuter(BorderProps bp) { + return bp != null && bp.isCollapseOuter(); + } + /** TODO merge with drawRectangularBorders? * @param borderRect the border rectangle * @param bpsBefore the border specification on the before side @@ -259,266 +267,274 @@ public abstract class BorderPainter { * @throws IOException on io exception * */ protected void drawRoundedBorders(Rectangle borderRect, - BorderProps bpsBefore, BorderProps bpsAfter, - BorderProps bpsStart, BorderProps bpsEnd) throws IOException { - - bpsBefore = sanitizeBorderProps(bpsBefore); - bpsAfter = sanitizeBorderProps(bpsAfter); - bpsStart = sanitizeBorderProps(bpsStart); - bpsEnd = sanitizeBorderProps(bpsEnd); - - boolean[] b = new boolean[] { - (bpsBefore != null), (bpsEnd != null), - (bpsAfter != null), (bpsStart != null)}; - if (!b[TOP] && !b[RIGHT] && !b[BOTTOM] && !b[LEFT]) { + BorderProps beforeBorderProps, BorderProps afterBorderProps, + BorderProps startBorderProps, BorderProps endBorderProps) throws IOException { + BorderSegment before = borderSegmentForBefore(beforeBorderProps); + BorderSegment after = borderSegmentForAfter(afterBorderProps); + BorderSegment start = borderSegmentForStart(startBorderProps); + BorderSegment end = borderSegmentForEnd(endBorderProps); + if (before.getWidth() == 0 && after.getWidth() == 0 && start.getWidth() == 0 && end.getWidth() == 0) { return; } - int[] bw = new int[] { - (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), - BorderProps.getClippedWidth(bpsEnd), - BorderProps.getClippedWidth(bpsAfter), - BorderProps.getClippedWidth(bpsStart)}; - - 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[LEFT] && b[TOP]), (b[TOP] && b[RIGHT]), - (b[RIGHT] && b[BOTTOM]), (b[LEFT] && b[BOTTOM])}; + final int startx = borderRect.x + start.getClippedWidth(); + final int starty = borderRect.y + before.getClippedWidth(); + final int width = borderRect.width - start.getClippedWidth() - end.getClippedWidth(); + final int height = borderRect.height - before.getClippedWidth() - after.getClippedWidth(); //Determine scale factor if any adjacent elliptic corners overlap - double esf = cornerScaleFactor(width, height, bpsBefore, bpsAfter, bpsStart, bpsEnd); + double cornerCorrectionFactor = calculateCornerScaleCorrection(width, height, before, after, start, end); + drawBorderSegment(start, before, end, 0, width, startx, starty, cornerCorrectionFactor); + drawBorderSegment(before, end, after, 1, height, startx + width, starty, cornerCorrectionFactor); + drawBorderSegment(end, after, start, 2, width, startx + width, starty + height, cornerCorrectionFactor); + drawBorderSegment(after, start, before, 3, height, startx, starty + height, cornerCorrectionFactor); + } - if (bpsBefore != null) { + private void drawBorderSegment(BorderSegment start, BorderSegment before, BorderSegment end, + int orientation, int width, int x, int y, double cornerCorrectionFactor) throws IOException { + if (before.getWidth() != 0) { //Let x increase in the START->END direction - final int sx2 = (slant[TOP_LEFT] ? bw[LEFT] - clipw[LEFT] : 0); + final int sx2 = start.getWidth() - start.getClippedWidth(); final int ex1 = width; - 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; - final int ellipseSBY = clipy + ellipseSBH; - final int ellipseBEW = bpsEnd == null ? 0 : (int)(esf * bpsEnd.getRadiusStart()); - final int ellipseBEH = (int)(esf * bpsBefore.getRadiusEnd()); - final int ellipseBEX = ex1 - ellipseBEW; - final int ellipseBEY = clipy + ellipseBEH; - + final int ex2 = ex1 - end.getWidth() + end.getClippedWidth(); + final int outery = -before.getClippedWidth(); + final int innery = outery + before.getWidth(); + final int ellipseSBRadiusX = (int) (cornerCorrectionFactor * start.getRadiusEnd()); + final int ellipseSBRadiusY = (int) (cornerCorrectionFactor * before.getRadiusStart()); + final int ellipseBERadiusX = (int) (cornerCorrectionFactor * end.getRadiusStart()); + final int ellipseBERadiusY = (int) (cornerCorrectionFactor * before.getRadiusEnd()); saveGraphicsState(); - translateCoordinates(startx, starty); - drawBorderSegment( sx2, ex1, ex2, outery, innery, - clipw[LEFT], clipw[RIGHT], - ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH, - ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH, - bpsBefore, bpsStart, bpsEnd - ); + translateCoordinates(x, y); + if (orientation != 0) { + rotateCoordinates(Math.PI * orientation / 2d); + } + final int ellipseSBX = ellipseSBRadiusX; + final int ellipseSBY = ellipseSBRadiusY; + final int ellipseBEX = ex1 - ellipseBERadiusX; + final int ellipseBEY = ellipseBERadiusY; + int sx1a = 0; + int ex1a = ex1; + if (ellipseSBRadiusX != 0 && ellipseSBRadiusY != 0) { + final double[] joinMetrics = getCornerBorderJoinMetrics(ellipseSBRadiusX, + ellipseSBRadiusY, sx2, innery); + final double outerJoinPointX = joinMetrics[0]; + final double outerJoinPointY = joinMetrics[1]; + final double sbJoinAngle = joinMetrics[2]; + moveTo((int) outerJoinPointX, (int) outerJoinPointY); + arcTo(Math.PI + sbJoinAngle, Math.PI * 3 / 2, + ellipseSBX, ellipseSBY, ellipseSBRadiusX, ellipseSBRadiusY); + } else { + moveTo(0, 0); + if (before.isCollapseOuter()) { + if (start.isCollapseOuter()) { + sx1a -= start.getClippedWidth(); + } + if (end.isCollapseOuter()) { + ex1a += end.getClippedWidth(); + } + lineTo(sx1a, outery); + lineTo(ex1a, outery); + } + } + if (ellipseBERadiusX != 0 && ellipseBERadiusY != 0) { + final double[] outerJoinMetrics = getCornerBorderJoinMetrics( + ellipseBERadiusX, ellipseBERadiusY, ex1 - ex2, innery); + final double beJoinAngle = ex1 == ex2 ? Math.PI / 2 : Math.PI / 2 - outerJoinMetrics[2]; + lineTo(ellipseBEX, 0); + arcTo(Math.PI * 3 / 2 , Math.PI * 3 / 2 + beJoinAngle, + ellipseBEX, ellipseBEY, ellipseBERadiusX, ellipseBERadiusY); + if (ellipseBEX < ex2 && ellipseBEY > innery) { + final double[] innerJoinMetrics = getCornerBorderJoinMetrics( + (double) ex2 - ellipseBEX, (double) ellipseBEY - innery, ex1 - ex2, innery); + final double innerJoinPointX = innerJoinMetrics[0]; + final double innerJoinPointY = innerJoinMetrics[1]; + final double beInnerJoinAngle = Math.PI / 2 - innerJoinMetrics[2]; + lineTo((int) (ex2 - innerJoinPointX), (int) (innerJoinPointY + innery)); + arcTo(beInnerJoinAngle + Math.PI * 3 / 2, Math.PI * 3 / 2, + ellipseBEX, ellipseBEY, ex2 - ellipseBEX, ellipseBEY - innery); + } else { + lineTo(ex2, innery); + } + } else { + lineTo(ex1, 0); + lineTo(ex2, innery); + } + if (ellipseSBRadiusX == 0) { + lineTo(sx2, innery); + } else { + if (ellipseSBX > sx2 && ellipseSBY > innery) { + final double[] innerJoinMetrics = getCornerBorderJoinMetrics(ellipseSBRadiusX - sx2, + ellipseSBRadiusY - innery, sx2, innery); + final double sbInnerJoinAngle = innerJoinMetrics[2]; + lineTo(ellipseSBX, innery); + arcTo(Math.PI * 3 / 2, sbInnerJoinAngle + Math.PI, + ellipseSBX, ellipseSBY, ellipseSBX - sx2, ellipseSBY - innery); + } else { + lineTo(sx2, innery); + } + } + closePath(); + clip(); + if (ellipseBERadiusY == 0 && ellipseSBRadiusY == 0) { + drawBorderLine(sx1a, outery, ex1a, innery, true, true, + before.getStyle(), before.getColor()); + } else { + int innerFillY = Math.max(Math.max(ellipseBEY, ellipseSBY), innery); + drawBorderLine(sx1a, outery, ex1a, innerFillY, true, true, + before.getStyle(), before.getColor()); + } restoreGraphicsState(); } + } - if (bpsStart != null) { - //Let x increase in the AFTER->BEFORE direction - final int sx2 = (slant[BOTTOM_LEFT] ? bw[BOTTOM] - clipw[BOTTOM] : 0); - final int ex1 = height; - 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; - final int ellipseSBY = clipy + ellipseSBH; - final int ellipseBEW = bpsBefore == null ? 0 : (int)(esf * bpsBefore.getRadiusStart()); - final int ellipseBEH = (int)(esf * bpsStart.getRadiusStart()); - final int ellipseBEX = ex1 - ellipseBEW; - final int ellipseBEY = clipy + ellipseBEH; + private static BorderSegment borderSegmentForBefore(BorderProps before) { + return AbstractBorderSegment.asBorderSegment(before); + } - saveGraphicsState(); - translateCoordinates(startx, starty + height); - rotateCoordinates(Math.PI * 3d / 2d); - drawBorderSegment( sx2, ex1, ex2, outery, innery, - clipw[BOTTOM], clipw[TOP], - ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH, - ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH, - bpsStart, bpsAfter, bpsBefore - ); - restoreGraphicsState(); + private static BorderSegment borderSegmentForAfter(BorderProps after) { + return AbstractBorderSegment.asFlippedBorderSegment(after); + } - } + private static BorderSegment borderSegmentForStart(BorderProps start) { + return AbstractBorderSegment.asFlippedBorderSegment(start); + } + private static BorderSegment borderSegmentForEnd(BorderProps end) { + return AbstractBorderSegment.asBorderSegment(end); + } - if (bpsAfter != null) { - //Let x increase in the START->END direction - final int sx2 = (slant[BOTTOM_LEFT] ? bw[LEFT] - clipw[LEFT] : 0); - final int ex1 = width; - 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; - final int ellipseSBY = clipy + ellipseSBH; - final int ellipseBEW = bpsEnd == null ? 0 : (int)(esf * bpsEnd.getRadiusEnd()); - final int ellipseBEH = (int)(esf * bpsAfter.getRadiusEnd()); - final int ellipseBEX = ex1 - ellipseBEW; - final int ellipseBEY = clipy + ellipseBEH; + private interface BorderSegment { - saveGraphicsState(); - translateCoordinates(startx, starty + height); - scaleCoordinates(1, -1); - drawBorderSegment( sx2, ex1, ex2, outery, innery, - clipw[LEFT], clipw[RIGHT], - ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH, - ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH, - bpsAfter, bpsStart, bpsEnd - ); - restoreGraphicsState(); - } + Color getColor(); - if (bpsEnd != null) { - //Let x increase in the BEFORE-> AFTER direction - final int sx2 = (slant[TOP_RIGHT] ? bw[TOP] - clipw[TOP] : 0); - final int ex1 = height; - 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; - final int ellipseSBY = clipy + ellipseSBH; - final int ellipseBEW = bpsAfter == null ? 0 : (int)(esf * bpsAfter.getRadiusEnd()); - final int ellipseBEH = (int)(esf * bpsEnd.getRadiusEnd()); - final int ellipseBEX = ex1 - ellipseBEW; - final int ellipseBEY = clipy + ellipseBEH; + int getStyle(); - saveGraphicsState(); - translateCoordinates(startx + width, starty); - rotateCoordinates(Math.PI / 2d); - drawBorderSegment( sx2, ex1, ex2, outery, innery, - clipw[TOP], clipw[BOTTOM], - ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH, - ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH, - bpsEnd, bpsBefore, bpsAfter - ); - restoreGraphicsState(); - } + int getWidth(); + + int getClippedWidth(); + + int getRadiusStart(); + + int getRadiusEnd(); + + boolean isCollapseOuter(); + + boolean isSpecified(); } - /** TODO collect parameters into useful data structures*/ - private void drawBorderSegment(final int sx2, final int ex1, final int ex2, - final int outery, final int innery, - final int clipWidthStart, final int clipWidthEnd, - final int ellipseSBX, final int ellipseSBY, - final int ellipseSBRadiusX, final int ellipseSBRadiusY, - final int ellipseBEX, final int ellipseBEY, - final int ellipseBERadiusX, final int ellipseBERadiusY, - final BorderProps bpsThis, final BorderProps bpsStart, final BorderProps bpsEnd ) - throws IOException { + private abstract static class AbstractBorderSegment implements BorderSegment { - int sx1a = 0; - int ex1a = ex1; + private static BorderSegment asBorderSegment(BorderProps borderProps) { + return borderProps == null ? NullBorderSegment.INSTANCE : new WrappingBorderSegment(borderProps); + } + private static BorderSegment asFlippedBorderSegment(BorderProps borderProps) { + return borderProps == null ? NullBorderSegment.INSTANCE : new FlippedBorderSegment(borderProps); + } - if (ellipseSBRadiusX != 0 && ellipseSBRadiusY != 0 ) { + public boolean isSpecified() { + return !(this instanceof NullBorderSegment); + } - final double[] joinMetrics = getCornerBorderJoinMetrics(ellipseSBRadiusX, - ellipseSBRadiusY, (double)innery / sx2); + private static class WrappingBorderSegment extends AbstractBorderSegment { - final double outerJoinPointX = joinMetrics[0]; - final double outerJoinPointY = joinMetrics[1]; - final double sbJoinAngle = joinMetrics[2]; + protected final BorderProps borderProps; - moveTo((int)outerJoinPointX, (int)outerJoinPointY); - arcTo(Math.PI + sbJoinAngle, Math.PI * 3 / 2, - ellipseSBX, ellipseSBY, ellipseSBRadiusX, ellipseSBRadiusY); - } else { + private final int clippedWidth; - moveTo(0, 0); + WrappingBorderSegment(BorderProps borderProps) { + this.borderProps = borderProps; + clippedWidth = BorderProps.getClippedWidth(borderProps); + } - if (bpsThis.mode == BorderProps.COLLAPSE_OUTER) { + public int getStyle() { + return borderProps.style; + } - if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) { - sx1a -= clipWidthStart; - } - if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { - ex1a += clipWidthEnd; - } + public Color getColor() { + return borderProps.color; + } - lineTo(sx1a, outery); - lineTo(ex1a, outery); + public int getWidth() { + return borderProps.width; } - } - if (ellipseBERadiusX != 0 && ellipseBERadiusY != 0) { + public int getClippedWidth() { + return clippedWidth; + } + public boolean isCollapseOuter() { + return borderProps.isCollapseOuter(); + } - final double[] outerJoinMetrics = getCornerBorderJoinMetrics( - ellipseBERadiusX, ellipseBERadiusY, (double)innery / (ex1 - ex2)); - final double beJoinAngle = Math.PI / 2 - outerJoinMetrics[2]; + public int getRadiusStart() { + return borderProps.getRadiusStart(); + } - lineTo(ellipseBEX, 0); - arcTo( Math.PI * 3 / 2 , Math.PI * 3 / 2 + beJoinAngle, - ellipseBEX, ellipseBEY, ellipseBERadiusX, ellipseBERadiusY); + public int getRadiusEnd() { + return borderProps.getRadiusEnd(); + } + } - if (ellipseBEX < ex2 && ellipseBEY > innery) { + private static class FlippedBorderSegment extends WrappingBorderSegment { - final double[] innerJoinMetrics = getCornerBorderJoinMetrics( - (double)ex2 - ellipseBEX, (double)ellipseBEY - innery, - (double)innery / (ex1 - ex2)); - final double innerJoinPointX = innerJoinMetrics[0]; - final double innerJoinPointY = innerJoinMetrics[1]; - final double beInnerJoinAngle = Math.PI / 2 - innerJoinMetrics[2]; + FlippedBorderSegment(BorderProps borderProps) { + super(borderProps); + } - lineTo((int) (ex2 - innerJoinPointX), (int)(innerJoinPointY + innery)); - arcTo(beInnerJoinAngle + Math.PI * 3 / 2, Math.PI * 3 / 2, - ellipseBEX, ellipseBEY, ex2 - ellipseBEX, ellipseBEY - innery); - } else { - lineTo(ex2, innery); + public int getRadiusStart() { + return borderProps.getRadiusEnd(); } - } else { - lineTo(ex1, 0); - lineTo(ex2, innery); + public int getRadiusEnd() { + return borderProps.getRadiusStart(); + } } - if (ellipseSBRadiusX == 0) { - lineTo(sx2, innery); - } else { - if (ellipseSBX > sx2 && ellipseSBY > innery) { + private static final class NullBorderSegment extends AbstractBorderSegment { + public static final NullBorderSegment INSTANCE = new NullBorderSegment(); - final double[] innerJoinMetrics = getCornerBorderJoinMetrics(ellipseSBRadiusX - sx2, - ellipseSBRadiusY - innery, (double)innery / sx2); + private NullBorderSegment() { + } - final double sbInnerJoinAngle = innerJoinMetrics[2]; + public int getWidth() { + return 0; + } - lineTo(ellipseSBX, innery); - arcTo(Math.PI * 3 / 2, sbInnerJoinAngle + Math.PI, - ellipseSBX, ellipseSBY, ellipseSBX - sx2, ellipseSBY - innery); - } else { - lineTo(sx2, innery); + public int getClippedWidth() { + return 0; } - } - closePath(); - clip(); + public int getRadiusStart() { + return 0; + } - if (ellipseBERadiusY == 0 && ellipseSBRadiusY == 0) { - drawBorderLine(sx1a, outery, ex1a, innery, true, true, - bpsThis.style, bpsThis.color); + public int getRadiusEnd() { + return 0; + } + + public boolean isCollapseOuter() { + return false; + } + + public Color getColor() { + throw new UnsupportedOperationException(); + } + + public int getStyle() { + throw new UnsupportedOperationException(); + } + + public boolean isSpecified() { + return false; + } + } + } + private double[] getCornerBorderJoinMetrics(double ellipseCenterX, double ellipseCenterY, double xWidth, + double yWidth) { + if (xWidth > 0) { + return getCornerBorderJoinMetrics(ellipseCenterX, ellipseCenterY, yWidth / xWidth); } else { - int innerFillY = Math.max(Math.max(ellipseBEY, ellipseSBY), innery); - drawBorderLine(sx1a, outery, ex1a, innerFillY, true, true, - bpsThis.style, bpsThis.color); + return new double[]{0, ellipseCenterY, 0}; } } @@ -527,8 +543,8 @@ public abstract class BorderPainter { double x = ellipseCenterY * ellipseCenterX * ( ellipseCenterY + ellipseCenterX * borderWidthRatio - Math.sqrt(2d * ellipseCenterX * ellipseCenterY * borderWidthRatio) - ) / (ellipseCenterY * ellipseCenterY - + ellipseCenterX * ellipseCenterX * borderWidthRatio * borderWidthRatio); + ) / (ellipseCenterY * ellipseCenterY + + ellipseCenterX * ellipseCenterX * borderWidthRatio * borderWidthRatio); double y = borderWidthRatio * x; return new double[]{x, y, Math.atan((ellipseCenterY - y) / (ellipseCenterX - x))}; } @@ -545,331 +561,335 @@ public abstract class BorderPainter { public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) throws IOException { + BorderSegment before = borderSegmentForBefore(bpsBefore); + BorderSegment after = borderSegmentForAfter(bpsAfter); + BorderSegment start = borderSegmentForStart(bpsStart); + BorderSegment end = borderSegmentForEnd(bpsEnd); int startx = rect.x; int starty = rect.y; int width = rect.width; int height = rect.height; + double correctionFactor = calculateCornerCorrectionFactor(width + start.getWidth() + end.getWidth(), + height + before.getWidth() + after.getWidth(), bpsBefore, bpsAfter, bpsStart, bpsEnd); + Corner cornerBeforeEnd = Corner.createBeforeEndCorner(before, end, correctionFactor); + Corner cornerEndAfter = Corner.createEndAfterCorner(end, after, correctionFactor); + Corner cornerAfterStart = Corner.createAfterStartCorner(after, start, correctionFactor); + Corner cornerStartBefore = Corner.createStartBeforeCorner(start, before, correctionFactor); + new PathPainter(startx + cornerStartBefore.radiusX, starty) + .lineHorizTo(width - cornerStartBefore.radiusX - cornerBeforeEnd.radiusX) + .drawCorner(cornerBeforeEnd) + .lineVertTo(height - cornerBeforeEnd.radiusY - cornerEndAfter.radiusY) + .drawCorner(cornerEndAfter) + .lineHorizTo(cornerEndAfter.radiusX + cornerAfterStart.radiusX - width) + .drawCorner(cornerAfterStart) + .lineVertTo(cornerAfterStart.radiusY + cornerStartBefore.radiusY - height) + .drawCorner(cornerStartBefore); + clip(); + } - int fullWidth = width + ( bpsStart == null ? 0 : bpsStart.width ) - + (bpsStart == null ? 0 : bpsStart.width); - int fullHeight = height + ( bpsBefore == null ? 0 : bpsBefore.width ) - + (bpsAfter == null ? 0 : bpsAfter.width); - - double esf = cornerScaleFactor( fullWidth, fullHeight, bpsBefore, bpsAfter, - bpsStart, bpsEnd); - - int ellipseSS = 0; - int ellipseBS = 0; - int ellipseBE = 0; - int ellipseES = 0; - int ellipseEE = 0; - int ellipseAE = 0; - int ellipseAS = 0; - int ellipseSE = 0; - if (bpsBefore != null && bpsBefore.getRadiusStart() > 0 - && bpsStart != null && bpsStart.getRadiusStart() > 0) { - ellipseSS = Math.max((int)(bpsStart.getRadiusStart() * esf) - bpsStart.width, 0); - ellipseBS = Math.max((int)(bpsBefore.getRadiusStart() * esf) - bpsBefore.width, 0); + /** + * The four corners + * SB - Start-Before + * BE - Before-End + * EA - End-After + * AS - After-Start + * + * 0 --> x + * | + * v + * y + * + * SB BE + * *----* + * | | + * | | + * *----* + * AS EA + * + */ + private enum CornerAngles { + /** The before-end angles */ + BEFORE_END(Math.PI * 3 / 2, 0), + /** The end-after angles */ + END_AFTER(0, Math.PI / 2), + /** The after-start angles*/ + AFTER_START(Math.PI / 2, Math.PI), + /** The start-before angles */ + START_BEFORE(Math.PI, Math.PI * 3 / 2); + + /** Angle of the start of the corner arch relative to the x-axis in the counter-clockwise direction */ + private final double start; + + /** Angle of the end of the corner arch relative to the x-axis in the counter-clockwise direction */ + private final double end; + + CornerAngles(double start, double end) { + this.start = start; + this.end = end; } - if (bpsBefore != null && bpsBefore.getRadiusEnd() > 0 - && bpsEnd != null && bpsEnd.getRadiusStart() > 0) { - ellipseBE = Math.max((int)(bpsBefore.getRadiusEnd() * esf) - bpsBefore.width, 0); - ellipseES = Math.max((int)(bpsEnd.getRadiusStart() * esf) - bpsEnd.width, 0); - } + } - if (bpsEnd != null && bpsEnd.getRadiusEnd() > 0 - && bpsAfter != null && bpsAfter.getRadiusEnd() > 0) { - ellipseEE = Math.max((int)(bpsEnd.getRadiusEnd() * esf) - bpsEnd.width, 0); - ellipseAE = Math.max((int)(bpsAfter.getRadiusEnd() * esf) - bpsAfter.width, 0); - } + private static final class Corner { - if (bpsAfter != null && bpsAfter.getRadiusStart() > 0 - && bpsStart != null && bpsStart.getRadiusEnd() > 0) { - ellipseAS = Math.max((int)(bpsAfter.getRadiusStart() * esf) - bpsAfter.width, 0); - ellipseSE = Math.max((int)(bpsStart.getRadiusEnd() * esf) - bpsStart.width, 0); - } + private static final Corner SQUARE = new Corner(0, 0, null, 0, 0, 0, 0); - // Draw clipping region in the order: Before->End->After->Start - moveTo(startx + ellipseSS, starty); + /** The radius of the elliptic corner in the x direction */ + protected final int radiusX; - lineTo(startx + width - ellipseES, starty); + /** The radius of the elliptic corner in the y direction */ + protected final int radiusY; - if (ellipseBE > 0 && ellipseES > 0) { - arcTo(Math.PI * 3 / 2, Math.PI * 2, - startx + width - ellipseES, starty + ellipseBE, ellipseES, ellipseBE); - } + /** The start and end angles of the corner ellipse */ + private final CornerAngles angles; - lineTo(startx + width, starty + height - ellipseAE); + /** The offset in the x direction of the center of the ellipse relative to the starting point */ + private final int centerX; - if (ellipseEE > 0 && ellipseAE > 0) { - arcTo(0, Math.PI / 2, startx + width - ellipseEE, - starty + height - ellipseAE, ellipseEE, ellipseAE); - } + /** The offset in the y direction of the center of the ellipse relative to the starting point */ + private final int centerY; - lineTo(startx + ellipseSE, starty + height); + /** The value in the x direction that the corner extends relative to the starting point */ + private final int incrementX; - if (ellipseSE > 0 && ellipseAS > 0) { - arcTo( Math.PI / 2, Math.PI, startx + ellipseSE, - starty + height - ellipseAS, ellipseSE, ellipseAS); - } + /** The value in the y direction that the corner extends relative to the starting point */ + private final int incrementY; - lineTo( startx, starty + ellipseBS); + private Corner(int radiusX, int radiusY, CornerAngles angles, int ellipseOffsetX, + int ellipseOffsetY, int incrementX, int incrementY) { + this.radiusX = radiusX; + this.radiusY = radiusY; + this.angles = angles; + this.centerX = ellipseOffsetX; + this.centerY = ellipseOffsetY; + this.incrementX = incrementX; + this.incrementY = incrementY; + } - if (ellipseSS > 0 && ellipseBS > 0) { - arcTo( Math.PI, Math.PI * 3 / 2, - startx + ellipseSS, starty + ellipseBS, ellipseSS, ellipseBS); + private static int extentFromRadiusStart(BorderSegment border, double correctionFactor) { + return extentFromRadius(border.getRadiusStart(), border, correctionFactor); } - clip(); + private static int extentFromRadiusEnd(BorderSegment border, double correctionFactor) { + return extentFromRadius(border.getRadiusEnd(), border, correctionFactor); + } - } + private static int extentFromRadius(int radius, BorderSegment border, double correctionFactor) { + return Math.max((int) (radius * correctionFactor) - border.getWidth(), 0); + } - /** - * TODO javadocs - * If an ellipse radii exceed the border edge length then all ellipses must be rescaled. - */ - protected double cornerScaleFactor(int width, int height, - BorderProps bpsBefore, BorderProps bpsAfter, - BorderProps bpsStart, BorderProps bpsEnd) { - // Ellipse scale factor - double esf = 1d; - - if (bpsBefore != null) { - double ellipseExtent = (bpsStart == null ? 0 : bpsStart.getRadiusStart()) - + (bpsEnd == null ? 0 : bpsEnd.getRadiusStart()); - - if (ellipseExtent > 0) { - double f = width / ellipseExtent; - if (f < esf) { - esf = f; - } + public static Corner createBeforeEndCorner(BorderSegment before, BorderSegment end, + double correctionFactor) { + int width = end.getRadiusStart(); + int height = before.getRadiusEnd(); + if (width == 0 || height == 0) { + return SQUARE; } + int x = extentFromRadiusStart(end, correctionFactor); + int y = extentFromRadiusEnd(before, correctionFactor); + return new Corner(x, y, CornerAngles.BEFORE_END, 0, y, x, y); } - if (bpsStart != null) { - double ellipseExtent = (bpsAfter == null ? 0 : bpsAfter.getRadiusStart()) - + (bpsBefore == null ? 0 : bpsBefore.getRadiusStart()); - - if (ellipseExtent > 0) { - double f = height / ellipseExtent; - if ( f < esf) { - esf = f; - } + public static Corner createEndAfterCorner(BorderSegment end, BorderSegment after, + double correctionFactor) { + int width = end.getRadiusEnd(); + int height = after.getRadiusStart(); + if (width == 0 || height == 0) { + return SQUARE; } + int x = extentFromRadiusEnd(end, correctionFactor); + int y = extentFromRadiusStart(after, correctionFactor); + return new Corner(x, y, CornerAngles.END_AFTER, -x, 0, -x, y); } - if (bpsAfter != null) { - double ellipseExtent = (bpsStart == null ? 0 : bpsStart.getRadiusEnd()) - + (bpsEnd == null ? 0 : bpsEnd.getRadiusEnd()); - - if (ellipseExtent > 0) { - double f = width / ellipseExtent; - if (f < esf) { - esf = f; - } + public static Corner createAfterStartCorner(BorderSegment after, BorderSegment start, + double correctionFactor) { + int width = start.getRadiusStart(); + int height = after.getRadiusEnd(); + if (width == 0 || height == 0) { + return SQUARE; } + int x = extentFromRadiusStart(start, correctionFactor); + int y = extentFromRadiusEnd(after, correctionFactor); + return new Corner(x, y, CornerAngles.AFTER_START, 0, -y, -x, -y); } - if (bpsEnd != null) { - double ellipseExtent = (bpsAfter == null ? 0 : bpsAfter.getRadiusEnd()) - + (bpsBefore == null ? 0 : bpsBefore.getRadiusEnd()); - - if (ellipseExtent > 0) { - double f = height / ellipseExtent; - if (f < esf) { - esf = f; - } + public static Corner createStartBeforeCorner(BorderSegment start, BorderSegment before, + double correctionFactor) { + int width = start.getRadiusEnd(); + int height = before.getRadiusStart(); + if (width == 0 || height == 0) { + return SQUARE; } + int x = extentFromRadiusEnd(start, correctionFactor); + int y = extentFromRadiusStart(before, correctionFactor); + return new Corner(x, y, CornerAngles.START_BEFORE, x, 0, x, -y); } - - return esf; } /** - * Draws a border line. - * @param x1 X coordinate of the upper left corner - * of the line's bounding rectangle (in millipoints) - * @param y1 start Y coordinate of the upper left corner - * of the line's bounding rectangle (in millipoints) - * @param x2 end X coordinate of the lower right corner - * of the line's bounding rectangle (in millipoints) - * @param y2 end y coordinate of the lower right corner - * of the line's bounding rectangle (in millipoints) - * @param horz true if it is a horizontal line - * @param startOrBefore true if the line is the start or end edge of a border box - * @param style the border style - * @param color the border color - * @throws IOException if an I/O error occurs + * This is a helper class for constructing curves composed of move, line and arc operations. Coordinates + * are relative to the terminal point of the previous operation */ - protected abstract void drawBorderLine( // CSOK: ParameterNumber - int x1, int y1, int x2, int y2, - boolean horz, boolean startOrBefore, int style, Color color) throws IOException; + private final class PathPainter { - /** - * Draws a line/rule. - * @param start start point (coordinates in millipoints) - * @param end end point (coordinates in millipoints) - * @param width width of the line - * @param color the line color - * @param style the rule style - * @throws IOException if an I/O error occurs - */ - public abstract void drawLine(Point start, Point end, - int width, Color color, RuleStyle style) throws IOException; + /** Current x position */ + private int x; - /** - * Moves the cursor to the given coordinate. - * @param x the X coordinate (in millipoints) - * @param y the Y coordinate (in millipoints) - * @throws IOException if an I/O error occurs - */ - protected abstract void moveTo(int x, int y) throws IOException; + /** Current y position */ + private int y; - /** - * Draws a line from the current cursor position to the given coordinates. - * @param x the X coordinate (in millipoints) - * @param y the Y coordinate (in millipoints) - * @throws IOException if an I/O error occurs - */ - protected abstract void lineTo(int x, int y) throws IOException; + PathPainter(int x, int y) throws IOException { + moveTo(x, y); + } - /** - * Draw a cubic bezier from current position to (p3x, p3y) using the control points - * (p1x, p1y) and (p2x, p2y) - * @param p1x x coordinate of the first control point - * @param p1y y coordinate of the first control point - * @param p2x x coordinate of the second control point - * @param p2y y coordinate of the second control point - * @param p3x x coordinate of the end point - * @param p3y y coordinate of the end point - * @throws IOException if an I/O error occurs - */ - protected abstract void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) - throws IOException; + private void moveTo(int x, int y) throws IOException { + this.x += x; + this.y += y; + BorderPainter.this.moveTo(this.x, this.y); + } - /** - * Draws an arc on the ellipse centered at (cx, cy) with width width and height height - * from start angle startAngle (with respect to the x-axis counter-clockwise) - * to the end angle endAngle. - * The ellipses major axis are assumed to coincide with the coordinate axis. - * The current position MUST coincide with the starting position on the ellipse. - * @param startAngle the start angle - * @param endAngle the end angle - * @param cx the x coordinate of the ellipse center - * @param cy the y coordinate of the ellipse center - * @param width the extent of the ellipse in the x direction - * @param height the extent of the ellipse in the y direction - * @throws IOException if an I/O error occurs - */ - protected void arcTo(final double startAngle, final double endAngle, final int cx, final int cy, - final int width, final int height) - throws IOException { + public PathPainter lineTo(int x, int y) throws IOException { + this.x += x; + this.y += y; + BorderPainter.this.lineTo(this.x, this.y); + return this; + } - // Implementation follows http://www.spaceroots.org/documents/ellipse/ - - // Drawing an elliptical arc using polylines, quadratic or cubic Bézier curves - // L. Maisonobe, July 21, 2003 + public PathPainter lineHorizTo(int x) throws IOException { + return lineTo(x, 0); + } - // Scaling the coordinate system to represent the ellipse as a circle: - final double etaStart = Math.atan(Math.tan(startAngle) * width / height) - + quadrant(startAngle); - final double etaEnd = Math.atan(Math.tan(endAngle) * width / height) - + quadrant(endAngle); + public PathPainter lineVertTo(int y) throws IOException { + return lineTo(0, y); + } - final double sinStart = Math.sin(etaStart); - final double cosStart = Math.cos(etaStart); - final double sinEnd = Math.sin(etaEnd); - final double cosEnd = Math.cos(etaEnd); + PathPainter drawCorner(Corner corner) throws IOException { + if (corner.radiusX == 0 && corner.radiusY == 0) { + return this; + } + if (corner.radiusX == 0 || corner.radiusY == 0) { + x += corner.incrementX; + y += corner.incrementY; + BorderPainter.this.lineTo(x, y); + return this; + } + BorderPainter.this.arcTo(corner.angles.start, corner.angles.end, x + corner.centerX, + y + corner.centerY, corner.radiusX, corner.radiusY); + x += corner.incrementX; + y += corner.incrementY; + return this; + } + } - final double p0x = cx + cosStart * width; - final double p0y = cy + sinStart * height; - final double p3x = cx + cosEnd * width; - final double p3y = cy + sinEnd * height; + /** + * Calculate the correction factor to handle over-sized elliptic corner radii. + * + * @param width the border width + * @param height the border height + * @param before the before border properties + * @param after the after border properties + * @param start the start border properties + * @param end the end border properties + * + */ + protected static double calculateCornerCorrectionFactor(int width, int height, BorderProps before, + BorderProps after, BorderProps start, BorderProps end) { + return calculateCornerScaleCorrection(width, height, borderSegmentForBefore(before), + borderSegmentForAfter(after), borderSegmentForStart(start), borderSegmentForEnd(end)); + } + /** + * Calculate the scaling factor to handle over-sized elliptic corner radii. + * + * @param width the border width + * @param height the border height + * @param before the before border segment + * @param after the after border segment + * @param start the start border segment + * @param end the end border segment + */ + protected static double calculateCornerScaleCorrection(int width, int height, BorderSegment before, + BorderSegment after, BorderSegment start, BorderSegment end) { + return CornerScaleCorrectionCalculator.calculate(width, height, before, after, start, end); + } - double etaDiff = Math.abs(etaEnd - etaStart); - double tan = Math.tan((etaDiff) / 2d); - final double alpha = Math.sin(etaDiff) * (Math.sqrt(4d + 3d * tan * tan) - 1d) / 3d; + private static final class CornerScaleCorrectionCalculator { + private double correctionFactor = 1; - int order = etaEnd > etaStart ? 1 : -1; + private CornerScaleCorrectionCalculator(int width, int height, + BorderSegment before, BorderSegment after, + BorderSegment start, BorderSegment end) { + calculateForSegment(width, start, before, end); + calculateForSegment(height, before, end, after); + calculateForSegment(width, end, after, start); + calculateForSegment(height, after, start, before); + } - // p1 = p0 + alpha*(-sin(startAngle), cos(startAngle)) - final double p1x = p0x - alpha * sinStart * width * order; - final double p1y = p0y + alpha * cosStart * height * order; + public static double calculate(int width, int height, + BorderSegment before, BorderSegment after, + BorderSegment start, BorderSegment end) { + return new CornerScaleCorrectionCalculator(width, height, before, after, start, end) + .correctionFactor; + } - // p1 = p3 + alpha*(sin(endAngle), -cos(endAngle)) - final double p2x = p3x + alpha * sinEnd * width * order; - final double p2y = p3y - alpha * cosEnd * height * order; + private void calculateForSegment(int width, BorderSegment bpsStart, BorderSegment bpsBefore, + BorderSegment bpsEnd) { + if (bpsBefore.isSpecified()) { + double ellipseExtent = bpsStart.getRadiusEnd() + bpsEnd.getRadiusStart(); + if (ellipseExtent > 0) { + double thisCorrectionFactor = width / ellipseExtent; + if (thisCorrectionFactor < correctionFactor) { + correctionFactor = thisCorrectionFactor; + } + } + } + } + } - //Draw the curve in original coordinate system - cubicBezierTo((int)p1x, (int)p1y, (int)p2x, (int)p2y, (int)p3x, (int)p3y); + private void drawBorderLine(int x1, int y1, int x2, int y2, boolean horz, boolean startOrBefore, + int style, Color color) throws IOException { + graphicsPainter.drawBorderLine(x1, y1, x2, y2, horz, startOrBefore, style, color); } - private double quadrant(double angle) { - if (angle <= Math.PI ) { - if (angle <= Math.PI / 2d) { - return 0; - } else { - return Math.PI; - } - } else { - if (angle > Math.PI * 3d / 2d) { - return 2d * Math.PI; - } else { - return Math.PI; - } - } + private void moveTo(int x, int y) throws IOException { + graphicsPainter.moveTo(x, y); } - /** - * Rotate the coordinate frame - * @param angle angle in radians to rotate the coordinate frame - * @throws IOException if an I/O error occurs - */ - protected abstract void rotateCoordinates(double angle) throws IOException; + private void lineTo(int x, int y) throws IOException { + graphicsPainter.lineTo(x, y); + } - /** - * Translate the coordinate frame - * @param xTranslate translation in the x direction - * @param yTranslate translation in the y direction - * @throws IOException if an I/O error occurs - */ - protected abstract void translateCoordinates(int xTranslate, int yTranslate) throws IOException; + private void arcTo(final double startAngle, final double endAngle, final int cx, final int cy, + final int width, final int height) throws IOException { + graphicsPainter.arcTo(startAngle, endAngle, cx, cy, width, height); + } - /** - * Scale the coordinate frame - * @param xScale scale factor in the x direction - * @param yScale scale factor in the y direction - * @throws IOException if an I/O error occurs - */ - protected abstract void scaleCoordinates(float xScale, float yScale) throws IOException; + private void rotateCoordinates(double angle) throws IOException { + graphicsPainter.rotateCoordinates(angle); + } + private void translateCoordinates(int xTranslate, int yTranslate) throws IOException { + graphicsPainter.translateCoordinates(xTranslate, yTranslate); + } - /** - * Closes the current path. - * @throws IOException if an I/O error occurs - */ - protected abstract void closePath() throws IOException; + private void closePath() throws IOException { + graphicsPainter.closePath(); + } - /** - * Reduces the current clipping region to the current path. - * @throws IOException if an I/O error occurs - */ - protected abstract void clip() throws IOException; + private void clip() throws IOException { + graphicsPainter.clip(); + } - /** - * Save the graphics state on the stack. - * @throws IOException if an I/O error occurs - */ - protected abstract void saveGraphicsState() throws IOException; + private void saveGraphicsState() throws IOException { + graphicsPainter.saveGraphicsState(); + } - /** - * Restore the last graphics state from the stack. - * @throws IOException if an I/O error occurs - */ - protected abstract void restoreGraphicsState() throws IOException; + private void restoreGraphicsState() throws IOException { + graphicsPainter.restoreGraphicsState(); + } } diff --git a/src/java/org/apache/fop/render/intermediate/GraphicsPainter.java b/src/java/org/apache/fop/render/intermediate/GraphicsPainter.java new file mode 100644 index 000000000..369cacd43 --- /dev/null +++ b/src/java/org/apache/fop/render/intermediate/GraphicsPainter.java @@ -0,0 +1,145 @@ +/* + * 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.awt.Color; +import java.awt.Point; +import java.io.IOException; + +import org.apache.fop.traits.RuleStyle; + +/** + * Used primarily by {@link BorderPainter}, implementations are created for rendering + * primitive graphical operations. + * + */ +public interface GraphicsPainter { + + /** + * Draws a border line. + * @param x1 X coordinate of the upper left corner + * of the line's bounding rectangle (in millipoints) + * @param y1 start Y coordinate of the upper left corner + * of the line's bounding rectangle (in millipoints) + * @param x2 end X coordinate of the lower right corner + * of the line's bounding rectangle (in millipoints) + * @param y2 end y coordinate of the lower right corner + * of the line's bounding rectangle (in millipoints) + * @param horz true if it is a horizontal line + * @param startOrBefore true if the line is the start or end edge of a border box + * @param style the border style + * @param color the border color + * @throws IOException if an I/O error occurs + */ + void drawBorderLine(int x1, int y1, int x2, int y2, + boolean horz, boolean startOrBefore, int style, Color color) throws IOException; + + /** + * Draws a line/rule. + * @param start start point (coordinates in millipoints) + * @param end end point (coordinates in millipoints) + * @param width width of the line + * @param color the line color + * @param style the rule style + * @throws IOException if an I/O error occurs + */ + void drawLine(Point start, Point end, + int width, Color color, RuleStyle style) throws IOException; + + /** + * Moves the cursor to the given coordinate. + * @param x the X coordinate (in millipoints) + * @param y the Y coordinate (in millipoints) + * @throws IOException if an I/O error occurs + */ + void moveTo(int x, int y) throws IOException; + + /** + * Draws a line from the current cursor position to the given coordinates. + * @param x the X coordinate (in millipoints) + * @param y the Y coordinate (in millipoints) + * @throws IOException if an I/O error occurs + */ + void lineTo(int x, int y) throws IOException; + + /** + * Draws an arc on the ellipse centered at (cx, cy) with width width and height height + * from start angle startAngle (with respect to the x-axis counter-clockwise) + * to the end angle endAngle. + * The ellipses major axis are assumed to coincide with the coordinate axis. + * The current position MUST coincide with the starting position on the ellipse. + * @param startAngle the start angle + * @param endAngle the end angle + * @param cx the x coordinate of the ellipse center + * @param cy the y coordinate of the ellipse center + * @param width the extent of the ellipse in the x direction + * @param height the extent of the ellipse in the y direction + * @throws IOException if an I/O error occurs + */ + void arcTo(final double startAngle, final double endAngle, final int cx, final int cy, + final int width, final int height) throws IOException; + + /** + * Rotate the coordinate frame + * @param angle angle in radians to rotate the coordinate frame + * @throws IOException if an I/O error occurs + */ + void rotateCoordinates(double angle) throws IOException; + + /** + * Translate the coordinate frame + * @param xTranslate translation in the x direction + * @param yTranslate translation in the y direction + * @throws IOException if an I/O error occurs + */ + void translateCoordinates(int xTranslate, int yTranslate) throws IOException; + + /** + * Scale the coordinate frame + * @param xScale scale factor in the x direction + * @param yScale scale factor in the y direction + * @throws IOException if an I/O error occurs + */ + void scaleCoordinates(float xScale, float yScale) throws IOException; + + /** + * Closes the current path. + * @throws IOException if an I/O error occurs + */ + void closePath() throws IOException; + + /** + * Reduces the current clipping region to the current path. + * @throws IOException if an I/O error occurs + */ + void clip() throws IOException; + + /** + * Save the graphics state on the stack. + * @throws IOException if an I/O error occurs + */ + void saveGraphicsState() throws IOException; + + /** + * Restore the last graphics state from the stack. + * @throws IOException if an I/O error occurs + */ + void restoreGraphicsState() throws IOException; +} diff --git a/src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java b/src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java deleted file mode 100644 index b3ad5ff7a..000000000 --- a/src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * 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.java2d; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.geom.GeneralPath; -import java.awt.geom.Line2D; -import java.io.IOException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.apache.fop.fo.Constants; -import org.apache.fop.render.intermediate.BorderPainter; -import org.apache.fop.traits.RuleStyle; -import org.apache.fop.util.ColorUtil; - -/** - * Java2D-specific implementation of the {@link BorderPainter}. - */ -public class Java2DBorderPainter extends BorderPainter { - - /** logging instance */ - private static Log log = LogFactory.getLog(Java2DBorderPainter.class); - - private Java2DPainter painter; - - private GeneralPath currentPath = null; - - /** - * Construct a java2d border painter. - * @param painter a painter - */ - public Java2DBorderPainter(Java2DPainter painter) { - this.painter = painter; - } - - private Java2DGraphicsState getG2DState() { - return this.painter.g2dState; - } - - private Graphics2D getG2D() { - return getG2DState().getGraph(); - } - - /** {@inheritDoc} */ - protected void drawBorderLine( // CSOK: ParameterNumber - int x1, int y1, int x2, int y2, boolean horz, - boolean startOrBefore, int style, Color color) { - float w = x2 - x1; - float h = y2 - y1; - if ((w < 0) || (h < 0)) { - log.error("Negative extent received. Border won't be painted."); - return; - } - switch (style) { - case Constants.EN_DASHED: - getG2D().setColor(color); - if (horz) { - float unit = Math.abs(2 * h); - int rep = (int)(w / unit); - if (rep % 2 == 0) { - rep++; - } - unit = w / rep; - float ym = y1 + (h / 2); - BasicStroke s = new BasicStroke(h, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_MITER, 10.0f, new float[] {unit}, 0); - getG2D().setStroke(s); - getG2D().draw(new Line2D.Float(x1, ym, x2, ym)); - } else { - float unit = Math.abs(2 * w); - int rep = (int)(h / unit); - if (rep % 2 == 0) { - rep++; - } - unit = h / rep; - float xm = x1 + (w / 2); - BasicStroke s = new BasicStroke(w, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_MITER, 10.0f, new float[] {unit}, 0); - getG2D().setStroke(s); - getG2D().draw(new Line2D.Float(xm, y1, xm, y2)); - } - break; - case Constants.EN_DOTTED: - getG2D().setColor(color); - if (horz) { - float unit = Math.abs(2 * h); - int rep = (int)(w / unit); - if (rep % 2 == 0) { - rep++; - } - unit = w / rep; - float ym = y1 + (h / 2); - BasicStroke s = new BasicStroke(h, BasicStroke.CAP_ROUND, - BasicStroke.JOIN_MITER, 10.0f, new float[] {0, unit}, 0); - getG2D().setStroke(s); - getG2D().draw(new Line2D.Float(x1, ym, x2, ym)); - } else { - float unit = Math.abs(2 * w); - int rep = (int)(h / unit); - if (rep % 2 == 0) { - rep++; - } - unit = h / rep; - float xm = x1 + (w / 2); - BasicStroke s = new BasicStroke(w, BasicStroke.CAP_ROUND, - BasicStroke.JOIN_MITER, 10.0f, new float[] {0, unit}, 0); - getG2D().setStroke(s); - getG2D().draw(new Line2D.Float(xm, y1, xm, y2)); - } - break; - case Constants.EN_DOUBLE: - getG2D().setColor(color); - if (horz) { - float h3 = h / 3; - float ym1 = y1 + (h3 / 2); - float ym2 = ym1 + h3 + h3; - BasicStroke s = new BasicStroke(h3); - getG2D().setStroke(s); - getG2D().draw(new Line2D.Float(x1, ym1, x2, ym1)); - getG2D().draw(new Line2D.Float(x1, ym2, x2, ym2)); - } else { - float w3 = w / 3; - float xm1 = x1 + (w3 / 2); - float xm2 = xm1 + w3 + w3; - BasicStroke s = new BasicStroke(w3); - getG2D().setStroke(s); - getG2D().draw(new Line2D.Float(xm1, y1, xm1, y2)); - getG2D().draw(new Line2D.Float(xm2, y1, xm2, y2)); - } - break; - case Constants.EN_GROOVE: - case Constants.EN_RIDGE: - float colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); - if (horz) { - Color uppercol = ColorUtil.lightenColor(color, -colFactor); - Color lowercol = ColorUtil.lightenColor(color, colFactor); - float h3 = h / 3; - float ym1 = y1 + (h3 / 2); - getG2D().setStroke(new BasicStroke(h3)); - getG2D().setColor(uppercol); - getG2D().draw(new Line2D.Float(x1, ym1, x2, ym1)); - getG2D().setColor(color); - getG2D().draw(new Line2D.Float(x1, ym1 + h3, x2, ym1 + h3)); - getG2D().setColor(lowercol); - getG2D().draw(new Line2D.Float(x1, ym1 + h3 + h3, x2, ym1 + h3 + h3)); - } else { - Color leftcol = ColorUtil.lightenColor(color, -colFactor); - Color rightcol = ColorUtil.lightenColor(color, colFactor); - float w3 = w / 3; - float xm1 = x1 + (w3 / 2); - getG2D().setStroke(new BasicStroke(w3)); - getG2D().setColor(leftcol); - getG2D().draw(new Line2D.Float(xm1, y1, xm1, y2)); - getG2D().setColor(color); - getG2D().draw(new Line2D.Float(xm1 + w3, y1, xm1 + w3, y2)); - getG2D().setColor(rightcol); - getG2D().draw(new Line2D.Float(xm1 + w3 + w3, y1, xm1 + w3 + w3, y2)); - } - break; - case Constants.EN_INSET: - case Constants.EN_OUTSET: - colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); - if (horz) { - color = ColorUtil.lightenColor(color, (startOrBefore ? 1 : -1) * colFactor); - getG2D().setStroke(new BasicStroke(h)); - float ym1 = y1 + (h / 2); - getG2D().setColor(color); - getG2D().draw(new Line2D.Float(x1, ym1, x2, ym1)); - } else { - color = ColorUtil.lightenColor(color, (startOrBefore ? 1 : -1) * colFactor); - float xm1 = x1 + (w / 2); - getG2D().setStroke(new BasicStroke(w)); - getG2D().setColor(color); - getG2D().draw(new Line2D.Float(xm1, y1, xm1, y2)); - } - break; - case Constants.EN_HIDDEN: - break; - default: - getG2D().setColor(color); - if (horz) { - float ym = y1 + (h / 2); - getG2D().setStroke(new BasicStroke(h)); - getG2D().draw(new Line2D.Float(x1, ym, x2, ym)); - } else { - float xm = x1 + (w / 2); - getG2D().setStroke(new BasicStroke(w)); - getG2D().draw(new Line2D.Float(xm, y1, xm, y2)); - } - } - } - - /** {@inheritDoc} */ - public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) { - if (start.y != end.y) { - //TODO Support arbitrary lines if necessary - throw new UnsupportedOperationException( - "Can only deal with horizontal lines right now"); - } - - saveGraphicsState(); - int half = width / 2; - int starty = start.y - half; - Rectangle boundingRect = new Rectangle(start.x, start.y - half, end.x - start.x, width); - getG2DState().updateClip(boundingRect); - - switch (style.getEnumValue()) { - case Constants.EN_SOLID: - case Constants.EN_DASHED: - case Constants.EN_DOUBLE: - drawBorderLine(start.x, start.y - half, end.x, end.y + half, - true, true, style.getEnumValue(), color); - break; - case Constants.EN_DOTTED: - int shift = half; //This shifts the dots to the right by half a dot's width - drawBorderLine(start.x + shift, start.y - half, end.x + shift, end.y + half, - true, true, style.getEnumValue(), color); - break; - case Constants.EN_GROOVE: - case Constants.EN_RIDGE: - getG2DState().updateColor(ColorUtil.lightenColor(color, 0.6f)); - moveTo(start.x, starty); - lineTo(end.x, starty); - lineTo(end.x, starty + 2 * half); - lineTo(start.x, starty + 2 * half); - closePath(); - getG2D().fill(currentPath); - currentPath = null; - getG2DState().updateColor(color); - if (style.getEnumValue() == Constants.EN_GROOVE) { - moveTo(start.x, starty); - lineTo(end.x, starty); - lineTo(end.x, starty + half); - lineTo(start.x + half, starty + half); - lineTo(start.x, starty + 2 * half); - } else { - moveTo(end.x, starty); - lineTo(end.x, starty + 2 * half); - lineTo(start.x, starty + 2 * half); - lineTo(start.x, starty + half); - lineTo(end.x - half, starty + half); - } - closePath(); - getG2D().fill(currentPath); - currentPath = null; - - case Constants.EN_NONE: - // No rule is drawn - break; - default: - } // end switch - restoreGraphicsState(); - } - - /** {@inheritDoc} */ - protected void clip() { - if (currentPath == null) { - throw new IllegalStateException("No current path available!"); - } - getG2DState().updateClip(currentPath); - currentPath = null; - } - - /** {@inheritDoc} */ - protected void closePath() { - currentPath.closePath(); - } - - /** {@inheritDoc} */ - protected void lineTo(int x, int y) { - if (currentPath == null) { - currentPath = new GeneralPath(); - } - currentPath.lineTo(x, y); - } - - /** {@inheritDoc} */ - protected void moveTo(int x, int y) { - if (currentPath == null) { - currentPath = new GeneralPath(); - } - currentPath.moveTo(x, y); - } - - /** {@inheritDoc} */ - protected void saveGraphicsState() { - this.painter.saveGraphicsState(); - } - - /** {@inheritDoc} */ - protected void restoreGraphicsState() { - this.painter.restoreGraphicsState(); - this.currentPath = null; - } - - /** {@inheritDoc} */ - protected void arcTo(double startAngle, double endAngle, int cx, int cy, int width, int height) - throws IOException { - // TODO Auto-generated method stub - - } - - /** {@inheritDoc} */ - protected void changeCoords(double a, double b, double c, double d, double e, double f) { - // TODO Auto-generated method stub - - } - - /** {@inheritDoc} */ - protected void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) - throws IOException { - // TODO Auto-generated method stub - - } - - /** {@inheritDoc} */ - protected void rotateCoordinates(double angle) throws IOException { - // TODO Auto-generated method stub - - } - - /** {@inheritDoc} */ - protected void scaleCoordinates(float xScale, float yScale) throws IOException { - // TODO Auto-generated method stub - - } - - /** {@inheritDoc} */ - protected void translateCoordinates(int xTranslate, int yTranslate) throws IOException { - // TODO Auto-generated method stub - - } - -} diff --git a/src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java b/src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java new file mode 100644 index 000000000..b485daf3d --- /dev/null +++ b/src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java @@ -0,0 +1,333 @@ +/* + * 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.java2d; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.fo.Constants; +import org.apache.fop.render.intermediate.GraphicsPainter; +import org.apache.fop.traits.RuleStyle; +import org.apache.fop.util.ColorUtil; + +class Java2DGraphicsPainter implements GraphicsPainter { + + /** logging instance */ + static final Log log = LogFactory.getLog(Java2DGraphicsPainter.class); + + + private GeneralPath currentPath; + + private final Java2DPainter painter; + + + Java2DGraphicsPainter(Java2DPainter painter) { + this.painter = painter; + } + + private Java2DGraphicsState getG2DState() { + return this.painter.g2dState; + } + + + private Graphics2D getG2D() { + return getG2DState().getGraph(); + } + + public void drawBorderLine(int x1, int y1, int x2, int y2, + boolean horz, boolean startOrBefore, int style, Color color) + throws IOException { + float w = x2 - x1; + float h = y2 - y1; + if ((w < 0) || (h < 0)) { + log.error("Negative extent received. Border won't be painted."); + return; + } + switch (style) { + case Constants.EN_DASHED: + getG2D().setColor(color); + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int)(w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + float ym = y1 + (h / 2); + BasicStroke s = new BasicStroke(h, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, 10.0f, new float[] {unit}, 0); + getG2D().setStroke(s); + getG2D().draw(new Line2D.Float(x1, ym, x2, ym)); + } else { + float unit = Math.abs(2 * w); + int rep = (int)(h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + float xm = x1 + (w / 2); + BasicStroke s = new BasicStroke(w, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, 10.0f, new float[] {unit}, 0); + getG2D().setStroke(s); + getG2D().draw(new Line2D.Float(xm, y1, xm, y2)); + } + break; + case Constants.EN_DOTTED: + getG2D().setColor(color); + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int)(w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + float ym = y1 + (h / 2); + BasicStroke s = new BasicStroke(h, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_MITER, 10.0f, new float[] {0, unit}, 0); + getG2D().setStroke(s); + getG2D().draw(new Line2D.Float(x1, ym, x2, ym)); + } else { + float unit = Math.abs(2 * w); + int rep = (int)(h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + float xm = x1 + (w / 2); + BasicStroke s = new BasicStroke(w, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_MITER, 10.0f, new float[] {0, unit}, 0); + getG2D().setStroke(s); + getG2D().draw(new Line2D.Float(xm, y1, xm, y2)); + } + break; + case Constants.EN_DOUBLE: + getG2D().setColor(color); + if (horz) { + float h3 = h / 3; + float ym1 = y1 + (h3 / 2); + float ym2 = ym1 + h3 + h3; + BasicStroke s = new BasicStroke(h3); + getG2D().setStroke(s); + getG2D().draw(new Line2D.Float(x1, ym1, x2, ym1)); + getG2D().draw(new Line2D.Float(x1, ym2, x2, ym2)); + } else { + float w3 = w / 3; + float xm1 = x1 + (w3 / 2); + float xm2 = xm1 + w3 + w3; + BasicStroke s = new BasicStroke(w3); + getG2D().setStroke(s); + getG2D().draw(new Line2D.Float(xm1, y1, xm1, y2)); + getG2D().draw(new Line2D.Float(xm2, y1, xm2, y2)); + } + break; + case Constants.EN_GROOVE: + case Constants.EN_RIDGE: + float colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); + if (horz) { + Color uppercol = ColorUtil.lightenColor(color, -colFactor); + Color lowercol = ColorUtil.lightenColor(color, colFactor); + float h3 = h / 3; + float ym1 = y1 + (h3 / 2); + getG2D().setStroke(new BasicStroke(h3)); + getG2D().setColor(uppercol); + getG2D().draw(new Line2D.Float(x1, ym1, x2, ym1)); + getG2D().setColor(color); + getG2D().draw(new Line2D.Float(x1, ym1 + h3, x2, ym1 + h3)); + getG2D().setColor(lowercol); + getG2D().draw(new Line2D.Float(x1, ym1 + h3 + h3, x2, ym1 + h3 + h3)); + } else { + Color leftcol = ColorUtil.lightenColor(color, -colFactor); + Color rightcol = ColorUtil.lightenColor(color, colFactor); + float w3 = w / 3; + float xm1 = x1 + (w3 / 2); + getG2D().setStroke(new BasicStroke(w3)); + getG2D().setColor(leftcol); + getG2D().draw(new Line2D.Float(xm1, y1, xm1, y2)); + getG2D().setColor(color); + getG2D().draw(new Line2D.Float(xm1 + w3, y1, xm1 + w3, y2)); + getG2D().setColor(rightcol); + getG2D().draw(new Line2D.Float(xm1 + w3 + w3, y1, xm1 + w3 + w3, y2)); + } + break; + case Constants.EN_INSET: + case Constants.EN_OUTSET: + colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); + if (horz) { + color = ColorUtil.lightenColor(color, (startOrBefore ? 1 : -1) * colFactor); + getG2D().setStroke(new BasicStroke(h)); + float ym1 = y1 + (h / 2); + getG2D().setColor(color); + getG2D().draw(new Line2D.Float(x1, ym1, x2, ym1)); + } else { + color = ColorUtil.lightenColor(color, (startOrBefore ? 1 : -1) * colFactor); + float xm1 = x1 + (w / 2); + getG2D().setStroke(new BasicStroke(w)); + getG2D().setColor(color); + getG2D().draw(new Line2D.Float(xm1, y1, xm1, y2)); + } + break; + case Constants.EN_HIDDEN: + break; + default: + getG2D().setColor(color); + if (horz) { + float ym = y1 + (h / 2); + getG2D().setStroke(new BasicStroke(h)); + getG2D().draw(new Line2D.Float(x1, ym, x2, ym)); + } else { + float xm = x1 + (w / 2); + getG2D().setStroke(new BasicStroke(w)); + getG2D().draw(new Line2D.Float(xm, y1, xm, y2)); + } + } + } + + public void drawLine(Point start, Point end, int width, Color color, + RuleStyle style) throws IOException { + if (start.y != end.y) { + //TODO Support arbitrary lines if necessary + throw new UnsupportedOperationException( + "Can only deal with horizontal lines right now"); + } + + saveGraphicsState(); + int half = width / 2; + int starty = start.y - half; + Rectangle boundingRect = new Rectangle(start.x, start.y - half, end.x - start.x, width); + getG2DState().updateClip(boundingRect); + + switch (style.getEnumValue()) { + case Constants.EN_SOLID: + case Constants.EN_DASHED: + case Constants.EN_DOUBLE: + drawBorderLine(start.x, start.y - half, end.x, end.y + half, + true, true, style.getEnumValue(), color); + break; + case Constants.EN_DOTTED: + int shift = half; //This shifts the dots to the right by half a dot's width + drawBorderLine(start.x + shift, start.y - half, end.x + shift, end.y + half, + true, true, style.getEnumValue(), color); + break; + case Constants.EN_GROOVE: + case Constants.EN_RIDGE: + getG2DState().updateColor(ColorUtil.lightenColor(color, 0.6f)); + moveTo(start.x, starty); + lineTo(end.x, starty); + lineTo(end.x, starty + 2 * half); + lineTo(start.x, starty + 2 * half); + closePath(); + getG2D().fill(currentPath); + currentPath = null; + getG2DState().updateColor(color); + if (style.getEnumValue() == Constants.EN_GROOVE) { + moveTo(start.x, starty); + lineTo(end.x, starty); + lineTo(end.x, starty + half); + lineTo(start.x + half, starty + half); + lineTo(start.x, starty + 2 * half); + } else { + moveTo(end.x, starty); + lineTo(end.x, starty + 2 * half); + lineTo(start.x, starty + 2 * half); + lineTo(start.x, starty + half); + lineTo(end.x - half, starty + half); + } + closePath(); + getG2D().fill(currentPath); + currentPath = null; + + case Constants.EN_NONE: + // No rule is drawn + break; + default: + } // end switch + restoreGraphicsState(); + } + + /** {@inheritDoc} */ + public void moveTo(int x, int y) throws IOException { + if (currentPath == null) { + currentPath = new GeneralPath(); + } + currentPath.moveTo(x, y); + } + + /** {@inheritDoc} */ + public void lineTo(int x, int y) throws IOException { + if (currentPath == null) { + currentPath = new GeneralPath(); + } + currentPath.lineTo(x, y); + } + + /** {@inheritDoc} */ + public void arcTo(double startAngle, double endAngle, int cx, int cy, + int width, int height) throws IOException { + } + + /** {@inheritDoc} */ + public void rotateCoordinates(double angle) throws IOException { + } + + /** {@inheritDoc} */ + public void translateCoordinates(int xTranslate, int yTranslate) + throws IOException { + } + + /** {@inheritDoc} */ + public void scaleCoordinates(float xScale, float yScale) + throws IOException { + } + + /** {@inheritDoc} */ + public void closePath() throws IOException { + currentPath.closePath(); + } + + /** {@inheritDoc} */ + public void clip() throws IOException { + if (currentPath == null) { + throw new IllegalStateException("No current path available!"); + } + getG2DState().updateClip(currentPath); + currentPath = null; + } + + /** {@inheritDoc} */ + public void saveGraphicsState() throws IOException { + this.painter.saveGraphicsState(); + } + + /** {@inheritDoc} */ + public void restoreGraphicsState() throws IOException { + this.painter.restoreGraphicsState(); + this.currentPath = null; + } + +}
\ No newline at end of file diff --git a/src/java/org/apache/fop/render/java2d/Java2DPainter.java b/src/java/org/apache/fop/render/java2d/Java2DPainter.java index 7b8a96b99..e34fb4bbb 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DPainter.java +++ b/src/java/org/apache/fop/render/java2d/Java2DPainter.java @@ -38,6 +38,8 @@ import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; import org.apache.fop.render.RenderingContext; import org.apache.fop.render.intermediate.AbstractIFPainter; +import org.apache.fop.render.intermediate.BorderPainter; +import org.apache.fop.render.intermediate.GraphicsPainter; import org.apache.fop.render.intermediate.IFContext; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFState; @@ -58,7 +60,9 @@ public class Java2DPainter extends AbstractIFPainter<Java2DDocumentHandler> { /** The font information */ protected FontInfo fontInfo; - private Java2DBorderPainter borderPainter; + private final GraphicsPainter graphicsPainter; + + private final BorderPainter borderPainter; /** The current state, holds a Graphics2D and its context */ protected Java2DGraphicsState g2dState; @@ -92,7 +96,8 @@ public class Java2DPainter extends AbstractIFPainter<Java2DDocumentHandler> { } this.fontInfo = fontInfo; this.g2dState = new Java2DGraphicsState(g2d, fontInfo, g2d.getTransform()); - this.borderPainter = new Java2DBorderPainter(this); + graphicsPainter = new Java2DGraphicsPainter(this); + this.borderPainter = new BorderPainter(graphicsPainter); } /** {@inheritDoc} */ @@ -202,7 +207,11 @@ public class Java2DPainter extends AbstractIFPainter<Java2DDocumentHandler> { /** {@inheritDoc} */ public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) throws IFException { - this.borderPainter.drawLine(start, end, width, color, style); + try { + this.graphicsPainter.drawLine(start, end, width, color, style); + } catch (IOException ioe) { + throw new IFException("Unexpected error drawing line", ioe); + } } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/pdf/PDFBorderPainter.java b/src/java/org/apache/fop/render/pdf/PDFBorderPainter.java deleted file mode 100644 index 4bfba9df3..000000000 --- a/src/java/org/apache/fop/render/pdf/PDFBorderPainter.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * 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.pdf; - -import java.awt.Color; -import java.awt.Point; -import java.awt.Rectangle; -import java.io.IOException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.apache.fop.fo.Constants; -import org.apache.fop.render.intermediate.BorderPainter; -import org.apache.fop.traits.RuleStyle; -import org.apache.fop.util.ColorUtil; - -/** - * PDF-specific implementation of the {@link BorderPainter}. - */ -public class PDFBorderPainter extends BorderPainter { - - /** logging instance */ - private static final Log LOG = LogFactory.getLog(PDFBorderPainter.class); - - private PDFContentGenerator generator; - - /** - * Construct a border painter. - * @param generator a pdf content generator - */ - public PDFBorderPainter(PDFContentGenerator generator) { - this.generator = generator; - } - - /** {@inheritDoc} */ - protected void drawBorderLine( // CSOK: ParameterNumber - int x1, int y1, int x2, int y2, boolean horz, - boolean startOrBefore, int style, Color col) { - //TODO lose scale? - drawBorderLine(generator, x1 / 1000f, y1 / 1000f, x2 / 1000f, y2 / 1000f, - horz, startOrBefore, style, col); - } - - /** - * @param generator pdf content generator - * @see BorderPainter#drawBorderLine - */ - public static void drawBorderLine( // CSOK: ParameterNumber|MethodLength - PDFContentGenerator generator, - float x1, float y1, float x2, float y2, boolean horz, // CSOK: JavadocMethod - boolean startOrBefore, int style, Color col) { // CSOK: JavadocMethod - float colFactor; - float w = x2 - x1; - float h = y2 - y1; - /* - if ((w < 0) || (h < 0)) { - LOG.error("Negative extent received (w=" + w + ", h=" + h - + "). Border won't be painted."); - return; - }*/ - switch (style) { - case Constants.EN_DASHED: - generator.setColor(col, false); - if (horz) { - float unit = Math.abs(2 * h); - int rep = (int)(w / unit); - if (rep % 2 == 0) { - rep++; - } - unit = w / rep; - generator.add("[" + format(unit) + "] 0 d "); - generator.add(format(h) + " w\n"); - float ym = y1 + (h / 2); - generator.add(format(x1) + " " + format(ym) + " m " - + format(x2) + " " + format(ym) + " l S\n"); - } else { - float unit = Math.abs(2 * w); - int rep = (int)(h / unit); - if (rep % 2 == 0) { - rep++; - } - unit = h / rep; - generator.add("[" + format(unit) + "] 0 d "); - generator.add(format(w) + " w\n"); - float xm = x1 + (w / 2); - generator.add(format(xm) + " " + format(y1) + " m " - + format(xm) + " " + format(y2) + " l S\n"); - } - break; - case Constants.EN_DOTTED: - generator.setColor(col, false); - generator.add("1 J "); - if (horz) { - float unit = Math.abs(2 * h); - int rep = (int)(w / unit); - if (rep % 2 == 0) { - rep++; - } - unit = w / rep; - generator.add("[0 " + format(unit) + "] 0 d "); - generator.add(format(h) + " w\n"); - float ym = y1 + (h / 2); - generator.add(format(x1) + " " + format(ym) + " m " - + format(x2) + " " + format(ym) + " l S\n"); - } else { - float unit = Math.abs(2 * w); - int rep = (int)(h / unit); - if (rep % 2 == 0) { - rep++; - } - unit = h / rep; - generator.add("[0 " + format(unit) + " ] 0 d "); - generator.add(format(w) + " w\n"); - float xm = x1 + (w / 2); - generator.add(format(xm) + " " + format(y1) + " m " - + format(xm) + " " + format(y2) + " l S\n"); - } - break; - case Constants.EN_DOUBLE: - generator.setColor(col, false); - generator.add("[] 0 d "); - if (horz) { - float h3 = h / 3; - generator.add(format(h3) + " w\n"); - float ym1 = y1 + (h3 / 2); - float ym2 = ym1 + h3 + h3; - generator.add(format(x1) + " " + format(ym1) + " m " - + format(x2) + " " + format(ym1) + " l S\n"); - generator.add(format(x1) + " " + format(ym2) + " m " - + format(x2) + " " + format(ym2) + " l S\n"); - } else { - float w3 = w / 3; - generator.add(format(w3) + " w\n"); - float xm1 = x1 + (w3 / 2); - float xm2 = xm1 + w3 + w3; - generator.add(format(xm1) + " " + format(y1) + " m " - + format(xm1) + " " + format(y2) + " l S\n"); - generator.add(format(xm2) + " " + format(y1) + " m " - + format(xm2) + " " + format(y2) + " l S\n"); - } - break; - case Constants.EN_GROOVE: - case Constants.EN_RIDGE: - colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); - generator.add("[] 0 d "); - if (horz) { - Color uppercol = ColorUtil.lightenColor(col, -colFactor); - Color lowercol = ColorUtil.lightenColor(col, colFactor); - float h3 = h / 3; - generator.add(format(h3) + " w\n"); - float ym1 = y1 + (h3 / 2); - generator.setColor(uppercol, false); - generator.add(format(x1) + " " + format(ym1) + " m " - + format(x2) + " " + format(ym1) + " l S\n"); - generator.setColor(col, false); - generator.add(format(x1) + " " + format(ym1 + h3) + " m " - + format(x2) + " " + format(ym1 + h3) + " l S\n"); - generator.setColor(lowercol, false); - generator.add(format(x1) + " " + format(ym1 + h3 + h3) + " m " - + format(x2) + " " + format(ym1 + h3 + h3) + " l S\n"); - } else { - Color leftcol = ColorUtil.lightenColor(col, -colFactor); - Color rightcol = ColorUtil.lightenColor(col, colFactor); - float w3 = w / 3; - generator.add(format(w3) + " w\n"); - float xm1 = x1 + (w3 / 2); - generator.setColor(leftcol, false); - generator.add(format(xm1) + " " + format(y1) + " m " - + format(xm1) + " " + format(y2) + " l S\n"); - generator.setColor(col, false); - generator.add(format(xm1 + w3) + " " + format(y1) + " m " - + format(xm1 + w3) + " " + format(y2) + " l S\n"); - generator.setColor(rightcol, false); - generator.add(format(xm1 + w3 + w3) + " " + format(y1) + " m " - + format(xm1 + w3 + w3) + " " + format(y2) + " l S\n"); - } - break; - case Constants.EN_INSET: - case Constants.EN_OUTSET: - colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); - generator.add("[] 0 d "); - Color c = col; - if (horz) { - c = ColorUtil.lightenColor(c, (startOrBefore ? 1 : -1) * colFactor); - generator.add(format(h) + " w\n"); - float ym1 = y1 + (h / 2); - generator.setColor(c, false); - generator.add(format(x1) + " " + format(ym1) + " m " - + format(x2) + " " + format(ym1) + " l S\n"); - } else { - c = ColorUtil.lightenColor(c, (startOrBefore ? 1 : -1) * colFactor); - generator.add(format(w) + " w\n"); - float xm1 = x1 + (w / 2); - generator.setColor(c, false); - generator.add(format(xm1) + " " + format(y1) + " m " - + format(xm1) + " " + format(y2) + " l S\n"); - } - break; - case Constants.EN_HIDDEN: - break; - default: - generator.setColor(col, false); - generator.add("[] 0 d "); - if (horz) { - generator.add(format(h) + " w\n"); - float ym = y1 + (h / 2); - generator.add(format(x1) + " " + format(ym) + " m " - + format(x2) + " " + format(ym) + " l S\n"); - } else { - generator.add(format(w) + " w\n"); - float xm = x1 + (w / 2); - generator.add(format(xm) + " " + format(y1) + " m " - + format(xm) + " " + format(y2) + " l S\n"); - } - } - } - - /** {@inheritDoc} */ - public void drawLine(Point start, Point end, - int width, Color color, RuleStyle style) { - if (start.y != end.y) { - //TODO Support arbitrary lines if necessary - throw new UnsupportedOperationException( - "Can only deal with horizontal lines right now"); - } - - saveGraphicsState(); - int half = width / 2; - int starty = start.y - half; - Rectangle boundingRect = new Rectangle(start.x, start.y - half, end.x - start.x, width); - switch (style.getEnumValue()) { - case Constants.EN_SOLID: - case Constants.EN_DASHED: - case Constants.EN_DOUBLE: - drawBorderLine(start.x, start.y - half, end.x, end.y + half, - true, true, style.getEnumValue(), color); - break; - case Constants.EN_DOTTED: - generator.clipRect(boundingRect); - //This displaces the dots to the right by half a dot's width - //TODO There's room for improvement here - generator.add("1 0 0 1 " + format(half) + " 0 cm\n"); - drawBorderLine(start.x, start.y - half, end.x, end.y + half, - true, true, style.getEnumValue(), color); - break; - case Constants.EN_GROOVE: - case Constants.EN_RIDGE: - generator.setColor(ColorUtil.lightenColor(color, 0.6f), true); - generator.add(format(start.x) + " " + format(starty) + " m\n"); - generator.add(format(end.x) + " " + format(starty) + " l\n"); - generator.add(format(end.x) + " " + format(starty + 2 * half) + " l\n"); - generator.add(format(start.x) + " " + format(starty + 2 * half) + " l\n"); - generator.add("h\n"); - generator.add("f\n"); - generator.setColor(color, true); - if (style == RuleStyle.GROOVE) { - generator.add(format(start.x) + " " + format(starty) + " m\n"); - generator.add(format(end.x) + " " + format(starty) + " l\n"); - generator.add(format(end.x) + " " + format(starty + half) + " l\n"); - generator.add(format(start.x + half) + " " + format(starty + half) + " l\n"); - generator.add(format(start.x) + " " + format(starty + 2 * half) + " l\n"); - } else { - generator.add(format(end.x) + " " + format(starty) + " m\n"); - generator.add(format(end.x) + " " + format(starty + 2 * half) + " l\n"); - generator.add(format(start.x) + " " + format(starty + 2 * half) + " l\n"); - generator.add(format(start.x) + " " + format(starty + half) + " l\n"); - generator.add(format(end.x - half) + " " + format(starty + half) + " l\n"); - } - generator.add("h\n"); - generator.add("f\n"); - break; - default: - throw new UnsupportedOperationException("rule style not supported"); - } - restoreGraphicsState(); - } - - static final String format(int coordinate) { - //TODO lose scale? - return format(coordinate / 1000f); - } - - static final String format(float coordinate) { - return PDFContentGenerator.format(coordinate); - } - - /** {@inheritDoc} */ - protected void moveTo(int x, int y) { - generator.add(format(x) + " " + format(y) + " m "); - } - - /** {@inheritDoc} */ - protected void lineTo(int x, int y) { - generator.add(format(x) + " " + format(y) + " l "); - } - - /** {@inheritDoc} */ - protected void closePath() { - generator.add("h "); - } - - /** {@inheritDoc} */ - protected void clip() { - generator.add("W\n" + "n\n"); - } - - /** {@inheritDoc} */ - protected void saveGraphicsState() { - generator.add("q\n"); - } - - /** {@inheritDoc} */ - protected void restoreGraphicsState() { - generator.add("Q\n"); - } - - /** {@inheritDoc} */ - protected void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) { - generator.add(format(p1x) + " " + format(p1y) + " " + format(p2x) + " " + format(p2y) - + " " + format(p3x) + " " + format(p3y) + " c "); - } - - - private void transformCoordinates(int a, int b, int c, int d, int e, int f) { - generator.add( "" + format(a) + " " + format(b) + " " + format(c) + " " + format(d) - + " " + format(e) + " " + format(f) + " cm "); - } - - private void transformCoordinates2(float a, float b, float c, float d, float e, float f) { - generator.add( "" + format(a) + " " + format(b) + " " + format(c) + " " + format(d) - + " " + format(e) + " " + format(f) + " cm "); - } - - /** {@inheritDoc} */ - protected void rotateCoordinates(double angle) throws IOException { - float s = (float)Math.sin(angle); - float c = (float)Math.cos(angle); - transformCoordinates2(c, s, -s, c, 0, 0); - } - - /** {@inheritDoc} */ - protected void translateCoordinates(int xTranslate, int yTranslate) throws IOException { - transformCoordinates(1000, 0, 0, 1000, xTranslate, yTranslate); - } - - /** {@inheritDoc} */ - protected void scaleCoordinates(float xScale, float yScale) throws IOException { - transformCoordinates2(xScale, 0, 0, yScale, 0, 0); - } - -} diff --git a/src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java b/src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java new file mode 100644 index 000000000..ec3073e6f --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java @@ -0,0 +1,499 @@ +/* + * 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.pdf; + +import java.awt.Color; +import java.awt.Point; +import java.awt.Rectangle; +import java.io.IOException; + +import org.apache.fop.fo.Constants; +import org.apache.fop.render.intermediate.ArcToBezierCurveTransformer; +import org.apache.fop.render.intermediate.BezierCurvePainter; +import org.apache.fop.render.intermediate.GraphicsPainter; +import org.apache.fop.traits.RuleStyle; +import org.apache.fop.util.ColorUtil; + +/** + * PDF-specific implementation of the {@link GraphicsPainter}. + */ +public class PDFGraphicsPainter implements GraphicsPainter, BezierCurvePainter { + + private final PDFContentGeneratorHelper generator; + + /** Used for drawing arcs since PS does not natively support drawing elliptic curves */ + private final ArcToBezierCurveTransformer arcToBezierCurveTransformer; + + public PDFGraphicsPainter(PDFContentGenerator generator) { + this.generator = new PDFContentGeneratorHelper(generator); + this.arcToBezierCurveTransformer = new ArcToBezierCurveTransformer(this); + } + + /** {@inheritDoc} */ + public void drawBorderLine(int x1, int y1, int x2, int y2, boolean horz, + boolean startOrBefore, int style, Color col) { + //TODO lose scale? + drawBorderLine2(x1 / 1000f, y1 / 1000f, x2 / 1000f, y2 / 1000f, + horz, startOrBefore, style, col); + } + + /** {@inheritDoc} */ + private void drawBorderLine2(float x1, float y1, float x2, float y2, boolean horz, + boolean startOrBefore, int style, Color col) { + float w = x2 - x1; + float h = y2 - y1; + switch (style) { + case Constants.EN_DASHED: + generator.setColor(col); + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int) (w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + float ym = y1 + (h / 2); + generator.setDashLine(unit) + .setLineWidth(h) + .strokeLine(x1, ym, x2, ym); + } else { + float unit = Math.abs(2 * w); + int rep = (int) (h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + float xm = x1 + (w / 2); + generator.setDashLine(unit) + .setLineWidth(w) + .strokeLine(xm, y1, xm, y2); + } + break; + case Constants.EN_DOTTED: + generator.setColor(col).setRoundCap(); + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int) (w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + float ym = y1 + (h / 2); + generator.setDashLine(0, unit) + .setLineWidth(h) + .strokeLine(x1, ym, x2, ym); + } else { + float unit = Math.abs(2 * w); + int rep = (int) (h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + float xm = x1 + (w / 2); + generator.setDashLine(0, unit) + .setLineWidth(w) + .strokeLine(xm, y1, xm, y2); + } + break; + case Constants.EN_DOUBLE: + generator.setColor(col) + .setSolidLine(); + if (horz) { + float h3 = h / 3; + float ym1 = y1 + (h3 / 2); + float ym2 = ym1 + h3 + h3; + generator.setLineWidth(h3) + .strokeLine(x1, ym1, x2, ym1) + .strokeLine(x1, ym2, x2, ym2); + } else { + float w3 = w / 3; + float xm1 = x1 + (w3 / 2); + float xm2 = xm1 + w3 + w3; + generator.setLineWidth(w3) + .strokeLine(xm1, y1, xm1, y2) + .strokeLine(xm2, y1, xm2, y2); + } + break; + case Constants.EN_GROOVE: + case Constants.EN_RIDGE: + { + float colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); + generator.setSolidLine(); + if (horz) { + Color uppercol = ColorUtil.lightenColor(col, -colFactor); + Color lowercol = ColorUtil.lightenColor(col, colFactor); + float h3 = h / 3; + float ym1 = y1 + (h3 / 2); + generator.setLineWidth(h3) + .setColor(uppercol) + .strokeLine(x1, ym1, x2, ym1) + .setColor(col) + .strokeLine(x1, ym1 + h3, x2, ym1 + h3) + .setColor(lowercol) + .strokeLine(x1, ym1 + h3 + h3, x2, ym1 + h3 + h3); + } else { + Color leftcol = ColorUtil.lightenColor(col, -colFactor); + Color rightcol = ColorUtil.lightenColor(col, colFactor); + float w3 = w / 3; + float xm1 = x1 + (w3 / 2); + generator.setLineWidth(w3) + .setColor(leftcol) + .strokeLine(xm1, y1, xm1, y2) + .setColor(col) + .strokeLine(xm1 + w3, y1, xm1 + w3, y2) + .setColor(rightcol) + .strokeLine(xm1 + w3 + w3, y1, xm1 + w3 + w3, y2); + } + break; + } + case Constants.EN_INSET: + case Constants.EN_OUTSET: + { + float colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); + generator.setSolidLine(); + Color c = col; + if (horz) { + c = ColorUtil.lightenColor(c, (startOrBefore ? 1 : -1) * colFactor); + float ym1 = y1 + (h / 2); + generator.setLineWidth(h) + .setColor(c) + .strokeLine(x1, ym1, x2, ym1); + } else { + c = ColorUtil.lightenColor(c, (startOrBefore ? 1 : -1) * colFactor); + float xm1 = x1 + (w / 2); + generator.setLineWidth(w) + .setColor(c) + .strokeLine(xm1, y1, xm1, y2); + } + break; + } + case Constants.EN_HIDDEN: + break; + default: + generator.setColor(col).setSolidLine(); + if (horz) { + float ym = y1 + (h / 2); + generator.setLineWidth(h) + .strokeLine(x1, ym, x2, ym); + } else { + float xm = x1 + (w / 2); + generator.setLineWidth(w) + .strokeLine(xm, y1, xm, y2); + } + } + } + + /** {@inheritDoc} */ + public void drawLine(Point start, Point end, + int width, Color color, RuleStyle style) { + if (start.y != end.y) { + //TODO Support arbitrary lines if necessary + throw new UnsupportedOperationException( + "Can only deal with horizontal lines right now"); + } + saveGraphicsState(); + int half = width / 2; + int starty = start.y - half; + Rectangle boundingRect = new Rectangle(start.x, start.y - half, end.x - start.x, width); + switch (style.getEnumValue()) { + case Constants.EN_SOLID: + case Constants.EN_DASHED: + case Constants.EN_DOUBLE: + drawBorderLine(start.x, start.y - half, end.x, end.y + half, + true, true, style.getEnumValue(), color); + break; + case Constants.EN_DOTTED: + generator.clipRect(boundingRect) + //This displaces the dots to the right by half a dot's width + //TODO There's room for improvement here + .transformCoordinatesLine(1, 0, 0 , 1, half, 0); + drawBorderLine(start.x, start.y - half, end.x, end.y + half, true, true, style.getEnumValue(), + color); + break; + case Constants.EN_GROOVE: + case Constants.EN_RIDGE: + generator.setFillColor(ColorUtil.lightenColor(color, 0.6f)) + .fillRect(start.x, start.y, end.x, starty + 2 * half) + .setFillColor(color) + .fillRidge(style, start.x, start.y, end.x, end.y, half); + break; + default: + throw new UnsupportedOperationException("rule style not supported"); + } + restoreGraphicsState(); + } + + private static String format(int coordinate) { + //TODO lose scale? + return format(coordinate / 1000f); + } + + private static String format(float coordinate) { + return PDFContentGenerator.format(coordinate); + } + + /** {@inheritDoc} */ + public void moveTo(int x, int y) { + generator.moveTo(x, y); + } + + /** {@inheritDoc} */ + public void lineTo(int x, int y) { + generator.lineTo(x, y); + } + + /** {@inheritDoc} */ + public void arcTo(final double startAngle, final double endAngle, final int cx, final int cy, + final int width, final int height) throws IOException { + arcToBezierCurveTransformer.arcTo(startAngle, endAngle, cx, cy, width, height); + } + + /** {@inheritDoc} */ + public void closePath() { + generator.closePath(); + } + + /** {@inheritDoc} */ + public void clip() { + generator.clip(); + } + + /** {@inheritDoc} */ + public void saveGraphicsState() { + generator.saveGraphicsState(); + } + + /** {@inheritDoc} */ + public void restoreGraphicsState() { + generator.restoreGraphicsState(); + } + + /** {@inheritDoc} */ + public void rotateCoordinates(double angle) throws IOException { + float s = (float) Math.sin(angle); + float c = (float) Math.cos(angle); + generator.transformFloatCoordinates(c, s, -s, c, 0, 0); + } + + /** {@inheritDoc} */ + public void translateCoordinates(int xTranslate, int yTranslate) throws IOException { + generator.transformCoordinates(1000, 0, 0, 1000, xTranslate, yTranslate); + } + + /** {@inheritDoc} */ + public void scaleCoordinates(float xScale, float yScale) throws IOException { + generator.transformFloatCoordinates(xScale, 0, 0, yScale, 0, 0); + } + + /** {@inheritDoc} */ + public void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) { + generator.cubicBezierTo(p1x, p1y, p2x, p2y, p3x, p3y); + } + + // TODO consider enriching PDFContentGenerator with part of this API + private static class PDFContentGeneratorHelper { + + private final PDFContentGenerator generator; + + public PDFContentGeneratorHelper(PDFContentGenerator generator) { + this.generator = generator; + } + + public PDFContentGeneratorHelper moveTo(int x, int y) { + return add("m", format(x), format(y)); + } + + public PDFContentGeneratorHelper lineTo(int x, int y) { + return add("l", format(x), format(y)); + } + + /** {@inheritDoc} */ + public PDFContentGeneratorHelper cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) { + return add("c", format(p1x), format(p1y), format(p2x), format(p2y), format(p3x), format(p3y)); + } + + public PDFContentGeneratorHelper closePath() { + return add("h"); + } + + public PDFContentGeneratorHelper clip() { + return addLine("W\nn"); + } + + public PDFContentGeneratorHelper clipRect(Rectangle rectangle) { + generator.clipRect(rectangle); + return this; + } + + public PDFContentGeneratorHelper saveGraphicsState() { + return addLine("q"); + } + + public PDFContentGeneratorHelper restoreGraphicsState() { + return addLine("Q"); + } + + public PDFContentGeneratorHelper setSolidLine() { + generator.add("[] 0 d "); + return this; + } + + public PDFContentGeneratorHelper setRoundCap() { + return add("J", "1"); + } + + public PDFContentGeneratorHelper strokeLine(float xStart, float yStart, float xEnd, float yEnd) { + add("m", xStart, yStart); + return addLine("l S", xEnd, yEnd); + } + + public PDFContentGeneratorHelper fillRect(int xStart, int yStart, int xEnd, int yEnd) { + String xS = format(xStart); + String xE = format(xEnd); + String yS = format(yStart); + String yE = format(yEnd); + return addLine("m", xS, yS) + .addLine("l", xE, yS) + .addLine("l", xE, yE) + .addLine("l", xS, yE) + .addLine("h") + .addLine("f"); + } + + public PDFContentGeneratorHelper fillRidge(RuleStyle style, int xStart, int yStart, int xEnd, + int yEnd, int half) { + String xS = format(xStart); + String xE = format(xEnd); + String yS = format(yStart); + String yE = format(yEnd); + if (style == RuleStyle.GROOVE) { + addLine("m", xS, yS) + .addLine("l", xE, yS) + .addLine("l", xE, format(yStart + half)) + .addLine("l", format(xStart + half), format(yStart + half)) + .addLine("l", xS, format(yStart + 2 * half)); + } else { + addLine("m", xE, yS) + .addLine("l", xE, format(yStart + 2 * half)) + .addLine("l", xS, format(yStart + 2 * half)) + .addLine("l", xS, format(yStart + half)) + .addLine("l", format(xEnd - half), format(yStart + half)); + } + return addLine("h").addLine("f"); + } + + public PDFContentGeneratorHelper setLineWidth(float width) { + return addLine("w", width); + } + + public PDFContentGeneratorHelper setDashLine(float first, float... rest) { + StringBuilder sb = new StringBuilder(); + sb.append("[").append(format(first)); + for (float unit : rest) { + sb.append(" ").append(format(unit)); + } + sb.append("] 0 d "); + generator.add(sb.toString()); + return this; + } + + public PDFContentGeneratorHelper setColor(Color col) { + generator.setColor(col, false); + return this; + } + + public PDFContentGeneratorHelper setFillColor(Color col) { + generator.setColor(col, true); + return this; + } + + public PDFContentGeneratorHelper transformFloatCoordinates(float a, float b, float c, float d, + float e, float f) { + return add("cm", a, b, c, d, e, f); + } + + public PDFContentGeneratorHelper transformCoordinates(int a, int b, int c, int d, int e, int f) { + return add("cm", format(a), format(b), format(c), format(d), format(e), format(f)); + } + + public PDFContentGeneratorHelper transformCoordinatesLine(int a, int b, int c, int d, int e, int f) { + return addLine("cm", format(a), format(b), format(c), format(d), format(e), format(f)); + } + + public PDFContentGeneratorHelper add(String op) { + assert op.equals(op.trim()); + generator.add(op + " "); + return this; + } + + private PDFContentGeneratorHelper add(String op, String... args) { + add(createArgs(args), op); + return this; + } + + public PDFContentGeneratorHelper addLine(String op) { + assert op.equals(op.trim()); + generator.add(op + "\n"); + return this; + } + + public PDFContentGeneratorHelper addLine(String op, String... args) { + addLine(createArgs(args), op); + return this; + } + + private PDFContentGeneratorHelper add(String op, float... args) { + add(createArgs(args), op); + return this; + } + + public PDFContentGeneratorHelper addLine(String op, float... args) { + addLine(createArgs(args), op); + return this; + } + + private StringBuilder createArgs(float... args) { + StringBuilder sb = new StringBuilder(); + for (float arg : args) { + sb.append(format(arg)).append(" "); + } + return sb; + } + + private StringBuilder createArgs(String... args) { + StringBuilder sb = new StringBuilder(); + for (String arg : args) { + sb.append(arg).append(" "); + } + return sb; + } + + private void add(StringBuilder args, String op) { + assert op.equals(op.trim()); + generator.add(args.append(op).append(" ").toString()); + } + + private void addLine(StringBuilder args, String op) { + assert op.equals(op.trim()); + generator.add(args.append(op).append("\n").toString()); + } + } + +} diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java index 77ec1d3bf..4ea7da945 100644 --- a/src/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java @@ -40,6 +40,8 @@ import org.apache.fop.pdf.PDFTextUtil; import org.apache.fop.pdf.PDFXObject; import org.apache.fop.render.RenderingContext; import org.apache.fop.render.intermediate.AbstractIFPainter; +import org.apache.fop.render.intermediate.BorderPainter; +import org.apache.fop.render.intermediate.GraphicsPainter; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFState; import org.apache.fop.render.intermediate.IFUtil; @@ -57,7 +59,9 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> { /** The current content generator */ protected PDFContentGenerator generator; - private final PDFBorderPainter borderPainter; + private final GraphicsPainter graphicsPainter; + + private final BorderPainter borderPainter; private boolean accessEnabled; @@ -75,7 +79,8 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> { super(documentHandler); this.logicalStructureHandler = logicalStructureHandler; this.generator = documentHandler.getGenerator(); - this.borderPainter = new PDFBorderPainter(this.generator); + this.graphicsPainter = new PDFGraphicsPainter(this.generator); + this.borderPainter = new BorderPainter(this.graphicsPainter); this.state = IFState.create(); accessEnabled = this.getUserAgent().isAccessibilityEnabled(); } @@ -267,7 +272,11 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> { public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) throws IFException { generator.endTextObject(); - this.borderPainter.drawLine(start, end, width, color, style); + try { + this.graphicsPainter.drawLine(start, end, width, color, style); + } catch (IOException ioe) { + throw new IFException("Cannot draw line", ioe); + } } private Typeface getTypeface(String fontName) { diff --git a/src/java/org/apache/fop/render/ps/PSBorderPainter.java b/src/java/org/apache/fop/render/ps/PSBorderPainter.java deleted file mode 100644 index 476e14c99..000000000 --- a/src/java/org/apache/fop/render/ps/PSBorderPainter.java +++ /dev/null @@ -1,387 +0,0 @@ -/* - * 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.ps; - -import java.awt.Color; -import java.awt.Point; -import java.io.IOException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.apache.xmlgraphics.ps.PSGenerator; - -import org.apache.fop.fo.Constants; -import org.apache.fop.render.intermediate.BorderPainter; -import org.apache.fop.traits.RuleStyle; -import org.apache.fop.util.ColorUtil; - -/** - * PostScript-specific implementation of the {@link BorderPainter}. - */ -public class PSBorderPainter extends BorderPainter { - - /** logging instance */ - private static Log log = LogFactory.getLog(PSBorderPainter.class); - - private PSGenerator generator; - - /** - * Creates a new border painter for PostScript. - * @param generator the PostScript generator - */ - public PSBorderPainter(PSGenerator generator) { - this.generator = generator; - } - - /** {@inheritDoc} */ - protected void drawBorderLine( // CSOK: ParameterNumber - int x1, int y1, int x2, int y2, boolean horz, - boolean startOrBefore, int style, Color col) throws IOException { - drawBorderLine(generator, toPoints(x1), toPoints(y1), toPoints(x2), toPoints(y2), - horz, startOrBefore, style, col); - } - - private static void drawLine(PSGenerator gen, - float startx, float starty, float endx, float endy) throws IOException { - gen.writeln(gen.formatDouble(startx) + " " - + gen.formatDouble(starty) + " " + gen.mapCommand("moveto") + " " - + gen.formatDouble(endx) + " " - + gen.formatDouble(endy) + " " + gen.mapCommand("lineto") + " " - + gen.mapCommand("stroke") + " " + gen.mapCommand("newpath")); - } - - /** - * @param gen ps content generator - * @see BorderPainter#drawBorderLine - */ - public static void drawBorderLine( // CSOK: ParameterNumber - PSGenerator gen, - float x1, float y1, float x2, float y2, boolean horz, // CSOK: JavadocMethod - boolean startOrBefore, int style, Color col) // CSOK: JavadocMethod - throws IOException { // CSOK: JavadocMethod - float w = x2 - x1; - float h = y2 - y1; - if ((w < 0) || (h < 0)) { - log.error("Negative extent received. Border won't be painted."); - return; - } - switch (style) { - case Constants.EN_DASHED: - gen.useColor(col); - if (horz) { - float unit = Math.abs(2 * h); - int rep = (int)(w / unit); - if (rep % 2 == 0) { - rep++; - } - unit = w / rep; - gen.useDash("[" + unit + "] 0"); - gen.useLineCap(0); - gen.useLineWidth(h); - float ym = y1 + (h / 2); - drawLine(gen, x1, ym, x2, ym); - } else { - float unit = Math.abs(2 * w); - int rep = (int)(h / unit); - if (rep % 2 == 0) { - rep++; - } - unit = h / rep; - gen.useDash("[" + unit + "] 0"); - gen.useLineCap(0); - gen.useLineWidth(w); - float xm = x1 + (w / 2); - drawLine(gen, xm, y1, xm, y2); - } - break; - case Constants.EN_DOTTED: - gen.useColor(col); - gen.useLineCap(1); //Rounded! - if (horz) { - float unit = Math.abs(2 * h); - int rep = (int)(w / unit); - if (rep % 2 == 0) { - rep++; - } - unit = w / rep; - gen.useDash("[0 " + unit + "] 0"); - gen.useLineWidth(h); - float ym = y1 + (h / 2); - drawLine(gen, x1, ym, x2, ym); - } else { - float unit = Math.abs(2 * w); - int rep = (int)(h / unit); - if (rep % 2 == 0) { - rep++; - } - unit = h / rep; - gen.useDash("[0 " + unit + "] 0"); - gen.useLineWidth(w); - float xm = x1 + (w / 2); - drawLine(gen, xm, y1, xm, y2); - } - break; - case Constants.EN_DOUBLE: - gen.useColor(col); - gen.useDash(null); - if (horz) { - float h3 = h / 3; - gen.useLineWidth(h3); - float ym1 = y1 + (h3 / 2); - float ym2 = ym1 + h3 + h3; - drawLine(gen, x1, ym1, x2, ym1); - drawLine(gen, x1, ym2, x2, ym2); - } else { - float w3 = w / 3; - gen.useLineWidth(w3); - float xm1 = x1 + (w3 / 2); - float xm2 = xm1 + w3 + w3; - drawLine(gen, xm1, y1, xm1, y2); - drawLine(gen, xm2, y1, xm2, y2); - } - break; - case Constants.EN_GROOVE: - case Constants.EN_RIDGE: - float colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); - gen.useDash(null); - if (horz) { - Color uppercol = ColorUtil.lightenColor(col, -colFactor); - Color lowercol = ColorUtil.lightenColor(col, colFactor); - float h3 = h / 3; - gen.useLineWidth(h3); - float ym1 = y1 + (h3 / 2); - gen.useColor(uppercol); - drawLine(gen, x1, ym1, x2, ym1); - gen.useColor(col); - drawLine(gen, x1, ym1 + h3, x2, ym1 + h3); - gen.useColor(lowercol); - drawLine(gen, x1, ym1 + h3 + h3, x2, ym1 + h3 + h3); - } else { - Color leftcol = ColorUtil.lightenColor(col, -colFactor); - Color rightcol = ColorUtil.lightenColor(col, colFactor); - float w3 = w / 3; - gen.useLineWidth(w3); - float xm1 = x1 + (w3 / 2); - gen.useColor(leftcol); - drawLine(gen, xm1, y1, xm1, y2); - gen.useColor(col); - drawLine(gen, xm1 + w3, y1, xm1 + w3, y2); - gen.useColor(rightcol); - drawLine(gen, xm1 + w3 + w3, y1, xm1 + w3 + w3, y2); - } - break; - case Constants.EN_INSET: - case Constants.EN_OUTSET: - colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); - gen.useDash(null); - if (horz) { - Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor); - gen.useLineWidth(h); - float ym1 = y1 + (h / 2); - gen.useColor(c); - drawLine(gen, x1, ym1, x2, ym1); - } else { - Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor); - gen.useLineWidth(w); - float xm1 = x1 + (w / 2); - gen.useColor(c); - drawLine(gen, xm1, y1, xm1, y2); - } - break; - case Constants.EN_HIDDEN: - break; - default: - gen.useColor(col); - gen.useDash(null); - gen.useLineCap(0); - if (horz) { - gen.useLineWidth(h); - float ym = y1 + (h / 2); - drawLine(gen, x1, ym, x2, ym); - } else { - gen.useLineWidth(w); - float xm = x1 + (w / 2); - drawLine(gen, xm, y1, xm, y2); - } - } - } - - /** {@inheritDoc} */ - public void drawLine(Point start, Point end, - int width, Color color, RuleStyle style) throws IOException { - if (start.y != end.y) { - //TODO Support arbitrary lines if necessary - throw new UnsupportedOperationException( - "Can only deal with horizontal lines right now"); - } - - saveGraphicsState(); - int half = width / 2; - int starty = start.y - half; - //Rectangle boundingRect = new Rectangle(start.x, start.y - half, end.x - start.x, width); - - switch (style.getEnumValue()) { - case Constants.EN_SOLID: - case Constants.EN_DASHED: - case Constants.EN_DOUBLE: - drawBorderLine(start.x, starty, end.x, starty + width, - true, true, style.getEnumValue(), color); - break; - case Constants.EN_DOTTED: - clipRect(start.x, starty, end.x - start.x, width); - //This displaces the dots to the right by half a dot's width - //TODO There's room for improvement here - generator.concatMatrix(1, 0, 0, 1, toPoints(half), 0); - drawBorderLine(start.x, starty, end.x, starty + width, - true, true, style.getEnumValue(), color); - break; - case Constants.EN_GROOVE: - case Constants.EN_RIDGE: - generator.useColor(ColorUtil.lightenColor(color, 0.6f)); - moveTo(start.x, starty); - lineTo(end.x, starty); - lineTo(end.x, starty + 2 * half); - lineTo(start.x, starty + 2 * half); - closePath(); - generator.write(" " + generator.mapCommand("fill")); - generator.writeln(" " + generator.mapCommand("newpath")); - generator.useColor(color); - if (style == RuleStyle.GROOVE) { - moveTo(start.x, starty); - lineTo(end.x, starty); - lineTo(end.x, starty + half); - lineTo(start.x + half, starty + half); - lineTo(start.x, starty + 2 * half); - } else { - moveTo(end.x, starty); - lineTo(end.x, starty + 2 * half); - lineTo(start.x, starty + 2 * half); - lineTo(start.x, starty + half); - lineTo(end.x - half, starty + half); - } - closePath(); - generator.write(" " + generator.mapCommand("fill")); - generator.writeln(" " + generator.mapCommand("newpath")); - break; - default: - throw new UnsupportedOperationException("rule style not supported"); - } - - restoreGraphicsState(); - - } - - private static float toPoints(int mpt) { - return mpt / 1000f; - } - - /** {@inheritDoc} */ - protected void moveTo(int x, int y) throws IOException { - generator.writeln(generator.formatDouble(toPoints(x)) + " " - + generator.formatDouble(toPoints(y)) + " " + generator.mapCommand("moveto")); - } - - /** {@inheritDoc} */ - protected void lineTo(int x, int y) throws IOException { - generator.writeln(generator.formatDouble(toPoints(x)) + " " - + generator.formatDouble(toPoints(y)) + " " + generator.mapCommand("lineto")); - } - - /** {@inheritDoc} */ - protected void closePath() throws IOException { - generator.writeln("cp"); - } - - private void clipRect(int x, int y, int width, int height) throws IOException { - generator.defineRect(toPoints(x), toPoints(y), toPoints(width), toPoints(height)); - clip(); - } - - /** {@inheritDoc} */ - protected void clip() throws IOException { - generator.writeln(generator.mapCommand("clip") + " " + generator.mapCommand("newpath")); - } - - /** {@inheritDoc} */ - protected void saveGraphicsState() throws IOException { - generator.saveGraphicsState(); - } - - /** {@inheritDoc} */ - protected void restoreGraphicsState() throws IOException { - generator.restoreGraphicsState(); - } - - - - - /** {@inheritDoc} */ - protected void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) - throws IOException { - StringBuffer sb = new StringBuffer(); - sb.append(generator.formatDouble(toPoints(p1x))); - sb.append(" "); - sb.append(generator.formatDouble(toPoints(p1y))); - sb.append(" "); - sb.append(generator.formatDouble(toPoints(p2x))); - sb.append(" "); - sb.append(generator.formatDouble(toPoints(p2y))); - sb.append(" "); - sb.append(generator.formatDouble(toPoints(p3x))); - sb.append(" "); - sb.append(generator.formatDouble(toPoints(p3y))); - sb.append(" curveto "); - generator.writeln(sb.toString()); - - } - - /** {@inheritDoc} */ - protected void rotateCoordinates(double angle) throws IOException { - StringBuffer sb = new StringBuffer(); - sb.append(generator.formatDouble(angle * 180d / Math.PI)); - sb.append(" "); - sb.append(" rotate "); - generator.writeln(sb.toString()); - } - - /** {@inheritDoc} */ - protected void translateCoordinates(int xTranslate, int yTranslate) throws IOException { - StringBuffer sb = new StringBuffer(); - sb.append(generator.formatDouble(toPoints(xTranslate))); - sb.append(" "); - sb.append(generator.formatDouble(toPoints(yTranslate))); - sb.append(" "); - sb.append(" translate "); - generator.writeln(sb.toString()); - } - - /** {@inheritDoc} */ - protected void scaleCoordinates(float xScale, float yScale) throws IOException { - StringBuffer sb = new StringBuffer(); - sb.append(generator.formatDouble(xScale)); - sb.append(" "); - sb.append(generator.formatDouble(yScale)); - sb.append(" "); - sb.append(" scale "); - generator.writeln(sb.toString()); - } - -} diff --git a/src/java/org/apache/fop/render/ps/PSGraphicsPainter.java b/src/java/org/apache/fop/render/ps/PSGraphicsPainter.java new file mode 100644 index 000000000..e9b4b4ff5 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSGraphicsPainter.java @@ -0,0 +1,387 @@ +/* + * 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.ps; + +import java.awt.Color; +import java.awt.Point; +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.xmlgraphics.ps.PSGenerator; + +import org.apache.fop.fo.Constants; +import org.apache.fop.render.intermediate.ArcToBezierCurveTransformer; +import org.apache.fop.render.intermediate.BezierCurvePainter; +import org.apache.fop.render.intermediate.BorderPainter; +import org.apache.fop.render.intermediate.GraphicsPainter; +import org.apache.fop.traits.RuleStyle; +import org.apache.fop.util.ColorUtil; + +/** + * PostScript-specific implementation of the {@link BorderPainter}. + */ +public class PSGraphicsPainter implements GraphicsPainter, BezierCurvePainter { + + /** logging instance */ + private static Log log = LogFactory.getLog(PSGraphicsPainter.class); + + private final PSGenerator generator; + + /** Used for drawing arcs since PS does not natively support drawing elliptic curves */ + private final ArcToBezierCurveTransformer arcToBezierCurveTransformer; + + /** + * Creates a new border painter for PostScript. + * @param generator the PostScript generator + */ + public PSGraphicsPainter(PSGenerator generator) { + this.generator = generator; + this.arcToBezierCurveTransformer = new ArcToBezierCurveTransformer(this); + } + + /** {@inheritDoc} */ + public void drawBorderLine(int x1, int y1, int x2, int y2, boolean horz, + boolean startOrBefore, int style, Color col) throws IOException { + drawBorderLine(generator, toPoints(x1), toPoints(y1), toPoints(x2), toPoints(y2), + horz, startOrBefore, style, col); + } + + private static void drawLine(PSGenerator gen, + float startx, float starty, float endx, float endy) throws IOException { + gen.writeln(gen.formatDouble(startx) + " " + + gen.formatDouble(starty) + " " + gen.mapCommand("moveto") + " " + + gen.formatDouble(endx) + " " + + gen.formatDouble(endy) + " " + gen.mapCommand("lineto") + " " + + gen.mapCommand("stroke") + " " + gen.mapCommand("newpath")); + } + + /** {@inheritDoc} */ + public static void drawBorderLine(PSGenerator gen, + float x1, float y1, float x2, float y2, boolean horz, + boolean startOrBefore, int style, Color col) throws IOException { + float w = x2 - x1; + float h = y2 - y1; + if ((w < 0) || (h < 0)) { + log.error("Negative extent received. Border won't be painted."); + return; + } + switch (style) { + case Constants.EN_DASHED: + gen.useColor(col); + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int)(w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + gen.useDash("[" + unit + "] 0"); + gen.useLineCap(0); + gen.useLineWidth(h); + float ym = y1 + (h / 2); + drawLine(gen, x1, ym, x2, ym); + } else { + float unit = Math.abs(2 * w); + int rep = (int)(h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + gen.useDash("[" + unit + "] 0"); + gen.useLineCap(0); + gen.useLineWidth(w); + float xm = x1 + (w / 2); + drawLine(gen, xm, y1, xm, y2); + } + break; + case Constants.EN_DOTTED: + gen.useColor(col); + gen.useLineCap(1); //Rounded! + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int)(w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + gen.useDash("[0 " + unit + "] 0"); + gen.useLineWidth(h); + float ym = y1 + (h / 2); + drawLine(gen, x1, ym, x2, ym); + } else { + float unit = Math.abs(2 * w); + int rep = (int)(h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + gen.useDash("[0 " + unit + "] 0"); + gen.useLineWidth(w); + float xm = x1 + (w / 2); + drawLine(gen, xm, y1, xm, y2); + } + break; + case Constants.EN_DOUBLE: + gen.useColor(col); + gen.useDash(null); + if (horz) { + float h3 = h / 3; + gen.useLineWidth(h3); + float ym1 = y1 + (h3 / 2); + float ym2 = ym1 + h3 + h3; + drawLine(gen, x1, ym1, x2, ym1); + drawLine(gen, x1, ym2, x2, ym2); + } else { + float w3 = w / 3; + gen.useLineWidth(w3); + float xm1 = x1 + (w3 / 2); + float xm2 = xm1 + w3 + w3; + drawLine(gen, xm1, y1, xm1, y2); + drawLine(gen, xm2, y1, xm2, y2); + } + break; + case Constants.EN_GROOVE: + case Constants.EN_RIDGE: + float colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); + gen.useDash(null); + if (horz) { + Color uppercol = ColorUtil.lightenColor(col, -colFactor); + Color lowercol = ColorUtil.lightenColor(col, colFactor); + float h3 = h / 3; + gen.useLineWidth(h3); + float ym1 = y1 + (h3 / 2); + gen.useColor(uppercol); + drawLine(gen, x1, ym1, x2, ym1); + gen.useColor(col); + drawLine(gen, x1, ym1 + h3, x2, ym1 + h3); + gen.useColor(lowercol); + drawLine(gen, x1, ym1 + h3 + h3, x2, ym1 + h3 + h3); + } else { + Color leftcol = ColorUtil.lightenColor(col, -colFactor); + Color rightcol = ColorUtil.lightenColor(col, colFactor); + float w3 = w / 3; + gen.useLineWidth(w3); + float xm1 = x1 + (w3 / 2); + gen.useColor(leftcol); + drawLine(gen, xm1, y1, xm1, y2); + gen.useColor(col); + drawLine(gen, xm1 + w3, y1, xm1 + w3, y2); + gen.useColor(rightcol); + drawLine(gen, xm1 + w3 + w3, y1, xm1 + w3 + w3, y2); + } + break; + case Constants.EN_INSET: + case Constants.EN_OUTSET: + colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); + gen.useDash(null); + if (horz) { + Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor); + gen.useLineWidth(h); + float ym1 = y1 + (h / 2); + gen.useColor(c); + drawLine(gen, x1, ym1, x2, ym1); + } else { + Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor); + gen.useLineWidth(w); + float xm1 = x1 + (w / 2); + gen.useColor(c); + drawLine(gen, xm1, y1, xm1, y2); + } + break; + case Constants.EN_HIDDEN: + break; + default: + gen.useColor(col); + gen.useDash(null); + gen.useLineCap(0); + if (horz) { + gen.useLineWidth(h); + float ym = y1 + (h / 2); + drawLine(gen, x1, ym, x2, ym); + } else { + gen.useLineWidth(w); + float xm = x1 + (w / 2); + drawLine(gen, xm, y1, xm, y2); + } + } + } + + /** {@inheritDoc} */ + public void drawLine(Point start, Point end, + int width, Color color, RuleStyle style) throws IOException { + if (start.y != end.y) { + //TODO Support arbitrary lines if necessary + throw new UnsupportedOperationException( + "Can only deal with horizontal lines right now"); + } + + saveGraphicsState(); + int half = width / 2; + int starty = start.y - half; + //Rectangle boundingRect = new Rectangle(start.x, start.y - half, end.x - start.x, width); + + switch (style.getEnumValue()) { + case Constants.EN_SOLID: + case Constants.EN_DASHED: + case Constants.EN_DOUBLE: + drawBorderLine(start.x, starty, end.x, starty + width, + true, true, style.getEnumValue(), color); + break; + case Constants.EN_DOTTED: + clipRect(start.x, starty, end.x - start.x, width); + //This displaces the dots to the right by half a dot's width + //TODO There's room for improvement here + generator.concatMatrix(1, 0, 0, 1, toPoints(half), 0); + drawBorderLine(start.x, starty, end.x, starty + width, + true, true, style.getEnumValue(), color); + break; + case Constants.EN_GROOVE: + case Constants.EN_RIDGE: + generator.useColor(ColorUtil.lightenColor(color, 0.6f)); + moveTo(start.x, starty); + lineTo(end.x, starty); + lineTo(end.x, starty + 2 * half); + lineTo(start.x, starty + 2 * half); + closePath(); + generator.write(" " + generator.mapCommand("fill")); + generator.writeln(" " + generator.mapCommand("newpath")); + generator.useColor(color); + if (style == RuleStyle.GROOVE) { + moveTo(start.x, starty); + lineTo(end.x, starty); + lineTo(end.x, starty + half); + lineTo(start.x + half, starty + half); + lineTo(start.x, starty + 2 * half); + } else { + moveTo(end.x, starty); + lineTo(end.x, starty + 2 * half); + lineTo(start.x, starty + 2 * half); + lineTo(start.x, starty + half); + lineTo(end.x - half, starty + half); + } + closePath(); + generator.write(" " + generator.mapCommand("fill")); + generator.writeln(" " + generator.mapCommand("newpath")); + break; + default: + throw new UnsupportedOperationException("rule style not supported"); + } + + restoreGraphicsState(); + + } + + private static float toPoints(int mpt) { + return mpt / 1000f; + } + + /** {@inheritDoc} */ + public void moveTo(int x, int y) throws IOException { + generator.writeln(generator.formatDouble(toPoints(x)) + " " + + generator.formatDouble(toPoints(y)) + " " + generator.mapCommand("moveto")); + } + + /** {@inheritDoc} */ + public void lineTo(int x, int y) throws IOException { + generator.writeln(generator.formatDouble(toPoints(x)) + " " + + generator.formatDouble(toPoints(y)) + " " + generator.mapCommand("lineto")); + } + + /** {@inheritDoc} */ + public void arcTo(final double startAngle, final double endAngle, final int cx, final int cy, + final int width, final int height) throws IOException { + arcToBezierCurveTransformer.arcTo(startAngle, endAngle, cx, cy, width, height); + } + + /** {@inheritDoc} */ + public void closePath() throws IOException { + generator.writeln("cp"); + } + + private void clipRect(int x, int y, int width, int height) throws IOException { + generator.defineRect(toPoints(x), toPoints(y), toPoints(width), toPoints(height)); + clip(); + } + + /** {@inheritDoc} */ + public void clip() throws IOException { + generator.writeln(generator.mapCommand("clip") + " " + generator.mapCommand("newpath")); + } + + /** {@inheritDoc} */ + public void saveGraphicsState() throws IOException { + generator.saveGraphicsState(); + } + + /** {@inheritDoc} */ + public void restoreGraphicsState() throws IOException { + generator.restoreGraphicsState(); + } + + /** {@inheritDoc} */ + public void rotateCoordinates(double angle) throws IOException { + StringBuffer sb = new StringBuffer() + .append(generator.formatDouble(angle * 180d / Math.PI)) + .append(" rotate "); + generator.writeln(sb.toString()); + } + + /** {@inheritDoc} */ + public void translateCoordinates(int xTranslate, int yTranslate) throws IOException { + StringBuffer sb = new StringBuffer() + .append(generator.formatDouble(toPoints(xTranslate))) + .append(" ") + .append(generator.formatDouble(toPoints(yTranslate))) + .append(" translate "); + generator.writeln(sb.toString()); + } + + /** {@inheritDoc} */ + public void scaleCoordinates(float xScale, float yScale) throws IOException { + StringBuffer sb = new StringBuffer() + .append(generator.formatDouble(xScale)) + .append(" ") + .append(generator.formatDouble(yScale)) + .append(" scale "); + generator.writeln(sb.toString()); + } + + /** {@inheritDoc} */ + public void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) + throws IOException { + StringBuffer sb = new StringBuffer() + .append(generator.formatDouble(toPoints(p1x))) + .append(" ") + .append(generator.formatDouble(toPoints(p1y))) + .append(" ") + .append(generator.formatDouble(toPoints(p2x))) + .append(" ") + .append(generator.formatDouble(toPoints(p2y))) + .append(" ") + .append(generator.formatDouble(toPoints(p3x))) + .append(" ") + .append(generator.formatDouble(toPoints(p3y))) + .append(" curveto "); + generator.writeln(sb.toString()); + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java index b8947ff60..97bf7e647 100644 --- a/src/java/org/apache/fop/render/ps/PSPainter.java +++ b/src/java/org/apache/fop/render/ps/PSPainter.java @@ -48,6 +48,8 @@ import org.apache.fop.fonts.SingleByteFont; import org.apache.fop.fonts.Typeface; import org.apache.fop.render.RenderingContext; import org.apache.fop.render.intermediate.AbstractIFPainter; +import org.apache.fop.render.intermediate.BorderPainter; +import org.apache.fop.render.intermediate.GraphicsPainter; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFState; import org.apache.fop.render.intermediate.IFUtil; @@ -64,7 +66,9 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> { /** logging instance */ private static Log log = LogFactory.getLog(PSPainter.class); - private PSBorderPainter borderPainter; + private final GraphicsPainter graphicsPainter; + + private BorderPainter borderPainter; private boolean inTextMode = false; @@ -78,7 +82,8 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> { protected PSPainter(PSDocumentHandler documentHandler, IFState state) { super(documentHandler); - this.borderPainter = new PSBorderPainter(getGenerator()); + this.graphicsPainter = new PSGraphicsPainter(getGenerator()); + this.borderPainter = new BorderPainter(graphicsPainter); this.state = state; } @@ -260,7 +265,7 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> { throws IFException { try { endTextObject(); - this.borderPainter.drawLine(start, end, width, color, style); + this.graphicsPainter.drawLine(start, end, width, color, style); } catch (IOException ioe) { throw new IFException("I/O error in drawLine()", ioe); } diff --git a/src/java/org/apache/fop/traits/BorderProps.java b/src/java/org/apache/fop/traits/BorderProps.java index 3eca9c43f..61a7d86b6 100644 --- a/src/java/org/apache/fop/traits/BorderProps.java +++ b/src/java/org/apache/fop/traits/BorderProps.java @@ -34,52 +34,75 @@ import org.apache.fop.util.ColorUtil; */ public class BorderProps implements Serializable { - private static final long serialVersionUID = -886871454032189183L; + private static final long serialVersionUID = 7053576586478548795L; - /** Separate border model */ - public static final int SEPARATE = 0; - /** Collapsing border model, for borders inside a table */ - public static final int COLLAPSE_INNER = 1; - /** Collapsing border model, for borders at the table's outer border */ - public static final int COLLAPSE_OUTER = 2; + public enum Mode { + SEPARATE("separate") { + @Override + int getClippedWidth(BorderProps bp) { + return 0; + } + }, + COLLAPSE_INNER("collapse-inner"), // for borders inside a table + COLLAPSE_OUTER("collapse-outer"); // for borders at the table's outer border + + private final String value; + + Mode(String value) { + this.value = value; + } + + int getClippedWidth(BorderProps bp) { + return bp.width / 2; + }; + } /** Border style (one of EN_*) */ - public int style; // Enum for border style // CSOK: VisibilityModifier + public final int style; // Enum for border style // CSOK: VisibilityModifier /** Border color */ - public Color color; // CSOK: VisibilityModifier + public final Color color; // CSOK: VisibilityModifier + /** Border width */ - public int width; // CSOK: VisibilityModifier + public final int width; // CSOK: VisibilityModifier - private int radiusStart = 0; - - private int radiusEnd = 0; - - /** Border mode (one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER) */ - public int mode; // CSOK: VisibilityModifier + private final int radiusStart; + + private final int radiusEnd; + + /** Border mode */ + private final Mode mode; // CSOK: VisibilityModifier /** * Constructs a new BorderProps instance. * @param style border style (one of EN_*) * @param width border width + * @param radiusStart radius of start corner in the direction perpendicular to border segment + * @param radiusEnd radius of end corner in the direction perpendicular to border segment * @param color border color * @param mode border mode ((one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER) */ - public BorderProps(int style, int width, Color color, int mode) { + public BorderProps(int style, int width, int radiusStart, int radiusEnd, Color color, Mode mode) { this.style = style; this.width = width; + this.radiusStart = radiusStart; + this.radiusEnd = radiusEnd; this.color = color; this.mode = mode; } /** - * Constructs a new BorderProps instance. - * @param style border style (one of the XSL enum values for border style) + * Factory method for a new BorderProps instance with rectangular corners. + * @param style border style (one of EN_*) * @param width border width * @param color border color * @param mode border mode ((one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER) */ - public BorderProps(String style, int width, Color color, int mode) { - this(getConstantForStyle(style), width, color, mode); + public static BorderProps makeRectangular(int style, int width, Color color, Mode mode) { + return new BorderProps(style, width, 0, 0, color, mode); + } + + private BorderProps(String style, int width, int radiusStart, int radiusEnd, Color color, Mode mode) { + this(getConstantForStyle(style), width, radiusStart, radiusEnd, color, mode); } /** @@ -91,14 +114,6 @@ public class BorderProps implements Serializable { } /** - * - * @param radiusStart the radius of the corner adjacent to the before or start border - */ - public void setRadiusStart(int radiusStart) { - this.radiusStart = radiusStart; - } - - /** * @return the radius of the corner adjacent to the after or end border */ public int getRadiusEnd() { @@ -106,23 +121,11 @@ public class BorderProps implements Serializable { } /** - * - * @param radiusEnd the radius of the corner adjacent to the after or end border - */ - public void setRadiusEnd(int radiusEnd) { - this.radiusEnd = radiusEnd; - } - - /** * @param bp the border properties or null * @return the effective width of the clipped part of the border */ public static int getClippedWidth(BorderProps bp) { - if ((bp != null) && (bp.mode != SEPARATE)) { - return bp.width / 2; - } else { - return 0; - } + return bp == null ? 0 : bp.mode.getClippedWidth(bp); } private String getStyleString() { @@ -133,6 +136,10 @@ public class BorderProps implements Serializable { return BorderStyle.valueOf(style).getEnumValue(); } + public boolean isCollapseOuter() { + return mode == Mode.COLLAPSE_OUTER; + } + /** {@inheritDoc} */ @Override public int hashCode() { @@ -169,83 +176,79 @@ public class BorderProps implements Serializable { * @return a BorderProps instance */ public static BorderProps valueOf(FOUserAgent foUserAgent, String s) { - if (s.startsWith("(") && s.endsWith(")")) { - s = s.substring(1, s.length() - 1); - Pattern pattern = Pattern.compile("([^,\\(]+(?:\\(.*\\))?)"); - Matcher m = pattern.matcher(s); - boolean found; - found = m.find(); - String style = m.group(); - found = m.find(); - String color = m.group(); - found = m.find(); - int width = Integer.parseInt(m.group()); - int mode = SEPARATE; - found = m.find(); - if (found) { - String ms = m.group(); - if ("collapse-inner".equalsIgnoreCase(ms)) { - mode = COLLAPSE_INNER; - } else if ("collapse-outer".equalsIgnoreCase(ms)) { - mode = COLLAPSE_OUTER; - } - } - Color c; - try { - c = ColorUtil.parseColorString(foUserAgent, color); - } catch (PropertyException e) { - throw new IllegalArgumentException(e.getMessage()); - } - - BorderProps bp = new BorderProps(style, width, c, mode); - - found = m.find(); - if (found) { - int startRadius = Integer.parseInt(m.group()); - m.find(); - int endRadius = Integer.parseInt(m.group()); - bp.setRadiusStart(startRadius); - bp.setRadiusEnd(endRadius); - } - - return bp; - } else { - throw new IllegalArgumentException("BorderProps must be surrounded by parentheses"); - } + return BorderPropsDeserializer.INSTANCE.valueOf(foUserAgent, s); } - /** {@inheritDoc} */ @Override public String toString() { StringBuffer sbuf = new StringBuffer(); - sbuf.append('('); - sbuf.append(getStyleString()); - sbuf.append(','); - sbuf.append(ColorUtil.colorToString(color)); - sbuf.append(','); - sbuf.append(width); - if (mode != SEPARATE) { - if (mode == COLLAPSE_INNER) { - sbuf.append(",collapse-inner"); - } else { - sbuf.append(",collapse-outer"); - } + sbuf.append('(') + .append(getStyleString()).append(',') + .append(ColorUtil.colorToString(color)).append(',') + .append(width); + if (!mode.equals(Mode.SEPARATE)) { + sbuf.append(",").append(mode.value); } if (radiusStart != 0 || radiusEnd != 0) { - if (mode == SEPARATE) { + if (mode.equals(Mode.SEPARATE)) { // Because of the corner radii properties the mode must be set // so that the parameter index is consistent - sbuf.append(",separate"); + sbuf.append(",").append(Mode.SEPARATE.value); } - sbuf.append(','); - sbuf.append(radiusStart); - - sbuf.append(','); - sbuf.append(radiusEnd); + sbuf.append(',').append(radiusStart) + .append(',').append(radiusEnd); } sbuf.append(')'); return sbuf.toString(); } + private static class BorderPropsDeserializer { + + private static final BorderPropsDeserializer INSTANCE = new BorderPropsDeserializer(); + + private static final Pattern PATTERN = Pattern.compile("([^,\\(]+(?:\\(.*\\))?)"); + + private BorderPropsDeserializer() { + } + + public BorderProps valueOf(FOUserAgent foUserAgent, String s) { + if (s.startsWith("(") && s.endsWith(")")) { + s = s.substring(1, s.length() - 1); + Matcher m = PATTERN.matcher(s); + m.find(); + String style = m.group(); + m.find(); + String color = m.group(); + m.find(); + int width = Integer.parseInt(m.group()); + Mode mode = Mode.SEPARATE; + if ( m.find()) { + String ms = m.group(); + if (Mode.COLLAPSE_INNER.value.equalsIgnoreCase(ms)) { + mode = Mode.COLLAPSE_INNER; + } else if (Mode.COLLAPSE_OUTER.value.equalsIgnoreCase(ms)) { + mode = Mode.COLLAPSE_OUTER; + } + } + Color c; + try { + c = ColorUtil.parseColorString(foUserAgent, color); + } catch (PropertyException e) { + throw new IllegalArgumentException(e.getMessage()); + } + int startRadius = 0; + int endRadius = 0; + if (m.find()) { + startRadius = Integer.parseInt(m.group()); + m.find(); + endRadius = Integer.parseInt(m.group()); + } + return new BorderProps(style, width, startRadius, endRadius, c, mode); + } else { + throw new IllegalArgumentException("BorderProps must be surrounded by parentheses"); + } + } + } + } |