Browse Source

Increased unit test coverage for the Painters



git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1395490 13f79535-47bb-0310-9956-ffa450edef68
tags/fop-2_0
Peter Hancock 11 years ago
parent
commit
e079e8eb19
26 changed files with 3439 additions and 1951 deletions
  1. 12
    22
      src/java/org/apache/fop/layoutmgr/TraitSetter.java
  2. 2
    2
      src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java
  3. 17
    12
      src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
  4. 146
    179
      src/java/org/apache/fop/render/afp/AFPPainter.java
  5. 102
    0
      src/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformer.java
  6. 37
    0
      src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java
  7. 509
    489
      src/java/org/apache/fop/render/intermediate/BorderPainter.java
  8. 145
    0
      src/java/org/apache/fop/render/intermediate/GraphicsPainter.java
  9. 0
    357
      src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java
  10. 333
    0
      src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java
  11. 12
    3
      src/java/org/apache/fop/render/java2d/Java2DPainter.java
  12. 0
    369
      src/java/org/apache/fop/render/pdf/PDFBorderPainter.java
  13. 499
    0
      src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java
  14. 12
    3
      src/java/org/apache/fop/render/pdf/PDFPainter.java
  15. 0
    387
      src/java/org/apache/fop/render/ps/PSBorderPainter.java
  16. 387
    0
      src/java/org/apache/fop/render/ps/PSGraphicsPainter.java
  17. 8
    3
      src/java/org/apache/fop/render/ps/PSPainter.java
  18. 108
    105
      src/java/org/apache/fop/traits/BorderProps.java
  19. 131
    0
      test/java/org/apache/fop/render/afp/AFPPainterTestCase.java
  20. 4
    6
      test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java
  21. 79
    0
      test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java
  22. 565
    0
      test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java
  23. 170
    0
      test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java
  24. 87
    0
      test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java
  25. 44
    0
      test/java/org/apache/fop/render/ps/PSPainterTestCase.java
  26. 30
    14
      test/java/org/apache/fop/traits/BorderPropsTestCase.java

+ 12
- 22
src/java/org/apache/fop/layoutmgr/TraitSetter.java View File

@@ -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;
}

+ 2
- 2
src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java View File

@@ -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) {

+ 17
- 12
src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java View File

@@ -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.

+ 146
- 179
src/java/org/apache/fop/render/afp/AFPPainter.java View File

@@ -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} */

+ 102
- 0
src/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformer.java View File

@@ -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;
}
}
}
}

+ 37
- 0
src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java View File

@@ -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;
}

+ 509
- 489
src/java/org/apache/fop/render/intermediate/BorderPainter.java
File diff suppressed because it is too large
View File


+ 145
- 0
src/java/org/apache/fop/render/intermediate/GraphicsPainter.java View File

@@ -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;
}

+ 0
- 357
src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java View File

@@ -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

}

}

+ 333
- 0
src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java View File

@@ -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;
}

}

+ 12
- 3
src/java/org/apache/fop/render/java2d/Java2DPainter.java View File

@@ -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} */

+ 0
- 369
src/java/org/apache/fop/render/pdf/PDFBorderPainter.java View File

@@ -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);
}

}

+ 499
- 0
src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java View File

@@ -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());
}
}

}

+ 12
- 3
src/java/org/apache/fop/render/pdf/PDFPainter.java View File

@@ -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) {

+ 0
- 387
src/java/org/apache/fop/render/ps/PSBorderPainter.java View File

@@ -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());
}

}

+ 387
- 0
src/java/org/apache/fop/render/ps/PSGraphicsPainter.java View File

@@ -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());
}

}

+ 8
- 3
src/java/org/apache/fop/render/ps/PSPainter.java View File

@@ -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);
}

+ 108
- 105
src/java/org/apache/fop/traits/BorderProps.java View File

@@ -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);
}

/**
@@ -90,14 +113,6 @@ public class BorderProps implements Serializable {
return radiusStart;
}

/**
*
* @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
*/
@@ -105,24 +120,12 @@ public class BorderProps implements Serializable {
return radiusEnd;
}

/**
*
* @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");
}
}
}

}

+ 131
- 0
test/java/org/apache/fop/render/afp/AFPPainterTestCase.java View File

@@ -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...");
}
}

}

+ 4
- 6
test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java View File

@@ -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);

+ 79
- 0
test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java View File

@@ -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 + ")");
}
}
}

+ 565
- 0
test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java View File

@@ -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);
}
}
}
}


}

+ 170
- 0
test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java View File

@@ -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;
}

}

+ 87
- 0
test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java View File

@@ -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...");
}
}

}

+ 44
- 0
test/java/org/apache/fop/render/ps/PSPainterTestCase.java View File

@@ -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...");
}
}

}

+ 30
- 14
test/java/org/apache/fop/traits/BorderPropsTestCase.java View File

@@ -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()));
}

}

Loading…
Cancel
Save