aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/java/org/apache/fop/layoutmgr/TraitSetter.java34
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java4
-rw-r--r--src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java29
-rw-r--r--src/java/org/apache/fop/render/afp/AFPPainter.java325
-rw-r--r--src/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformer.java102
-rw-r--r--src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java37
-rw-r--r--src/java/org/apache/fop/render/intermediate/BorderPainter.java998
-rw-r--r--src/java/org/apache/fop/render/intermediate/GraphicsPainter.java145
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java357
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java333
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DPainter.java15
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFBorderPainter.java369
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java499
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFPainter.java15
-rw-r--r--src/java/org/apache/fop/render/ps/PSBorderPainter.java387
-rw-r--r--src/java/org/apache/fop/render/ps/PSGraphicsPainter.java387
-rw-r--r--src/java/org/apache/fop/render/ps/PSPainter.java11
-rw-r--r--src/java/org/apache/fop/traits/BorderProps.java213
-rw-r--r--test/java/org/apache/fop/render/afp/AFPPainterTestCase.java131
-rw-r--r--test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java10
-rw-r--r--test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java79
-rw-r--r--test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java565
-rw-r--r--test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java170
-rw-r--r--test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java87
-rw-r--r--test/java/org/apache/fop/render/ps/PSPainterTestCase.java44
-rw-r--r--test/java/org/apache/fop/traits/BorderPropsTestCase.java44
26 files changed, 3439 insertions, 1951 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");
+ }
+ }
+ }
+
}
diff --git a/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java b/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java
new file mode 100644
index 000000000..fd6209bf1
--- /dev/null
+++ b/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java
@@ -0,0 +1,131 @@
+/*
+ * 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.afp;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.util.Map;
+
+import org.junit.Test;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.impl.DefaultImageContext;
+import org.apache.xmlgraphics.image.loader.impl.DefaultImageSessionContext;
+import org.apache.xmlgraphics.image.loader.impl.ImageBuffered;
+
+import org.apache.fop.afp.AFPEventProducer;
+import org.apache.fop.afp.AFPPaintingState;
+import org.apache.fop.afp.AFPResourceManager;
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.render.ImageHandlerRegistry;
+import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.traits.BorderProps;
+
+public class AFPPainterTestCase {
+
+ @Test
+ public void testDrawBorderRect() {
+ // the goal of this test is to check that the drawing of rounded corners in AFP uses a bitmap of the
+ // rounded corners (in fact the whole rectangle with rounded corners). the check is done by verifying
+ // that the AFPImageHandlerRenderedImage.handleImage() method is called
+ // mock
+ AFPPaintingState afpPaintingState = mock(AFPPaintingState.class);
+ when(afpPaintingState.getResolution()).thenReturn(72);
+ // mock
+ EventBroadcaster eventBroadcaster = mock(EventBroadcaster.class);
+ // mock
+ DefaultImageContext defaultImageContext = mock(DefaultImageContext.class);
+ when(defaultImageContext.getSourceResolution()).thenReturn(72000f);
+ // mock
+ DefaultImageSessionContext defaultImageSessionContxt = mock(DefaultImageSessionContext.class);
+ when(defaultImageSessionContxt.getParentContext()).thenReturn(defaultImageContext);
+ when(defaultImageSessionContxt.getTargetResolution()).thenReturn(72000f);
+ // mock
+ ImageBuffered imageBuffered = mock(ImageBuffered.class);
+ // mock
+ ImageManager imageManager = mock(ImageManager.class);
+ // mock
+ AFPImageHandlerRenderedImage afpImageHandlerRenderedImage = mock(AFPImageHandlerRenderedImage.class);
+ // mock
+ ImageHandlerRegistry imageHandlerRegistry = mock(ImageHandlerRegistry.class);
+ when(imageHandlerRegistry.getHandler(any(AFPRenderingContext.class), any(Image.class))).thenReturn(
+ afpImageHandlerRenderedImage);
+ // mock
+ FOUserAgent foUserAgent = mock(FOUserAgent.class);
+ when(foUserAgent.getEventBroadcaster()).thenReturn(eventBroadcaster);
+ when(foUserAgent.getImageSessionContext()).thenReturn(defaultImageSessionContxt);
+ when(foUserAgent.getImageManager()).thenReturn(imageManager);
+ when(foUserAgent.getImageHandlerRegistry()).thenReturn(imageHandlerRegistry);
+ // mock
+ AFPEventProducer afpEventProducer = mock(AFPEventProducer.class);
+ when(AFPEventProducer.Provider.get(eventBroadcaster)).thenReturn(afpEventProducer);
+ // mock
+ AFPResourceManager afpResourceManager = mock(AFPResourceManager.class);
+ when(afpResourceManager.isObjectCached(null)).thenReturn(false);
+ // mock
+ IFContext ifContext = mock(IFContext.class);
+ when(ifContext.getUserAgent()).thenReturn(foUserAgent);
+ // mock
+ AFPDocumentHandler afpDocumentHandler = mock(AFPDocumentHandler.class);
+ when(afpDocumentHandler.getPaintingState()).thenReturn(afpPaintingState);
+ when(afpDocumentHandler.getContext()).thenReturn(ifContext);
+ when(afpDocumentHandler.getResourceManager()).thenReturn(afpResourceManager);
+ when(afpDocumentHandler.cacheRoundedCorner("a2a48964ba2d")).thenReturn("RC000000");
+ // real instance, no mock
+ AFPPainter afpPainter = new AFPPainter(afpDocumentHandler);
+ // build rectangle 200 x 50 (points, which are converted to millipoints)
+ Rectangle rectangle = new Rectangle(0, 0, 200000, 50000);
+ // build border properties
+ int style = Constants.EN_SOLID;
+ BorderProps.Mode mode = BorderProps.Mode.SEPARATE;
+ Color color = Color.BLACK;
+ int borderWidth = 4000;
+ int radiusStart = 30000;
+ int radiusEnd = 30000;
+ BorderProps border1 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ BorderProps border2 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ BorderProps border3 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ BorderProps border4 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ try {
+ when(imageManager.convertImage(any(Image.class), any(ImageFlavor[].class), any(Map.class)))
+ .thenReturn(imageBuffered);
+ afpPainter.drawBorderRect(rectangle, border1, border2, border3, border4, Color.WHITE);
+ // note: here we would really like to verify that the second and third arguments passed to
+ // handleImage() are the instances ib and rect declared above but that causes mockito to throw
+ // an exception, probably because we cannot declare the AFPRenderingContext and are forced to
+ // use any(), which forces the use of any() for all arguments
+ verify(afpImageHandlerRenderedImage).handleImage(any(AFPRenderingContext.class),
+ any(Image.class), any(Rectangle.class));
+ } catch (Exception e) {
+ fail("something broke...");
+ }
+ }
+
+}
diff --git a/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java b/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java
index 0f3e6e847..592335648 100644
--- a/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java
+++ b/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java
@@ -60,6 +60,10 @@ public class AbstractIFPainterTestCase {
public void clipRect(Rectangle rect) throws IFException {
}
+ public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
+ BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
+ }
+
public void fillRect(Rectangle rect, Paint fill) throws IFException {
}
@@ -78,12 +82,6 @@ public class AbstractIFPainterTestCase {
String text) throws IFException {
}
- public void clipBackground(Rectangle rect, BorderProps bpsBefore,
- BorderProps bpsAfter, BorderProps bpsStart,
- BorderProps bpsEnd) throws IFException {
- // TODO Auto-generated method stub
- throw new UnsupportedOperationException();
- }
};
FontInfo fontInfo = mock(FontInfo.class);
when(handler.getFontInfo()).thenReturn(fontInfo);
diff --git a/test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java b/test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java
new file mode 100644
index 000000000..61093c629
--- /dev/null
+++ b/test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package org.apache.fop.render.intermediate;
+
+import java.io.IOException;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class ArcToBezierCurveTransformerTestCase {
+
+ @Test
+ public void arcTo() throws Exception {
+ testArcTo(Math.PI / 3, Math.PI / 2, 100, 200, 1000, 1000);
+ }
+
+ private void testArcTo(double startAngle, double endAngle, int xCenter, int yCenter, int width,
+ int height) throws IOException {
+ assertAngleWithinFirstQuadrant(startAngle);
+ assertAngleWithinFirstQuadrant(endAngle);
+ BezierCurvePainter bezierCurvePainter = mock(BezierCurvePainter.class);
+ ArcToBezierCurveTransformer sut = new ArcToBezierCurveTransformer(bezierCurvePainter);
+ sut.arcTo(startAngle, endAngle, xCenter, yCenter, width, height);
+ double tan1 = Math.tan(startAngle);
+ double tan2 = Math.tan(endAngle);
+ double lambda1 = Math.atan(height * tan1 / width);
+ double lambda2 = Math.atan(height * tan2 / width);
+ double xStart = width * Math.cos(lambda1) + xCenter;
+ double yStart = height * Math.sin(lambda1) + yCenter;
+ double xEnd = width * Math.cos(lambda2) + xCenter;
+ double yEnd = height * Math.sin(lambda2) + yCenter;
+ ArgumentCaptor<Integer> xP1Captor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> yP1Captor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> xP2Captor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> yP2Captor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> xP3Captor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> yP3Captor = ArgumentCaptor.forClass(Integer.class);
+ verify(bezierCurvePainter).cubicBezierTo(xP1Captor.capture(), yP1Captor.capture(),
+ xP2Captor.capture(), yP2Captor.capture(), xP3Captor.capture(), yP3Captor.capture());
+ int xP1 = xP1Captor.getValue();
+ int yP1 = yP1Captor.getValue();
+ int xP2 = xP2Captor.getValue();
+ int yP2 = yP2Captor.getValue();
+ int xP3 = xP3Captor.getValue();
+ int yP3 = yP3Captor.getValue();
+ // TODO do more than check the direction of the tangents at the end
+ // points
+ assertEquals((yP1 - yStart) / (xP1 - xStart), -width * width / height / height / tan1, 0.01);
+ assertEquals((yP2 - yEnd) / (xP2 - xEnd), -width * width / height / height / tan2, 0.01);
+ assertEquals((int) xEnd, xP3);
+ assertEquals((int) yEnd, yP3);
+ }
+
+ private void assertAngleWithinFirstQuadrant(double angle) {
+ if (angle <= 0 || angle > Math.PI / 2) {
+ fail("Angle " + angle + " is in (0, " + Math.PI / 2 + ")");
+ }
+ }
+} \ No newline at end of file
diff --git a/test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java b/test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java
new file mode 100644
index 000000000..fab6e0f4c
--- /dev/null
+++ b/test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java
@@ -0,0 +1,565 @@
+/*
+ * 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.
+ */
+
+package org.apache.fop.render.intermediate;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.io.IOException;
+
+import org.junit.Test;
+
+import org.apache.fop.fo.Constants;
+import org.apache.fop.traits.BorderProps;
+import org.apache.fop.traits.BorderProps.Mode;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+public class BorderPainterTestCase {
+
+ private static final BorderProps BORDER_PROPS = new BorderProps(Constants.EN_SOLID, 10, 50, 50,
+ Color.BLACK, BorderProps.Mode.SEPARATE);
+
+ @Test
+ public void clipBackground() throws Exception {
+ // Rectangular borders
+ test(new ClipBackgroundTester(0, 0, 10, 10));
+ test(new ClipBackgroundTester(5, 10, 10, 10));
+ test(new ClipBackgroundTester(0, 0, 10, 10).setBorderWidth(1));
+ test(new ClipBackgroundTester(0, 0, 10, 10).beforeBorder().setWidth(10).tester());
+ // Rounded corners
+ test(new ClipBackgroundTester(0, 0, 10, 10).setEndBefore(1, 1));
+ test(new ClipBackgroundTester(0, 0, 10, 10).setEndAfter(1, 1));
+ test(new ClipBackgroundTester(0, 0, 10, 10).setStartAfter(1, 1));
+ test(new ClipBackgroundTester(0, 0, 10, 10).setStartBefore(1, 1));
+ test(new ClipBackgroundTester(0, 0, 100, 100)
+ .setCornerRadii(10)
+ .beforeBorder().setWidth(5).tester()
+ .startBorder().setWidth(5).tester());
+ test(new ClipBackgroundTester(0, 0, 100, 100)
+ .setCornerRadii(10)
+ .beforeBorder().setWidth(10).tester()
+ .startBorder().setWidth(10).tester());
+ test(new ClipBackgroundTester(0, 0, 100, 100)
+ .setCornerRadii(10)
+ .beforeBorder().setWidth(5).tester());
+ test(new ClipBackgroundTester(0, 0, 100, 100)
+ .setCornerRadii(10)
+ .setStartBefore(10, 10)
+ .beforeBorder().setWidth(10).tester());
+ }
+
+ private void test(BorderPainterTester<?> tester) throws IOException {
+ tester.test();
+ }
+
+ @Test (expected = IFException.class)
+ public void drawBordersThrowsIFException() throws Exception {
+ GraphicsPainter graphicsPainter = mock(GraphicsPainter.class);
+ doThrow(new IOException()).when(graphicsPainter).saveGraphicsState();
+ new BorderPainter(graphicsPainter).drawBorders(new Rectangle(0, 0, 1000, 1000), BORDER_PROPS,
+ BORDER_PROPS, BORDER_PROPS, BORDER_PROPS, Color.WHITE);
+ }
+
+ @Test
+ public void testDrawRectangularBorders() throws IOException {
+ test(new DrawRectangularBordersTester(0, 0, 1000, 1000).setBorderWidth(10));
+ test(new DrawRectangularBordersTester(0, 0, 1000, 1000));
+ test(new DrawRectangularBordersTester(0, 0, 1000, 1000).setBorderWidth(10)
+ .beforeBorder().setWidth(0).tester());
+ }
+
+ @Test
+ public void testDrawRectangularBordersWithNullBorders() throws IOException, IFException {
+ GraphicsPainter graphicsPainter = mock(GraphicsPainter.class);
+ BorderProps nullBorderProps = null;
+ new BorderPainter(graphicsPainter).drawRectangularBorders(new Rectangle(0, 0, 1000, 1000),
+ nullBorderProps, nullBorderProps, nullBorderProps, nullBorderProps);
+ verifyZeroInteractions(graphicsPainter);
+ }
+
+ @Test
+ public void drawRoundedBorders() throws Exception {
+ test(new DrawRoundedBordersTester(0, 0, 10, 10).setBorderWidth(10));
+ test(new DrawRoundedBordersTester(0, 0, 10, 10).beforeBorder().setWidth(10).tester());
+ test(new DrawRoundedBordersTester(0, 0, 10, 10).setBorderWidth(10).setCornerRadii(5)
+ .beforeBorder().setWidth(0).tester());
+ test(new DrawRoundedBordersTester(0, 0, 10, 10)
+ .beforeBorder().setWidth(10).tester().endBorder().setWidth(10).tester());
+ test(new DrawRoundedBordersTester(0, 0, 100, 100).setBorderWidth(15).setCornerRadii(10));
+ test(new DrawRoundedBordersTester(0, 0, 100, 100).setBorderWidth(15).setCornerRadii(10)
+ .beforeBorder().setWidth(5).tester());
+ test(new DrawRoundedBordersTester(0, 0, 60, 60).setBorderWidth(4).setCornerRadii(30));
+ }
+
+ @Test
+ public void testDrawRoundedBordersWithNullBorders() throws IOException, IFException {
+ GraphicsPainter graphicsPainter = mock(GraphicsPainter.class);
+ BorderProps nullBorderProps = null;
+ new BorderPainter(graphicsPainter).drawRoundedBorders(new Rectangle(0, 0, 1000, 1000),
+ nullBorderProps, nullBorderProps, nullBorderProps, nullBorderProps);
+ verifyZeroInteractions(graphicsPainter);
+ }
+
+ @Test
+ public void testCalculateCornerCorrectionFactor() {
+ calculateCornerCorrectionFactorHelper(30000, 500000);
+ calculateCornerCorrectionFactorHelper(30000, 10000);
+ }
+
+ private void calculateCornerCorrectionFactorHelper(int radius, int rectWidth) {
+ BorderProps borderProps = new BorderProps(Constants.EN_SOLID, 4000, radius, radius, Color.BLACK,
+ BorderProps.Mode.SEPARATE);
+ int rectHeight = rectWidth + 100;
+ double expected = (2 * radius > rectWidth) ? (double) rectWidth / (2 * radius) : 1.0;
+ double actual = BorderPainter.calculateCornerCorrectionFactor(rectWidth, rectHeight, borderProps,
+ borderProps, borderProps, borderProps);
+ assertEquals(expected, actual, 0);
+ }
+
+ private abstract static class BorderPainterTester<T extends BorderPainterTester<?>> {
+
+ protected final Rectangle borderExtent;
+
+ protected BorderProps before;
+
+ protected BorderProps after;
+
+ protected BorderProps start;
+
+ protected BorderProps end;
+
+ protected final GraphicsPainter graphicsPainter;
+
+ protected final BorderPainter sut;
+
+ private final T thisInstance;
+
+ private final BorderPropsBuilder<T> beforeBuilder;
+
+ private final BorderPropsBuilder<T> afterBuilder;
+
+ private final BorderPropsBuilder<T> startBuilder;
+
+ private final BorderPropsBuilder<T> endBuilder;
+
+ public BorderPainterTester(int xOrigin, int yOrigin, int width, int height) {
+ this.thisInstance = (T) this;
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("Cannot test degenerate borders");
+ }
+ beforeBuilder = new BorderPropsBuilder<T>(this.thisInstance);
+ afterBuilder = new BorderPropsBuilder<T>(this.thisInstance);
+ startBuilder = new BorderPropsBuilder<T>(this.thisInstance);
+ endBuilder = new BorderPropsBuilder<T>(this.thisInstance);
+ this.borderExtent = new Rectangle(xOrigin, yOrigin, width, height);
+ this.graphicsPainter = mock(GraphicsPainter.class);
+ this.sut = new BorderPainter(graphicsPainter);
+ }
+
+ public BorderPropsBuilder<T> beforeBorder() {
+ return beforeBuilder;
+ }
+
+ public BorderPropsBuilder<T> afterBorder() {
+ return afterBuilder;
+ }
+
+ public BorderPropsBuilder<T> startBorder() {
+ return startBuilder;
+ }
+
+ public BorderPropsBuilder<T> endBorder() {
+ return endBuilder;
+ }
+
+ public T setBorderWidth(int width) {
+ beforeBuilder.setWidth(width);
+ endBuilder.setWidth(width);
+ afterBuilder.setWidth(width);
+ startBuilder.setWidth(width);
+ return thisInstance;
+ }
+
+ public T setCornerRadii(int radius) {
+ return setCornerRadii(radius, radius);
+ }
+
+ public T setCornerRadii(int xRadius, int yRadius) {
+ setStartBefore(xRadius, yRadius);
+ setEndBefore(xRadius, yRadius);
+ setEndAfter(xRadius, yRadius);
+ setStartAfter(xRadius, yRadius);
+ return thisInstance;
+ }
+
+ public T setStartBefore(int xRadius, int yRadius) {
+ startBuilder.setRadiusStart(xRadius);
+ beforeBuilder.setRadiusStart(yRadius);
+ return thisInstance;
+ }
+
+ public T setEndBefore(int xRadius, int yRadius) {
+ endBuilder.setRadiusStart(xRadius);
+ beforeBuilder.setRadiusEnd(yRadius);
+ return thisInstance;
+ }
+
+ public T setEndAfter(int xRadius, int yRadius) {
+ endBuilder.setRadiusEnd(xRadius);
+ afterBuilder.setRadiusEnd(yRadius);
+ return thisInstance;
+ }
+
+ public T setStartAfter(int xRadius, int yRadius) {
+ startBuilder.setRadiusEnd(xRadius);
+ afterBuilder.setRadiusStart(yRadius);
+ return thisInstance;
+ }
+
+ public final void test() throws IOException {
+ before = beforeBuilder.build();
+ after = afterBuilder.build();
+ end = endBuilder.build();
+ start = startBuilder.build();
+ testMethod();
+ }
+
+ protected abstract void testMethod() throws IOException;
+
+ protected static int numberOfNonZeroBorders(BorderProps first, BorderProps... borders) {
+ int i = first.width == 0 ? 0 : 1;
+ for (BorderProps borderProp : borders) {
+ if (borderProp.width > 0) {
+ i++;
+ }
+ }
+ return i;
+ }
+
+ protected int numberOfNonZeroBorders() {
+ return numberOfNonZeroBorders(before, end, after, start);
+ }
+
+ }
+
+ private static class BorderPropsBuilder<T extends BorderPainterTester<?>> {
+
+ private final int style = 0;
+
+ private final Color color = null;
+
+ private final Mode mode = BorderProps.Mode.SEPARATE;
+
+ private int width;
+
+ private int radiusStart;
+
+ private int radiusEnd;
+
+ private final T tester;
+
+ public BorderPropsBuilder(T tester) {
+ this.tester = tester;
+ }
+
+ public T tester() {
+ return tester;
+ }
+
+ public BorderPropsBuilder<T> setWidth(int width) {
+ this.width = width;
+ return this;
+ }
+
+ public BorderPropsBuilder<T> setRadiusStart(int radiusStart) {
+ this.radiusStart = radiusStart;
+ return this;
+ }
+
+ public BorderPropsBuilder<T> setRadiusEnd(int radiusEnd) {
+ this.radiusEnd = radiusEnd;
+ return this;
+ }
+
+ public BorderProps build() {
+ return new BorderProps(style, width, radiusStart, radiusEnd, color, mode);
+ }
+ }
+
+ private static final class DrawRectangularBordersTester
+ extends BorderPainterTester<DrawRectangularBordersTester> {
+
+ public DrawRectangularBordersTester(int xOrigin, int yOrigin, int width, int height)
+ throws IOException {
+ super(xOrigin, yOrigin, width, height);
+ }
+
+ public DrawRectangularBordersTester setStartBefore(int xRadius, int yRadius) {
+ return notSupported();
+ }
+
+ public DrawRectangularBordersTester setEndBefore(int xRadius, int yRadius) {
+ return notSupported();
+ }
+
+ public DrawRectangularBordersTester setEndAfter(int xRadius, int yRadius) {
+ return notSupported();
+ }
+
+ public DrawRectangularBordersTester setStartAfter(int xRadius, int yRadius) {
+ return notSupported();
+ }
+
+ private DrawRectangularBordersTester notSupported() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void testMethod() throws IOException {
+ sut.drawRectangularBorders(borderExtent, before, after, start, end);
+ verifyDrawing();
+ }
+
+ private void verifyDrawing() throws IOException {
+ final int rectX = borderExtent.x;
+ final int rectY = borderExtent.y;
+ final int rectWidth = borderExtent.width;
+ final int rectHeight = borderExtent.height;
+ if (before.width > 0) {
+ verify(graphicsPainter).moveTo(rectX, rectY);
+ verify(graphicsPainter).lineTo(rectWidth, rectY);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(before, end)))
+ .lineTo(rectWidth - end.width, rectY + before.width);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(before, start)))
+ .lineTo(rectX + start.width, rectY + before.width);
+ }
+ if (end.width > 0) {
+ verify(graphicsPainter).moveTo(rectWidth, rectY);
+ verify(graphicsPainter).lineTo(rectWidth, rectHeight);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(end, after)))
+ .lineTo(rectWidth - end.width, rectHeight - after.width);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(end, before)))
+ .lineTo(rectWidth - end.width, rectY + before.width);
+ }
+ if (after.width > 0) {
+ verify(graphicsPainter).moveTo(rectWidth, rectHeight);
+ verify(graphicsPainter).lineTo(rectX, rectHeight);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(after, end)))
+ .lineTo(rectX + start.width, rectHeight - after.width);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(after, start)))
+ .lineTo(rectWidth - end.width, rectHeight - after.width);
+ }
+ if (start.width > 0) {
+ verify(graphicsPainter).moveTo(rectX, rectHeight);
+ verify(graphicsPainter).lineTo(rectX, rectY);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(start, before)))
+ .lineTo(rectX + start.width, rectY + before.width);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(start, after)))
+ .lineTo(rectX + start.width, rectHeight - after.width);
+ }
+ int numBorders = numberOfNonZeroBorders();
+ verify(graphicsPainter, times(numBorders)).saveGraphicsState();
+ verify(graphicsPainter, times(numBorders)).closePath();
+ verify(graphicsPainter, times(numBorders)).restoreGraphicsState();
+ verify(graphicsPainter, times(numBorders)).clip();
+
+ }
+ }
+
+ private static final class DrawRoundedBordersTester extends BorderPainterTester<DrawRoundedBordersTester> {
+
+ public DrawRoundedBordersTester(int xOrigin, int yOrigin, int width, int height) throws IOException {
+ super(xOrigin, yOrigin, width, height);
+ }
+
+ public void testMethod() throws IOException {
+ sut.drawRoundedBorders(borderExtent, before, after, start, end);
+ verifyDrawing();
+ }
+
+ private void verifyDrawing() throws IOException {
+ int numBorders = numberOfNonZeroBorders();
+ final int rectWidth = borderExtent.width;
+ final int rectHeight = borderExtent.height;
+ if (before.width > 0) {
+ verify(graphicsPainter, atLeastOnce()).lineTo(rectWidth - end.getRadiusStart(), 0);
+ verify(graphicsPainter, atLeastOnce()).lineTo(calcLineEnd(start.width, before.width,
+ start.getRadiusStart(), before.getRadiusStart()), before.width);
+ }
+ if (end.width > 0) {
+ verify(graphicsPainter, atLeastOnce()).lineTo(rectHeight - after.getRadiusEnd(), 0);
+ verify(graphicsPainter, atLeastOnce()).lineTo(calcLineEnd(before.width, end.width,
+ before.getRadiusEnd(), end.getRadiusStart()), end.width);
+ }
+ if (after.width > 0) {
+ verify(graphicsPainter, atLeastOnce()).lineTo(rectWidth - start.getRadiusEnd(), 0);
+ verify(graphicsPainter, atLeastOnce()).lineTo(calcLineEnd(start.width, after.width,
+ start.getRadiusEnd(), after.getRadiusStart()), after.width);
+ }
+ if (start.width > 0) {
+ verify(graphicsPainter, atLeastOnce()).lineTo(rectHeight - after.getRadiusStart(), 0);
+ verify(graphicsPainter, atLeastOnce()).lineTo(calcLineEnd(before.width, start.width,
+ before.getRadiusStart(), before.getRadiusStart()), start.width);
+ }
+ // verify the drawing of the symmetric rounded corners (the ones that are a quarter of a circle)
+ // verification is restricted to those since it is too complex in the general case
+ if (before.width == end.width && before.getRadiusStart() == before.getRadiusEnd()
+ && end.getRadiusStart() == end.getRadiusEnd()
+ && before.getRadiusEnd() == end.getRadiusStart() && end.getRadiusStart() > 0) {
+ verify(graphicsPainter, atLeastOnce()).arcTo(Math.PI * 5 / 4, Math.PI * 3 / 2,
+ before.getRadiusStart(), end.getRadiusEnd(), before.getRadiusStart(),
+ end.getRadiusEnd());
+ }
+ if (end.width == after.width && end.getRadiusStart() == end.getRadiusEnd()
+ && after.getRadiusStart() == after.getRadiusEnd()
+ && end.getRadiusEnd() == after.getRadiusStart() && after.getRadiusStart() > 0) {
+ verify(graphicsPainter, atLeastOnce()).arcTo(Math.PI * 5 / 4, Math.PI * 3 / 2,
+ end.getRadiusStart(), after.getRadiusEnd(), end.getRadiusStart(),
+ after.getRadiusEnd());
+ }
+ if (after.width == start.width && after.getRadiusStart() == after.getRadiusEnd()
+ && start.getRadiusStart() == start.getRadiusEnd()
+ && after.getRadiusEnd() == start.getRadiusStart() && start.getRadiusStart() > 0) {
+ verify(graphicsPainter, atLeastOnce()).arcTo(Math.PI * 5 / 4, Math.PI * 3 / 2,
+ after.getRadiusStart(), start.getRadiusEnd(), after.getRadiusStart(),
+ start.getRadiusEnd());
+ }
+ if (start.width == before.width && start.getRadiusStart() == start.getRadiusEnd()
+ && before.getRadiusStart() == before.getRadiusEnd()
+ && start.getRadiusEnd() == before.getRadiusStart() && before.getRadiusStart() > 0) {
+ verify(graphicsPainter, atLeastOnce()).arcTo(Math.PI * 5 / 4, Math.PI * 3 / 2,
+ start.getRadiusStart(), before.getRadiusEnd(), start.getRadiusStart(),
+ before.getRadiusEnd());
+ }
+ verify(graphicsPainter, times(numBorders)).saveGraphicsState();
+ verify(graphicsPainter, times(numBorders)).closePath();
+ verify(graphicsPainter, times(numBorders)).restoreGraphicsState();
+ verify(graphicsPainter, times(numBorders)).clip();
+ }
+
+ private int calcLineEnd(int xWidth, int yWidth, int xRadius, int yRadius) {
+ return yWidth > yRadius ? yWidth : xWidth > 0 ? Math.max(xRadius, xWidth) : 0;
+ }
+
+ }
+
+ private static final class ClipBackgroundTester extends BorderPainterTester<ClipBackgroundTester> {
+
+ public ClipBackgroundTester(int xOrigin, int yOrigin, int width, int height) throws IOException {
+ super(xOrigin, yOrigin, width, height);
+ }
+
+ public void testMethod() throws IOException {
+ sut.clipBackground(borderExtent, before, after, start, end);
+ verifyClipping();
+ }
+
+ private void verifyClipping() throws IOException {
+ int xOrigin = borderExtent.x;
+ int yOrigin = borderExtent.y;
+ int xEnd = xOrigin + borderExtent.width;
+ int yEnd = yOrigin + borderExtent.height;
+
+ Corner startBeforeCorner = Corner.createStartBeforeCorner(getInnerRadiusStart(start),
+ getInnerRadiusStart(before));
+ Corner endBeforeCorner = Corner.createEndBeforeCorner(getInnerRadiusStart(end), getRadiusEnd(before));
+ Corner endAfterCorner = Corner.createEndAfterCorner(getRadiusEnd(end), getRadiusEnd(after));
+ Corner startAfterCorner = Corner.createStartAfterCorner(getRadiusEnd(start),
+ getInnerRadiusStart(after));
+ verify(graphicsPainter, times(1)).moveTo(xOrigin + startBeforeCorner.xRadius, yOrigin);
+ verify(graphicsPainter, times(1)).lineTo(xEnd - endBeforeCorner.xRadius, yOrigin);
+ endBeforeCorner.verifyCornerDrawn(graphicsPainter, xEnd - endBeforeCorner.xRadius,
+ yOrigin + endBeforeCorner.yRadius);
+ verify(graphicsPainter, times(1)).lineTo(xEnd, yEnd - endAfterCorner.yRadius);
+ endAfterCorner.verifyCornerDrawn(graphicsPainter, xEnd - endAfterCorner.xRadius,
+ yEnd - endAfterCorner.yRadius);
+ verify(graphicsPainter, times(1)).lineTo(xOrigin + startAfterCorner.xRadius, yEnd);
+ startAfterCorner.verifyCornerDrawn(graphicsPainter, xOrigin + startAfterCorner.xRadius,
+ yEnd - startAfterCorner.yRadius);
+ verify(graphicsPainter, times(1)).lineTo(xOrigin, yOrigin + startBeforeCorner.yRadius);
+ startBeforeCorner.verifyCornerDrawn(graphicsPainter, xOrigin + startBeforeCorner.xRadius,
+ yOrigin + startBeforeCorner.yRadius);
+ verify(graphicsPainter, times(1)).clip();
+ }
+
+ private int getInnerRadiusStart(BorderProps borderProps) {
+ return getInnerRadius(borderProps.getRadiusStart(), borderProps.width);
+ }
+
+ private int getRadiusEnd(BorderProps borderProps) {
+ return getInnerRadius(borderProps.getRadiusEnd(), borderProps.width);
+ }
+
+ private int getInnerRadius(int radius, int borderWidth) {
+ return Math.max(radius - borderWidth, 0);
+ }
+
+ private static class Corner {
+
+ public final int xRadius;
+
+ public final int yRadius;
+
+ private final double startAngle;
+
+ private final double endAngle;
+
+ public Corner(int xRadius, int yRadius, double startAngle, double endAngle) {
+ this.xRadius = xRadius;
+ this.yRadius = yRadius;
+ this.startAngle = startAngle;
+ this.endAngle = endAngle;
+ }
+
+ public static Corner createStartBeforeCorner(int xRadius, int yRadius) {
+ return new Corner(xRadius, yRadius, Math.PI, Math.PI * 3 / 2);
+ }
+
+ public static Corner createEndBeforeCorner(int xRadius, int yRadius) {
+ return new Corner(xRadius, yRadius, Math.PI * 3 / 2, 0);
+ }
+
+ public static Corner createEndAfterCorner(int xRadius, int yRadius) {
+ return new Corner(xRadius, yRadius, 0, Math.PI / 2);
+ }
+
+ public static Corner createStartAfterCorner(int xRadius, int yRadius) {
+ return new Corner(xRadius, yRadius, Math.PI / 2, Math.PI);
+ }
+
+ public void verifyCornerDrawn(GraphicsPainter graphicsPainter, int xCenter, int yCenter)
+ throws IOException {
+ if (xRadius != 0 && yRadius != 0) {
+ verify(graphicsPainter, times(1)).arcTo(startAngle, endAngle,
+ xCenter, yCenter, xRadius, yRadius);
+ } else {
+ verify(graphicsPainter, never()).arcTo(startAngle, endAngle,
+ xCenter, yCenter, xRadius, yRadius);
+ }
+ }
+ }
+ }
+
+
+} \ No newline at end of file
diff --git a/test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java b/test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java
new file mode 100644
index 000000000..4f3a5e628
--- /dev/null
+++ b/test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java
@@ -0,0 +1,170 @@
+/*
+ * 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.io.IOException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.fop.pdf.PDFNumber;
+
+import static org.mockito.Matchers.endsWith;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class PDFGraphicsPainterTestCase {
+
+ private PDFGraphicsPainter sut;
+
+ private PDFContentGenerator generator;
+
+ @Before
+ public void setup() {
+ generator = mock(PDFContentGenerator.class);
+ sut = new PDFGraphicsPainter(generator);
+ }
+
+ @Test
+ public void moveTo() {
+ int x = 10;
+ int y = 20;
+ sut.moveTo(x, y);
+ verify(generator).add(op("m", x, y));
+ }
+
+ @Test
+ public void lineTo() {
+ int x = 10;
+ int y = 20;
+ sut.lineTo(x, y);
+ verify(generator).add(op("l", x, y));
+ }
+
+ @Test
+ public void arcTo() throws IOException {
+ int width = 10;
+ int height = 10;
+ int x = 0;
+ int y = 0;
+ double startAngle = 0;
+ double endAngle = Math.PI / 2;
+ sut.arcTo(startAngle, endAngle, x, y, width, height);
+ //TODO stricter verification
+ verify(generator).add(endsWith(" c "));
+ }
+
+ @Test
+ public void closePath() {
+ sut.closePath();
+ verify(generator).add(op("h"));
+ }
+
+ @Test
+ public void clip() {
+ sut.clip();
+ verify(generator).add(opln("W\nn"));
+ }
+
+ @Test
+ public void saveGraphicsState() {
+ sut.saveGraphicsState();
+ verify(generator).add(opln("q"));
+ }
+
+ @Test
+ public void restoreGraphicsState() {
+ sut.restoreGraphicsState();
+ verify(generator).add(opln("Q"));
+ }
+
+ @Test
+ public void rotateCoordinates() throws IOException {
+ double angle = 0;
+ float s = (float) Math.sin(angle);
+ float c = (float) Math.cos(angle);
+ sut.rotateCoordinates(angle);
+ testTransformCoordinatesF(c, s, -s, c, 0, 0);
+ }
+
+ @Test
+ public void translateCoordinates() throws IOException {
+ int x = 10;
+ int y = 20;
+ sut.translateCoordinates(x, y);
+ testTransformCoordinates(1000, 0, 0, 1000, x, y);
+ }
+
+ @Test
+ public void scaleCoordinates() throws IOException {
+ float xScaleFactor = 10f;
+ float yScaleFactor = 2f;
+ sut.scaleCoordinates(xScaleFactor, yScaleFactor);
+ testTransformCoordinatesF(xScaleFactor, 0f, 0f, yScaleFactor, 0f, 0f);
+ }
+
+ @Test
+ public void cubicBezierTo() {
+ int[] args = new int[]{1, 2, 3, 4, 5, 6};
+ sut.cubicBezierTo(args[0], args[1], args[2], args[3], args[4], args[5]);
+ verify(generator).add(op("c", args));
+ }
+
+ private void testTransformCoordinatesF(float... args) {
+ verify(generator).add(opf("cm", args));
+ }
+
+ private void testTransformCoordinates(int... args) {
+ verify(generator).add(op("cm", args));
+ }
+
+ private String opf(String op, float... args) {
+ return opf(op, " ", args);
+ }
+
+ private String op(String op, int... args) {
+ return op(op, " ", args);
+ }
+
+ private String opln(String op, int... args) {
+ return op(op, "\n", args);
+ }
+
+ private String opf(String op, String ending, float... args) {
+ StringBuilder sb = new StringBuilder();
+ for (float arg : args) {
+ sb.append("" + PDFNumber.doubleOut(arg) + " ");
+ }
+ return sb.append(op.trim()).append(ending).toString();
+ }
+
+ private String op(String op, String ending, int... args) {
+ float[] formattedArgs = new float[args.length];
+ for (int i = 0; i < args.length; i++) {
+ formattedArgs[i] = format(args[i]);
+ }
+ return opf(op, ending, formattedArgs);
+ }
+
+ private float format(int i) {
+ return (float) i / 1000;
+ }
+
+}
diff --git a/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java b/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java
new file mode 100644
index 000000000..31f2f5c3b
--- /dev/null
+++ b/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java
@@ -0,0 +1,87 @@
+/*
+ * 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.Rectangle;
+
+import org.junit.Test;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.endsWith;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.traits.BorderProps;
+
+public class PDFPainterTestCase {
+
+ @Test
+ public void testDrawBorderRect() {
+ // the goal of this test is to check that the drawing of rounded corners in PDF calls
+ // PDFGraphicsPaiter.cubicBezierTo(); the check is done by verifying that a " c " command is written
+ // to the PDFContentGenerator
+ // mock
+ PDFContentGenerator pdfContentGenerator = mock(PDFContentGenerator.class);
+ // the next line is commented out because the format() method is static and not possible to mock
+ // when(PDFContentGenerator.format(anyFloat())).thenReturn("20.0");
+ // mock
+ FOUserAgent foUserAgent = mock(FOUserAgent.class);
+ when(foUserAgent.isAccessibilityEnabled()).thenReturn(false);
+ // mock
+ IFContext ifContext = mock(IFContext.class);
+ when(ifContext.getUserAgent()).thenReturn(foUserAgent);
+ // mock
+ PDFDocumentHandler pdfDocumentHandler = mock(PDFDocumentHandler.class);
+ when(pdfDocumentHandler.getGenerator()).thenReturn(pdfContentGenerator);
+ when(pdfDocumentHandler.getContext()).thenReturn(ifContext);
+ // mock
+ PDFLogicalStructureHandler pdfLogicalStructureHandler = mock(PDFLogicalStructureHandler.class);
+ // real, not mock
+ PDFPainter pdfPainter = new PDFPainter(pdfDocumentHandler, pdfLogicalStructureHandler);
+ // build rectangle 200 x 50 (points, which are converted to milipoints)
+ Rectangle rectangle = new Rectangle(0, 0, 200000, 50000);
+ // build border properties: width 4pt, radius 30pt
+ int style = Constants.EN_SOLID;
+ BorderProps.Mode mode = BorderProps.Mode.SEPARATE;
+ Color color = Color.BLACK;
+ int borderWidth = 4000;
+ int radiusStart = 30000;
+ int radiusEnd = 30000;
+ BorderProps border1 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ BorderProps border2 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ BorderProps border3 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ BorderProps border4 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ try {
+ pdfPainter.drawBorderRect(rectangle, border1, border2, border3, border4, Color.WHITE);
+ // since we cannot mock the PDFContentGenerator.format() static method we have to restrict the
+ // verification to commands that end with " c ".
+ verify(pdfContentGenerator, times(16)).add(endsWith(" c "));
+ } catch (Exception e) {
+ fail("something broke...");
+ }
+ }
+
+}
diff --git a/test/java/org/apache/fop/render/ps/PSPainterTestCase.java b/test/java/org/apache/fop/render/ps/PSPainterTestCase.java
index 90db3b98f..49ffb77fb 100644
--- a/test/java/org/apache/fop/render/ps/PSPainterTestCase.java
+++ b/test/java/org/apache/fop/render/ps/PSPainterTestCase.java
@@ -16,6 +16,8 @@
*/
package org.apache.fop.render.ps;
+import java.awt.Color;
+import java.awt.Rectangle;
import java.io.IOException;
import java.util.Collections;
@@ -26,9 +28,13 @@ import org.mockito.verification.VerificationMode;
import org.apache.xmlgraphics.ps.PSGenerator;
import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fo.Constants;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFState;
+import org.apache.fop.traits.BorderProps;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyFloat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -75,4 +81,42 @@ public class PSPainterTestCase {
}
verify(gen, test).useColor(state.getTextColor());
}
+
+ @Test
+ public void testDrawBorderRect() {
+ // the goal of this test is to check that the drawing of rounded corners in PS calls
+ // PSGraphicsPaiter.cubicBezierTo(); the check is done by verifying that a curveto command is written
+ // to the PSGenerator
+ // mock
+ PSGenerator psGenerator = mock(PSGenerator.class);
+ when(psGenerator.formatDouble(anyFloat())).thenReturn("20.0"); // simplify!
+ // mock
+ PSRenderingUtil psRenderingUtil = mock(PSRenderingUtil.class);
+ // mock
+ PSDocumentHandler psDocumentHandler = mock(PSDocumentHandler.class);
+ when(psDocumentHandler.getGenerator()).thenReturn(psGenerator);
+ when(psDocumentHandler.getPSUtil()).thenReturn(psRenderingUtil);
+ // real instance, no mock
+ PSPainter psPainter = new PSPainter(psDocumentHandler);
+ // build rectangle 200 x 50 (points, which are converted to milipoints)
+ Rectangle rectangle = new Rectangle(0, 0, 200000, 50000);
+ // build border properties: width 4pt, radius 30pt
+ int style = Constants.EN_SOLID;
+ BorderProps.Mode mode = BorderProps.Mode.SEPARATE;
+ Color color = Color.BLACK;
+ int borderWidth = 4000;
+ int radiusStart = 30000;
+ int radiusEnd = 30000;
+ BorderProps border1 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ BorderProps border2 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ BorderProps border3 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ BorderProps border4 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ try {
+ psPainter.drawBorderRect(rectangle, border1, border2, border3, border4, Color.WHITE);
+ verify(psGenerator, times(16)).writeln("20.0 20.0 20.0 20.0 20.0 20.0 curveto ");
+ } catch (Exception e) {
+ fail("something broke...");
+ }
+ }
+
}
diff --git a/test/java/org/apache/fop/traits/BorderPropsTestCase.java b/test/java/org/apache/fop/traits/BorderPropsTestCase.java
index 25227867b..ec93d708e 100644
--- a/test/java/org/apache/fop/traits/BorderPropsTestCase.java
+++ b/test/java/org/apache/fop/traits/BorderPropsTestCase.java
@@ -19,16 +19,17 @@
package org.apache.fop.traits;
-import static org.junit.Assert.assertEquals;
-
import java.awt.Color;
+import org.junit.Test;
+
import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives;
import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace;
import org.apache.fop.fo.Constants;
import org.apache.fop.util.ColorUtil;
-import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
/**
* Tests the BorderProps class.
@@ -44,23 +45,38 @@ public class BorderPropsTestCase {
Color col = new Color(1.0f, 1.0f, 0.5f, 1.0f);
//Normalize: Avoid false alarms due to color conversion (rounding)
col = ColorUtil.parseColorString(null, ColorUtil.colorToString(col));
-
- BorderProps b1 = new BorderProps(Constants.EN_DOUBLE, 1250,
- col, BorderProps.COLLAPSE_OUTER);
- String ser = b1.toString();
- BorderProps b2 = BorderProps.valueOf(null, ser);
- assertEquals(b1, b2);
+ BorderProps sut = BorderProps.makeRectangular(Constants.EN_DOUBLE, 1250, col,
+ BorderProps.Mode.COLLAPSE_OUTER);
+ testSerialization(sut);
float[] cmyk = new float[] {1.0f, 1.0f, 0.5f, 1.0f};
col = DeviceCMYKColorSpace.createCMYKColor(cmyk);
//Convert to sRGB with CMYK alternative as constructed by the cmyk() function
float[] rgb = col.getRGBColorComponents(null);
col = new ColorWithAlternatives(rgb[0], rgb[1], rgb[2], new Color[] {col});
- b1 = new BorderProps(Constants.EN_INSET, 9999,
- col, BorderProps.SEPARATE);
- ser = b1.toString();
- b2 = BorderProps.valueOf(null, ser);
- assertEquals(b1, b2);
+ sut = BorderProps.makeRectangular(Constants.EN_INSET, 9999, col, BorderProps.Mode.SEPARATE);
+ testSerialization(sut);
+ }
+
+ /**
+ * Test serialization and deserialization to/from String.
+ * @throws Exception if an error occurs
+ */
+ @Test
+ public void testSerializationWithCornerRadii() throws Exception {
+ Color col = new Color(1.0f, 1.0f, 0.5f, 1.0f);
+ //Normalize: Avoid false alarms due to color conversion (rounding)
+ col = ColorUtil.parseColorString(null, ColorUtil.colorToString(col));
+ for(BorderProps.Mode mode : BorderProps.Mode.values()) {
+ BorderProps sut = BorderProps.makeRectangular(Constants.EN_SOLID, 10, col, mode);
+ testSerialization(sut);
+ sut = new BorderProps(Constants.EN_SOLID, 10, 4, 3, col, mode);
+ testSerialization(sut);
+ }
+ }
+
+ private void testSerialization(BorderProps borderProp) {
+ assertEquals(borderProp, BorderProps.valueOf(null, borderProp.toString()));
}
}