git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1395490 13f79535-47bb-0310-9956-ffa450edef68tags/fop-2_0
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; | ||||
} | } |
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) { |
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. |
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} */ |
/* | |||||
* 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; | |||||
} | |||||
} | |||||
} | |||||
} |
/* | |||||
* 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; | |||||
} |
/* | |||||
* 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; | |||||
} |
/* | |||||
* 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 | |||||
} | |||||
} |
/* | |||||
* 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; | |||||
} | |||||
} |
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} */ |
/* | |||||
* 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); | |||||
} | |||||
} |
/* | |||||
* 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()); | |||||
} | |||||
} | |||||
} |
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) { |
/* | |||||
* 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()); | |||||
} | |||||
} |
/* | |||||
* 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()); | |||||
} | |||||
} |
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); | ||||
} | } |
*/ | */ | ||||
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"); | |||||
} | |||||
} | |||||
} | |||||
} | } |
/* | |||||
* 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..."); | |||||
} | |||||
} | |||||
} |
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); |
/* | |||||
* 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 + ")"); | |||||
} | |||||
} | |||||
} |
/* | |||||
* 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); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
/* | |||||
* 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; | |||||
} | |||||
} |
/* | |||||
* 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..."); | |||||
} | |||||
} | |||||
} |
*/ | */ | ||||
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..."); | |||||
} | |||||
} | |||||
} | } |
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())); | |||||
} | } | ||||
} | } |