I am using PDF box to attempt to create some rectangles with curved edges. After finally figuring out how to use Bézier curves, I was able to get a shape that I liked. My problem now is that I cannot figure out how to fill it. I've tried closing the path in random spots, drawing the shape using only Bézier curves, stroking the path at random spots, closing the path at random spots, but it still won't fill the whole thing. It only seems to fill the rounded edges of the curve. Could anyone please let me know what I am doing wrong? Thanks so much. generator is an object that I am using to fetch the horizontal and vertical positions currently set in the page. The horizontal and vertical position values do not change in this example (the horizontal position is 200, and the vertical 240). Here is the code that I am using (sorry for the magic numbers in there)
Here is the resulting image in the PDF page, which won't fill in for some reason:
//Creating PDF document object
PDDocument document = new PDDocument();
//Creating the PDDocumentInformation object
PDDocumentInformation pdd = document.getDocumentInformation();
generator.drawRectangleWithCurvedBorders(200, 400, cs, generator);
public void drawRectangleWithCurvedBorders(int width, int height, PDPageContentStream contentStream, XClass generator) throws IOException
{
contentStream.setStrokingColor( Color.BLACK );
contentStream.setNonStrokingColor( Color.BLACK );
// bottom of rectangle
contentStream.moveTo(generator.getHorizontalPosition() - 0.5f, generator.getVerticalPosition() );
contentStream.lineTo(generator.getHorizontalPosition() + width + 0.5f, generator.getVerticalPosition() );
contentStream.moveTo(generator.getHorizontalPosition() + width, generator.getVerticalPosition() );
contentStream.curveTo(generator.getHorizontalPosition() + width + 5.9f, generator.getVerticalPosition() + 0.14f,
generator.getHorizontalPosition() + width + 11.06f, generator.getVerticalPosition() + 5.16f,
generator.getHorizontalPosition() + width + 10.96f, generator.getVerticalPosition() + 10);
// left of rectangle
contentStream.moveTo(generator.getHorizontalPosition(), generator.getVerticalPosition() );
contentStream.curveTo(generator.getHorizontalPosition() - 5.9f, generator.getVerticalPosition() + 0.14f,
generator.getHorizontalPosition() - 11.06f, generator.getVerticalPosition() + 5.16f,
generator.getHorizontalPosition() - 10.96f, generator.getVerticalPosition() + 10);
contentStream.moveTo(generator.getHorizontalPosition() - 10.96f, generator.getVerticalPosition() + 10 - 0.5f);
contentStream.lineTo(generator.getHorizontalPosition() - 10.96f, generator.getVerticalPosition() + height + 0.5f );
// right of rectangle
contentStream.moveTo(generator.getHorizontalPosition() + width + 10.96f, generator.getVerticalPosition() + 10 - 0.5f);
contentStream.lineTo(generator.getHorizontalPosition() + width + 10.96f, generator.getVerticalPosition() + height + 0.5f);
contentStream.moveTo(generator.getHorizontalPosition() + width, generator.getVerticalPosition() + height + 10);
contentStream.curveTo(generator.getHorizontalPosition() + width + 5.9f, generator.getVerticalPosition() + height + 0.14f + 10,
generator.getHorizontalPosition() + width + 11.06f, generator.getVerticalPosition() + height - 5.16f + 10,
generator.getHorizontalPosition() + width + 10.96f, generator.getVerticalPosition() + height);
// top of rectangle
contentStream.moveTo(generator.getHorizontalPosition() + width + 0.5f, generator.getVerticalPosition() + height + 10);
contentStream.lineTo(generator.getHorizontalPosition() - 0.5f, generator.getVerticalPosition() + height + 10);
contentStream.moveTo(generator.getHorizontalPosition(), generator.getVerticalPosition() + height + 10);
contentStream.curveTo(generator.getHorizontalPosition() - 5.9f, generator.getVerticalPosition() + height + 0.14f + 10,
generator.getHorizontalPosition() - 11.06f, generator.getVerticalPosition() + height - 5.16f + 10,
generator.getHorizontalPosition() - 10.96f, generator.getVerticalPosition() + height);
contentStream.closePath();
contentStream.fill();
}
As Tilman already said in a comment to the question, the problem are the many moveTo()
instructions. Indeed, each moveTo
starts a new sub path and each sub path implies a separate fill area. Depending on the filling variant and the subpath orientations, intersections of those subpaths might actually be excluded from being filled.
Thus, to create a filled rectangle with curved borders as outlined by the OP, one should rearrange the path building instructions so that they outline the rectangle without a moveTo
interrupting the flow, e.g. like this:
try ( PDDocument document = new PDDocument() ) {
PDPage page = new PDPage();
document.addPage(page);
try ( PDPageContentStream contentStream = new PDPageContentStream(document, page) ) {
float x = 100;
float y = 100;
float width = 200;
float height = 300;
contentStream.setStrokingColor( Color.BLACK );
contentStream.setNonStrokingColor( Color.BLACK );
contentStream.moveTo(x, y);
// bottom of rectangle, left to right
contentStream.lineTo(x + width, y );
contentStream.curveTo(x + width + 5.9f, y + 0.14f,
x + width + 11.06f, y + 5.16f,
x + width + 10.96f, y + 10);
// right of rectangle, bottom to top
contentStream.lineTo(x + width + 10.96f, y + height);
contentStream.curveTo(x + width + 11.06f, y + height - 5.16f + 10,
x + width + 5.9f, y + height + 0.14f + 10,
x + width, y + height + 10);
// top of rectangle, right to left
contentStream.lineTo(x, y + height + 10);
contentStream.curveTo(x - 5.9f, y + height + 0.14f + 10,
x - 11.06f, y + height - 5.16f + 10,
x - 10.96f, y + height);
// left of rectangle, top to bottom
contentStream.lineTo(x - 10.96f, y + 10);
contentStream.curveTo(x - 11.06f, y + 5.16f,
x - 5.9f, y + 0.14f,
x, y);
contentStream.closePath();
contentStream.fill();
}
document.save(new File("CurvedBorderRectangleLikeMaht33n-improved.pdf"));
}
(CurvedBorderRectangle test testLikeMaht33nImproved
)
(I don't have that XClass generator
object, so I used two float
variables x
and y
instead.)
The result, as desired: