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



addBorderTrait(area, bpProps, isNotFirst, addBorderTrait(area, bpProps, isNotFirst,
CommonBorderPaddingBackground.START, CommonBorderPaddingBackground.START,
BorderProps.SEPARATE, Trait.BORDER_START, context);
BorderProps.Mode.SEPARATE, Trait.BORDER_START, context);


addBorderTrait(area, bpProps, isNotLast, addBorderTrait(area, bpProps, isNotLast,
CommonBorderPaddingBackground.END, CommonBorderPaddingBackground.END,
BorderProps.SEPARATE, Trait.BORDER_END, context);
BorderProps.Mode.SEPARATE, Trait.BORDER_END, context);


addBorderTrait(area, bpProps, false, addBorderTrait(area, bpProps, false,
CommonBorderPaddingBackground.BEFORE, CommonBorderPaddingBackground.BEFORE,
BorderProps.SEPARATE, Trait.BORDER_BEFORE, context);
BorderProps.Mode.SEPARATE, Trait.BORDER_BEFORE, context);


addBorderTrait(area, bpProps, false, addBorderTrait(area, bpProps, false,
CommonBorderPaddingBackground.AFTER, CommonBorderPaddingBackground.AFTER,
BorderProps.SEPARATE, Trait.BORDER_AFTER, context);
BorderProps.Mode.SEPARATE, Trait.BORDER_AFTER, context);
} }


/* /*
*/ */
private static void addBorderTrait(Area area, private static void addBorderTrait(Area area,
CommonBorderPaddingBackground bpProps, 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 iBP = bpProps.getBorderWidth(iSide, bDiscard);
int radiusStart = bpProps.getBorderRadiusStart(iSide, bDiscard, context); int radiusStart = bpProps.getBorderRadiusStart(iSide, bDiscard, context);
int radiusEnd = bpProps.getBorderRadiusEnd(iSide, bDiscard, context); int radiusEnd = bpProps.getBorderRadiusEnd(iSide, bDiscard, context);
if (iBP > 0 || radiusStart > 0 || radiusEnd > 0) { 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));
} }
} }


int radiusStart = bordProps.getBorderRadiusStart(side, false, context); int radiusStart = bordProps.getBorderRadiusStart(side, false, context);
int radiusEnd = bordProps.getBorderRadiusEnd(side, false, context); int radiusEnd = bordProps.getBorderRadiusEnd(side, false, context);
if (width != 0 || radiusStart != 0 || radiusEnd != 0) { 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 { } else {
return null; return null;
} }
assert borderInfo != null; assert borderInfo != null;
int width = borderInfo.getRetainedWidth(); int width = borderInfo.getRetainedWidth();
if (width != 0) { 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 { } else {
return null; return null;
} }

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

blocks[i][j].addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE); blocks[i][j].addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
blocks[i][j].setPositioning(Block.ABSOLUTE); 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(), 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) { private static void adjustXOffset(Block block, int amount) {

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

moveTo(sx1, clipy); moveTo(sx1, clipy);
float sx1a = sx1; float sx1a = sx1;
float ex1a = ex1; 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]; sx1a -= clipw[LEFT];
} }
if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) {
if (isCollapseOuter(bpsRight)) {
ex1a += clipw[RIGHT]; ex1a += clipw[RIGHT];
} }
lineTo(sx1a, outery); lineTo(sx1a, outery);
moveTo(clipx, sy1); moveTo(clipx, sy1);
float sy1a = sy1; float sy1a = sy1;
float ey1a = ey1; 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]; sy1a -= clipw[TOP];
} }
if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) {
if (isCollapseOuter(bpsBottom)) {
ey1a += clipw[BOTTOM]; ey1a += clipw[BOTTOM];
} }
lineTo(outerx, sy1a); lineTo(outerx, sy1a);
moveTo(ex1, clipy); moveTo(ex1, clipy);
float sx1a = sx1; float sx1a = sx1;
float ex1a = ex1; 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]; sx1a -= clipw[LEFT];
} }
if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) {
if (isCollapseOuter(bpsRight)) {
ex1a += clipw[RIGHT]; ex1a += clipw[RIGHT];
} }
lineTo(ex1a, outery); lineTo(ex1a, outery);
moveTo(clipx, ey1); moveTo(clipx, ey1);
float sy1a = sy1; float sy1a = sy1;
float ey1a = ey1; 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]; sy1a -= clipw[TOP];
} }
if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) {
if (isCollapseOuter(bpsBottom)) {
ey1a += clipw[BOTTOM]; ey1a += clipw[BOTTOM];
} }
lineTo(outerx, ey1a); lineTo(outerx, ey1a);
} }
} }


private boolean isCollapseOuter(BorderProps bp) {
return bp != null && bp.isCollapseOuter();
}

/** /**
* Common method to render the background and borders for any inline area. * Common method to render the background and borders for any inline area.
* The all borders and padding are drawn outside the specified 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



import org.w3c.dom.Document; 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.Image;
import org.apache.xmlgraphics.image.loader.ImageException; import org.apache.xmlgraphics.image.loader.ImageException;
import org.apache.xmlgraphics.image.loader.ImageInfo; import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.fop.render.RenderingContext; import org.apache.fop.render.RenderingContext;
import org.apache.fop.render.intermediate.AbstractIFPainter; import org.apache.fop.render.intermediate.AbstractIFPainter;
import org.apache.fop.render.intermediate.BorderPainter; 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.IFException;
import org.apache.fop.render.intermediate.IFState; import org.apache.fop.render.intermediate.IFState;
import org.apache.fop.render.intermediate.IFUtil; import org.apache.fop.render.intermediate.IFUtil;
*/ */
public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> { 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 X = 0;

private static final int Y = 1; private static final int Y = 1;


private final GraphicsPainter graphicsPainter;

/** the border painter */ /** the border painter */
private final AFPBorderPainterAdapter borderPainter; private final AFPBorderPainterAdapter borderPainter;
/** the rectangle painter */ /** the rectangle painter */
public AFPPainter(AFPDocumentHandler documentHandler) { public AFPPainter(AFPDocumentHandler documentHandler) {
super(documentHandler); super(documentHandler);
this.state = IFState.create(); 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.rectanglePainter = documentHandler.createRectanglePainter();
this.unitConv = getPaintingState().getUnitConverter(); this.unitConv = getPaintingState().getUnitConverter();
this.eventProducer = AFPEventProducer.Provider.get(getUserAgent().getEventBroadcaster()); this.eventProducer = AFPEventProducer.Provider.get(getUserAgent().getEventBroadcaster());


/** {@inheritDoc} */ /** {@inheritDoc} */
public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect) 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 //AFP doesn't support clipping, so we treat viewport like a group
//this is the same code as for startGroup() //this is the same code as for startGroup()
try { try {
/** {@inheritDoc} */ /** {@inheritDoc} */
protected void drawImage(Image image, Rectangle rect, protected void drawImage(Image image, Rectangle rect,
RenderingContext context, boolean convert, Map additionalHints) RenderingContext context, boolean convert, Map additionalHints)
throws IOException, ImageException {
throws IOException, ImageException {




AFPRenderingContext afpContext = (AFPRenderingContext)context;
AFPRenderingContext afpContext = (AFPRenderingContext) context;


AFPResourceInfo resourceInfo = AFPImageHandler.createResourceInformation( AFPResourceInfo resourceInfo = AFPImageHandler.createResourceInformation(
image.getInfo().getOriginalURI(), image.getInfo().getOriginalURI(),
} }
if (rect.width != 0 && rect.height != 0) { if (rect.width != 0 && rect.height != 0) {
if (fill instanceof Color) { if (fill instanceof Color) {
getPaintingState().setColor((Color)fill);
getPaintingState().setColor((Color) fill);
} else { } else {
throw new UnsupportedOperationException("Non-Color paints NYI"); throw new UnsupportedOperationException("Non-Color paints NYI");
} }
} }




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 //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. //and this one. Not done for now to avoid a lot of re-implementation and code duplication.

private static class AFPBorderPainterAdapter extends BorderPainter { private static class AFPBorderPainterAdapter extends BorderPainter {


private final class BorderImagePainter implements Graphics2DImagePainter { private final class BorderImagePainter implements Graphics2DImagePainter {
private final double esf;
private final double cornerCorrectionFactor;
private final Rectangle borderRect; private final Rectangle borderRect;
private final BorderProps bpsStart; private final BorderProps bpsStart;
private final BorderProps bpsEnd; private final BorderProps bpsEnd;
private final Color innerBackgroundColor; private final Color innerBackgroundColor;


/* TODO represent border related parameters in a class */ /* 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 bpsStart, BorderProps bpsEnd,
BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsBefore, BorderProps bpsAfter,
boolean[] roundCorner, Color innerBackgroundColor) { boolean[] roundCorner, Color innerBackgroundColor) {
this.esf = esf;
this.cornerCorrectionFactor = cornerCorrectionFactor;
this.borderRect = borderRect; this.borderRect = borderRect;
this.bpsStart = bpsStart; this.bpsStart = bpsStart;
this.bpsBefore = bpsBefore; this.bpsBefore = bpsBefore;
//background //background
Area background = new Area(area); Area background = new Area(area);
Area cornerRegion = new 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]) { if (roundCorner[TOP_LEFT]) {
AffineTransform transform = new AffineTransform(); 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 beforeWidth = bpsBefore.width;
int startWidth = bpsStart.width; int startWidth = bpsStart.width;
AffineTransform transform AffineTransform transform
= new AffineTransform(-1, 0, 0, 1, borderRect.width, 0); = 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 beforeWidth = bpsBefore.width;
int startWidth = bpsEnd.width; int startWidth = bpsEnd.width;
AffineTransform transform = new AffineTransform(-1, 0, 0, -1, AffineTransform transform = new AffineTransform(-1, 0, 0, -1,
borderRect.width, borderRect.height); 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 beforeWidth = bpsAfter.width;
int startWidth = bpsEnd.width; int startWidth = bpsEnd.width;
AffineTransform transform AffineTransform transform
= new AffineTransform(1, 0, 0, -1, 0, borderRect.height); = 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 beforeWidth = bpsAfter.width;
int startWidth = bpsStart.width; int startWidth = bpsStart.width;
borderPath.lineTo( borderPath.lineTo(
borderRect.width - (bpsEnd == null ? 0 : bpsEnd.width), borderRect.width - (bpsEnd == null ? 0 : bpsEnd.width),
borderRect.height - bpsAfter.width); borderRect.height - bpsAfter.width);
borderPath.lineTo(
bpsStart == null ? 0 : bpsStart.width,
borderPath.lineTo(bpsStart == null ? 0 : bpsStart.width,
borderRect.height - bpsAfter.width); borderRect.height - bpsAfter.width);
Area border = new Area(borderPath); Area border = new Area(borderPath);


} }
} }


private AFPBorderPainter delegate;
private final AFPPainter painter; private final AFPPainter painter;
private final AFPDocumentHandler documentHandler; private final AFPDocumentHandler documentHandler;


public AFPBorderPainterAdapter(AFPBorderPainter borderPainter, AFPPainter painter,
public AFPBorderPainterAdapter(GraphicsPainter graphicsPainter, AFPPainter painter,
AFPDocumentHandler documentHandler) { AFPDocumentHandler documentHandler) {
this.delegate = borderPainter;
super(graphicsPainter);
this.painter = painter; this.painter = painter;
this.documentHandler = documentHandler; this.documentHandler = documentHandler;
} }
return !hasRoundedCorners(bpsBefore, bpsAfter, bpsStart, bpsEnd); 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) { final BorderProps bpsStart, final BorderProps bpsEnd) {
return ((bpsStart == null ? false : bpsStart.getRadiusStart() > 0) 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, private void drawRoundedCorners(final Rectangle borderRect,
final BorderProps bpsBefore, final BorderProps bpsAfter, final BorderProps bpsBefore, final BorderProps bpsAfter,
final BorderProps bpsStart, final BorderProps bpsEnd, 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[]{ final boolean[] roundCorner = new boolean[]{
bpsBefore != null && bpsStart != null 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] if (!roundCorner[TOP_LEFT] && !roundCorner[TOP_RIGHT]
&& !roundCorner[BOTTOM_RIGHT] && !roundCorner[BOTTOM_LEFT]) {
&& !roundCorner[BOTTOM_RIGHT] && !roundCorner[BOTTOM_LEFT]) {
try { try {
drawRectangularBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); drawRectangularBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd);
} catch (IOException ioe) { } catch (IOException ioe) {


name = documentHandler.cacheRoundedCorner(areaKey); name = documentHandler.cacheRoundedCorner(areaKey);


painter = new BorderImagePainter(esf, borderRect,
painter = new BorderImagePainter(cornerCorrectionFactor, borderRect,
bpsStart, bpsEnd, bpsBefore, bpsAfter, bpsStart, bpsEnd, bpsBefore, bpsAfter,
roundCorner, innerBackgroundColor); roundCorner, innerBackgroundColor);
} }
paintCornersAsBitmap(painter, borderRect, name); paintCornersAsBitmap(painter, borderRect, name);
} }


private boolean isNotCollapseOuter(BorderProps bp) {
return !bp.isCollapseOuter();
}


private Area makeCornerClip(final int beforeRadius, final int startRadius, private Area makeCornerClip(final int beforeRadius, final int startRadius,
final AffineTransform transform) {
final AffineTransform transform) {


Rectangle clipR = new Rectangle(0, 0, startRadius, beforeRadius); Rectangle clipR = new Rectangle(0, 0, startRadius, beforeRadius);




GeneralPath cut = new GeneralPath(); GeneralPath cut = new GeneralPath();
cut.moveTo(0, 0); cut.moveTo(0, 0);
float borderWidthRatio = ((float)beforeWidth) / startWidth;
float borderWidthRatio = ((float) beforeWidth) / startWidth;
if (beforeWidth * startRadius > startWidth * beforeRadius) { if (beforeWidth * startRadius > startWidth * beforeRadius) {
cut.lineTo(startRadius, borderWidthRatio * startRadius); cut.lineTo(startRadius, borderWidthRatio * startRadius);
cut.lineTo(startRadius, 0); cut.lineTo(startRadius, 0);


GeneralPath cut = new GeneralPath(); GeneralPath cut = new GeneralPath();
cut.moveTo(0, 0); cut.moveTo(0, 0);
float borderWidthRatio = ((float)beforeWidth) / startWidth;
float borderWidthRatio = ((float) beforeWidth) / startWidth;
if (beforeWidth * startRadius > startWidth * beforeRadius) { if (beforeWidth * startRadius > startWidth * beforeRadius) {
cut.lineTo(startRadius, borderWidthRatio * startRadius); cut.lineTo(startRadius, borderWidthRatio * startRadius);
cut.lineTo(startRadius, 0); cut.lineTo(startRadius, 0);


private String makeKey(Rectangle area, BorderProps beforeProps, private String makeKey(Rectangle area, BorderProps beforeProps,
BorderProps endProps, BorderProps afterProps, BorderProps startProps, BorderProps endProps, BorderProps afterProps, BorderProps startProps,
Color innerBackgroundColor) {
Color innerBackgroundColor) {


return hash(new StringBuffer() return hash(new StringBuffer()
.append(area.width) .append(area.width)
char[] digits = {'0', '1', '2', '3', '4', '5', '6', char[] digits = {'0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
for (int idx = 0; idx < 6; ++idx) { 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(); return sb.toString();
} }
} }
} }


@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, protected void arcTo(double startAngle, double endAngle, int cx, int cy, int width,
int height) throws IOException { 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} */ /** {@inheritDoc} */
@Override @Override
public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
throws IFException {
throws IFException {
try { try {
this.borderPainter.drawLine(start, end, width, color, style);
this.graphicsPainter.drawLine(start, end, width, color, style);
} catch (IOException ioe) { } catch (IOException ioe) {
throw new IFException("I/O error in drawLine()", ioe); throw new IFException("I/O error in drawLine()", ioe);
} }


// register font as necessary // register font as necessary
Map<String, Typeface> fontMetricMap = getFontInfo().getFonts(); 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); final Font font = getFontInfo().getFontInstance(triplet, fontSize);
AFPPageFonts pageFonts = getPaintingState().getPageFonts(); AFPPageFonts pageFonts = getPaintingState().getPageFonts();
AFPFontAttributes fontAttributes = pageFonts.registerFont(fontKey, afpFont, fontSize); AFPFontAttributes fontAttributes = pageFonts.registerFont(fontKey, afpFont, fontSize);


final int fontReference = fontAttributes.getFontReference(); 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); final CharacterSet charSet = afpFont.getCharacterSet(fontSize);


builder.absoluteMoveInline(p.x); builder.absoluteMoveInline(p.x);


builder.setExtendedTextColor(state.getTextColor()); builder.setExtendedTextColor(state.getTextColor());
builder.setCodedFont((byte)fontReference);
builder.setCodedFont((byte) fontReference);


int l = text.length(); int l = text.length();
int[] dx = IFUtil.convertDPToDX ( dp ); int[] dx = IFUtil.convertDPToDX ( dp );
/** {@inheritDoc} */ /** {@inheritDoc} */
public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter, public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) throws IFException { BorderProps bpsStart, BorderProps bpsEnd) throws IFException {

//not supported by AFP
} }


/** {@inheritDoc} */ /** {@inheritDoc} */
public boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter, public boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) { BorderProps bpsStart, BorderProps bpsEnd) {
return borderPainter.isBackgroundRequired( bpsBefore, bpsAfter,
bpsStart, bpsEnd);
return borderPainter.isBackgroundRequired(bpsBefore, bpsAfter, bpsStart, bpsEnd);
} }


/** {@inheritDoc} */ /** {@inheritDoc} */

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

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

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

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

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

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

import org.apache.fop.fonts.FontTriplet; import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.render.RenderingContext; import org.apache.fop.render.RenderingContext;
import org.apache.fop.render.intermediate.AbstractIFPainter; 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.IFContext;
import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFState; import org.apache.fop.render.intermediate.IFState;
/** The font information */ /** The font information */
protected FontInfo fontInfo; protected FontInfo fontInfo;


private Java2DBorderPainter borderPainter;
private final GraphicsPainter graphicsPainter;

private final BorderPainter borderPainter;


/** The current state, holds a Graphics2D and its context */ /** The current state, holds a Graphics2D and its context */
protected Java2DGraphicsState g2dState; protected Java2DGraphicsState g2dState;
} }
this.fontInfo = fontInfo; this.fontInfo = fontInfo;
this.g2dState = new Java2DGraphicsState(g2d, fontInfo, g2d.getTransform()); this.g2dState = new Java2DGraphicsState(g2d, fontInfo, g2d.getTransform());
this.borderPainter = new Java2DBorderPainter(this);
graphicsPainter = new Java2DGraphicsPainter(this);
this.borderPainter = new BorderPainter(graphicsPainter);
} }


/** {@inheritDoc} */ /** {@inheritDoc} */
/** {@inheritDoc} */ /** {@inheritDoc} */
public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
throws IFException { 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} */ /** {@inheritDoc} */

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

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

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

import org.apache.fop.pdf.PDFXObject; import org.apache.fop.pdf.PDFXObject;
import org.apache.fop.render.RenderingContext; import org.apache.fop.render.RenderingContext;
import org.apache.fop.render.intermediate.AbstractIFPainter; 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.IFException;
import org.apache.fop.render.intermediate.IFState; import org.apache.fop.render.intermediate.IFState;
import org.apache.fop.render.intermediate.IFUtil; import org.apache.fop.render.intermediate.IFUtil;
/** The current content generator */ /** The current content generator */
protected PDFContentGenerator generator; protected PDFContentGenerator generator;


private final PDFBorderPainter borderPainter;
private final GraphicsPainter graphicsPainter;

private final BorderPainter borderPainter;


private boolean accessEnabled; private boolean accessEnabled;


super(documentHandler); super(documentHandler);
this.logicalStructureHandler = logicalStructureHandler; this.logicalStructureHandler = logicalStructureHandler;
this.generator = documentHandler.getGenerator(); 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(); this.state = IFState.create();
accessEnabled = this.getUserAgent().isAccessibilityEnabled(); accessEnabled = this.getUserAgent().isAccessibilityEnabled();
} }
public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
throws IFException { throws IFException {
generator.endTextObject(); 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) { private Typeface getTypeface(String fontName) {

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

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

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

import org.apache.fop.fonts.Typeface; import org.apache.fop.fonts.Typeface;
import org.apache.fop.render.RenderingContext; import org.apache.fop.render.RenderingContext;
import org.apache.fop.render.intermediate.AbstractIFPainter; 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.IFException;
import org.apache.fop.render.intermediate.IFState; import org.apache.fop.render.intermediate.IFState;
import org.apache.fop.render.intermediate.IFUtil; import org.apache.fop.render.intermediate.IFUtil;
/** logging instance */ /** logging instance */
private static Log log = LogFactory.getLog(PSPainter.class); private static Log log = LogFactory.getLog(PSPainter.class);


private PSBorderPainter borderPainter;
private final GraphicsPainter graphicsPainter;

private BorderPainter borderPainter;


private boolean inTextMode = false; private boolean inTextMode = false;




protected PSPainter(PSDocumentHandler documentHandler, IFState state) { protected PSPainter(PSDocumentHandler documentHandler, IFState state) {
super(documentHandler); super(documentHandler);
this.borderPainter = new PSBorderPainter(getGenerator());
this.graphicsPainter = new PSGraphicsPainter(getGenerator());
this.borderPainter = new BorderPainter(graphicsPainter);
this.state = state; this.state = state;
} }


throws IFException { throws IFException {
try { try {
endTextObject(); endTextObject();
this.borderPainter.drawLine(start, end, width, color, style);
this.graphicsPainter.drawLine(start, end, width, color, style);
} catch (IOException ioe) { } catch (IOException ioe) {
throw new IFException("I/O error in drawLine()", ioe); throw new IFException("I/O error in drawLine()", ioe);
} }

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

*/ */
public class BorderProps implements Serializable { 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_*) */ /** 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 */ /** Border color */
public Color color; // CSOK: VisibilityModifier
public final Color color; // CSOK: VisibilityModifier

/** Border width */ /** 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. * Constructs a new BorderProps instance.
* @param style border style (one of EN_*) * @param style border style (one of EN_*)
* @param width border width * @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 color border color
* @param mode border mode ((one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER) * @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.style = style;
this.width = width; this.width = width;
this.radiusStart = radiusStart;
this.radiusEnd = radiusEnd;
this.color = color; this.color = color;
this.mode = mode; 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 width border width
* @param color border color * @param color border color
* @param mode border mode ((one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER) * @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);
} }


/** /**
return radiusStart; 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 * @return the radius of the corner adjacent to the after or end border
*/ */
return radiusEnd; 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 * @param bp the border properties or null
* @return the effective width of the clipped part of the border * @return the effective width of the clipped part of the border
*/ */
public static int getClippedWidth(BorderProps bp) { 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() { private String getStyleString() {
return BorderStyle.valueOf(style).getEnumValue(); return BorderStyle.valueOf(style).getEnumValue();
} }


public boolean isCollapseOuter() {
return mode == Mode.COLLAPSE_OUTER;
}

/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public int hashCode() { public int hashCode() {
* @return a BorderProps instance * @return a BorderProps instance
*/ */
public static BorderProps valueOf(FOUserAgent foUserAgent, String s) { 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} */ /** {@inheritDoc} */
@Override @Override
public String toString() { public String toString() {
StringBuffer sbuf = new StringBuffer(); 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 (radiusStart != 0 || radiusEnd != 0) {
if (mode == SEPARATE) {
if (mode.equals(Mode.SEPARATE)) {
// Because of the corner radii properties the mode must be set // Because of the corner radii properties the mode must be set
// so that the parameter index is consistent // 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(')'); sbuf.append(')');
return sbuf.toString(); 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

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

public void clipRect(Rectangle rect) throws IFException { 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 { public void fillRect(Rectangle rect, Paint fill) throws IFException {
} }


String text) throws IFException { 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); FontInfo fontInfo = mock(FontInfo.class);
when(handler.getFontInfo()).thenReturn(fontInfo); when(handler.getFontInfo()).thenReturn(fontInfo);

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

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

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

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

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

*/ */
package org.apache.fop.render.ps; package org.apache.fop.render.ps;


import java.awt.Color;
import java.awt.Rectangle;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;


import org.apache.xmlgraphics.ps.PSGenerator; import org.apache.xmlgraphics.ps.PSGenerator;


import org.apache.fop.apps.FOUserAgent; 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.IFContext;
import org.apache.fop.render.intermediate.IFState; 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.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
} }
verify(gen, test).useColor(state.getTextColor()); 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



package org.apache.fop.traits; package org.apache.fop.traits;


import static org.junit.Assert.assertEquals;

import java.awt.Color; import java.awt.Color;


import org.junit.Test;

import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives; import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives;
import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace; import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace;


import org.apache.fop.fo.Constants; import org.apache.fop.fo.Constants;
import org.apache.fop.util.ColorUtil; import org.apache.fop.util.ColorUtil;
import org.junit.Test;

import static org.junit.Assert.assertEquals;


/** /**
* Tests the BorderProps class. * Tests the BorderProps class.
Color col = new Color(1.0f, 1.0f, 0.5f, 1.0f); Color col = new Color(1.0f, 1.0f, 0.5f, 1.0f);
//Normalize: Avoid false alarms due to color conversion (rounding) //Normalize: Avoid false alarms due to color conversion (rounding)
col = ColorUtil.parseColorString(null, ColorUtil.colorToString(col)); 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}; float[] cmyk = new float[] {1.0f, 1.0f, 0.5f, 1.0f};
col = DeviceCMYKColorSpace.createCMYKColor(cmyk); col = DeviceCMYKColorSpace.createCMYKColor(cmyk);
//Convert to sRGB with CMYK alternative as constructed by the cmyk() function //Convert to sRGB with CMYK alternative as constructed by the cmyk() function
float[] rgb = col.getRGBColorComponents(null); float[] rgb = col.getRGBColorComponents(null);
col = new ColorWithAlternatives(rgb[0], rgb[1], rgb[2], new Color[] {col}); 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