i tried to create rectangles as in the image, when i tried to create rectangles using coordinates the
two rectangles are placing one after other.
Here is the code how iam creating Rectangle.
when i give coordinates for two rectangles those are generating one after other, i want them to overlap as in the image..How can i make it?
PdfWriter writer= PdfWriter.getInstance(document, new FileOutputStream(filename));
document.open();
PdfContentByte cb = writer.getDirectContent();
Rectangle rect,rect1;
rect = new Rectangle(p1,p2,p3,p4); // CO-ORDINATES OF RECTANGLE
rect.setBorder(Rectangle.BOX);
cb.rectangle(rect);
Please take a look at the Rectangles example to find out how to create a PDF that looks like rectangles.pdf:
When creating a rectangle, you need the coordinates of the lower-left corner and the upper-right corner of the rectangle. For instance:
float llx = 36;
float lly = 700;
float urx = 200;
float ury = 806;
You already know that you need a PdfContentByte instance to draw the first rectangle:
PdfContentByte canvas = writer.getDirectContent();
Rectangle rect1 = new Rectangle(llx, lly, urx, ury);
rect1.setBackgroundColor(BaseColor.LIGHT_GRAY);
rect1.setBorder(Rectangle.BOX);
rect1.setBorderWidth(1);
canvas.rectangle(rect1);
For clarity, I have defined a background color and I've set the border width to 1 pt.
Now when you want to add an extra rectangle that overlaps the same way as described in your question, you need to change the llx and ury value. That's elementary math. For instance:
Rectangle rect2 = new Rectangle(llx + 60, lly, urx, ury - 40);
rect2.setBackgroundColor(BaseColor.DARK_GRAY);
rect2.setBorder(Rectangle.BOX);
rect2.setBorderColor(BaseColor.WHITE);
rect2.setBorderWidth(0.5f);
canvas.rectangle(rect2);
To make sure you see the difference, I've now used another background color, and I defined 0.5 pt as the border width and white as the border color.
It doesn't get any simpler than this.
Related
My requirement is straight, I want to create a rectangle positioned vertically and add text to it starting from bottom to going upward direction. (basically 90 degree box) in all pages of PDF
I tried achieving it using below code snap, but I want it to be enclosed within a specific dimension of box, which I cant control using this ColumnText approach
for example:
ColumnText.showTextAligned(canvas, PdfContentByte.ALIGN_LEFT, note,
.03F * pageWidth, .68F * pageHeight, 90);
For a task like yours the static convenience methods of ColumnText don't suffice, you need to actually create and parameterize a full ColumnText instance.
For example like this:
float width = Utilities.millimetersToPoints(10);
float height = Utilities.millimetersToPoints(100);
float x = Utilities.millimetersToPoints(15);
float y = Utilities.millimetersToPoints(150);
float fontHeight = Utilities.millimetersToPoints(4);
String content = "Some text to fill the box. There's nothing really to say, just a box to fill. So let's fill the box.";
PdfReader reader = new PdfReader(YOUR_SOURCE_FILE);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(new File(RESULT_FOLDER, "RotatedBoxForAbbas.pdf")));
Rectangle cropBox = reader.getCropBox(1);
PdfContentByte canvas = stamper.getOverContent(1);
canvas.concatCTM(0, 1, -1, 0, cropBox.getLeft() + x + width, cropBox.getBottom() + y);
canvas.rectangle(0, 0, height, width);
canvas.stroke();
ColumnText columnText = new ColumnText(canvas);
columnText.addText(new Chunk(content, new Font(FontFamily.HELVETICA, fontHeight)));
columnText.setLeading(fontHeight);
columnText.setSimpleColumn(2, 0, height - 4, width);
columnText.go();
stamper.close();
(AddTextBox test testRotatedBoxForAbbas)
(While this test has been created for iText 5, it should work identically with iText 2.1.7 and OpenPdf after adapting the import packages.)
You didn't mention dimensions in this question but in your previous, removed one you mentioned dimensions given in mm, so I also used millimeters here.
The result on an empty source page:
double degPi = degrees * Math.PI / 180;
double a = Math.cos(degPi)*tImgCover.getScaledHeight();
double b = Math.sin(degPi)*tImgCover.getScaledWidth();
double c = -Math.sin(degPi) * tImgCover.getScaledHeight();
double d = Math.cos(degPi)* tImgCover.getScaledWidth();
double e = absX;
double f = absY;
contentByte.addImage(imgae, a, b, c, d, e, f);/*add image*/
How to rotate around the image center by itext?
If we have an Image image and coordinates x, y, we can draw the image without rotation with its lower left corner at the given coordinates like this
contentByte.addImage(image, image.getWidth(), 0, 0, image.getHeight(), x, y);
A bitmap image from the resources has a size of 1x1 with the coordinate origin at its lower left. Thus, this operation stretches the image to its correct size and moves it so its lower left is at the given coordinates.
If we want to draw the same image as if the one drawn above was rotated around its center by an angle rotate, therefore, we can do this by moving the 1x1 image so that the origin is in its center, stretch it to its correct size, rotate it, and then move the origin (which still is at the center of the rotated image) to the center of the unrotated image. These operations are easier to express using AffineTransform instances (from package com.itextpdf.awt.geom) instead number tupels. Thus:
// Draw image as if the previous image was rotated around its center
// Image starts out being 1x1 with origin in lower left
// Move origin to center of image
AffineTransform A = AffineTransform.getTranslateInstance(-0.5, -0.5);
// Stretch it to its dimensions
AffineTransform B = AffineTransform.getScaleInstance(image.getWidth(), image.getHeight());
// Rotate it
AffineTransform C = AffineTransform.getRotateInstance(rotate);
// Move it to have the same center as above
AffineTransform D = AffineTransform.getTranslateInstance(x + image.getWidth()/2, y + image.getHeight()/2);
// Concatenate
AffineTransform M = (AffineTransform) A.clone();
M.preConcatenate(B);
M.preConcatenate(C);
M.preConcatenate(D);
//Draw
contentByte.addImage(image, M);
(AddRotatedImage.java test method testAddRotatedImage)
For example drawing both images using
int x = 200;
int y = 300;
float rotate = (float) Math.PI / 3;
results in something like this:
With a Flip
The OP asked in a comment
how to add rotate and flip image?
For this you simply insert a mirroring affine transformation into the sequence of transformations above.
Unfortunately the OP did not mention which he meant a horizontal or a vertical flip. But as changing the rotation angle accordingly transforms one in the other, that isn't really necessary, either.
// Draw image as if the previous image was flipped and rotated around its center
// Image starts out being 1x1 with origin in lower left
// Move origin to center of image
AffineTransform A = AffineTransform.getTranslateInstance(-0.5, -0.5);
// Flip it horizontally
AffineTransform B = new AffineTransform(-1, 0, 0, 1, 0, 0);
// Stretch it to its dimensions
AffineTransform C = AffineTransform.getScaleInstance(image.getWidth(), image.getHeight());
// Rotate it
AffineTransform D = AffineTransform.getRotateInstance(rotate);
// Move it to have the same center as above
AffineTransform E = AffineTransform.getTranslateInstance(x + image.getWidth()/2, y + image.getHeight()/2);
// Concatenate
AffineTransform M = (AffineTransform) A.clone();
M.preConcatenate(B);
M.preConcatenate(C);
M.preConcatenate(D);
M.preConcatenate(E);
//Draw
contentByte.addImage(image, M);
(AddRotatedImage.java test method testAddRotatedFlippedImage)
The result with the same image as above:
With Interpolation
The OP asked in a yet another comment
How anti aliasing ?
The iText Image class knows an Interpolation property. By setting it to true (before adding the image to the document, obviously),
image.setInterpolation(true);
low resolution images are subject to interpolation when drawn.
E.g. using a 2x2 image with differently colored pixels instead of the image of Willi, you get the following results, first without interpolation, then with interpolation:
Confer the AddRotatedImage.java test testAddRotatedInterpolatedImage which adds this image:
Beware: iText Image property Interpolation effectively sets the Interpolate entry in the PDF image dictionary. The PDF specification notes in this context:
NOTE A conforming Reader may choose to not implement this feature of PDF, or may use any specific implementation of interpolation that it wishes.
Thus, on some viewers interpolation may occur differently than in your viewer, maybe even not at all. If you need a specific kind of interpolation on every viewer, upscale the image with the desired amount of interpolation / anti-aliasing before loading it into an iText Image.
public static BufferedImage rotateClockwise90( BufferedImage inputImage ){
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage returnImage = new BufferedImage( height, width , inputImage.getType() );
for( int x = 0; x < width; x++ ) {
for( int y = 0; y < height; y++ ) {
returnImage.setRGB( height-y-1, x, inputImage.getRGB( x, y ) );
}
}
return returnImage;
}
I try to draw some nested tables in iText as i thought it would be the easiest way to position everything.
So i have multiple tables inside another table who all have background color and/or strokes (via PdfPCellEvents). Unfortunately the strokes of the outer table are overlapping the background of the inner table.
I assume that comes from a wrong order of applying or some wrong set saveState or restoreState in my PdfPCellEvents.
Can anyone explain the right usage of saveState and restoreState to me and give me a hint how to apply backgrounds and strokes the right way?
Here is my code for adding a striped background cell:
PdfPCell scaleBackground = new PdfPCell();
scaleBackground.setBorder(Rectangle.NO_BORDER);
scaleBackground.setVerticalAlignment(Element.ALIGN_TOP);
scaleBackground.setCellEvent(new StripedScaleBackground(max, scaleHeight));
cellLayout method of StripedScaleBackground:
public void cellLayout(PdfPCell cell, Rectangle rect, PdfContentByte[] canvases)
{
PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];
float llx = rect.getLeft();
float lly = rect.getBottom();
float urx = rect.getRight();
float ury = rect.getTop();
// Light scale lines with padding from left
canvas.setLineWidth(Constants.BORDER_WIDTH_THIN);
canvas.setColorStroke(Colors.LIGHT_GRAY);
float paddingLeft = 22f;
for (int i = 0; i <= this.maxValue; i++)
{
canvas.moveTo(llx + paddingLeft, lly + (this.scaleHeight * (i + 1)));
canvas.lineTo(urx, lly + (this.scaleHeight * (i + 1)));
}
// Vertical line
canvas.moveTo(llx + (((urx - llx) + paddingLeft) / 2), ury);
canvas.lineTo(llx + (((urx - llx) + paddingLeft) / 2), lly);
canvas.stroke();
// Fat line left and right
canvas.moveTo(llx, ury);
canvas.lineTo(llx, lly);
canvas.moveTo(urx, ury);
canvas.lineTo(urx, lly);
canvas.setLineWidth(0.8f);
canvas.setColorStroke(Colors.MEDIUM_GRAY);
canvas.stroke();
canvas.saveState();
canvas.restoreState();
}
The bar charts are tables where each cell has a cell event for gradient and border. The bar charts are added to the scaleBackground PdfPCell of the first piece of code and have following PdfPCellEvents (example of black part of the chart):
public void cellLayout(PdfPCell cell, Rectangle rect, PdfContentByte[] canvases)
{
PdfContentByte backgroundCanvas = canvases[PdfPTable.BACKGROUNDCANVAS];
float llx = rect.getLeft();
float lly = rect.getBottom();
float urx = rect.getRight();
float ury = rect.getTop();
// Draw background
// Define shading with direction and color
PdfShading shading = PdfShading.simpleAxial(this.writer,
llx, ury,
llx, lly,
Colors.BAR_CHART_BLACK_LIGHT, Colors.BAR_CHART_BLACK_DARK);
PdfShadingPattern pattern = new PdfShadingPattern(shading);
backgroundCanvas.setShadingFill(pattern);
// Draw shape with defined shading
backgroundCanvas.moveTo(llx, ury);
backgroundCanvas.lineTo(llx, lly);
backgroundCanvas.lineTo(urx, lly);
backgroundCanvas.lineTo(urx, ury);
backgroundCanvas.lineTo(llx, ury);
backgroundCanvas.fill();
backgroundCanvas.saveState();
backgroundCanvas.restoreState();
// Draw border
PdfContentByte lineCanvas = canvases[PdfPTable.LINECANVAS];
float lineWidth = Constants.BORDER_WIDTH_THIN;
lineCanvas.setLineWidth(lineWidth);
lineCanvas.moveTo(llx, ury - lineWidth);
lineCanvas.lineTo(llx, lly);
lineCanvas.lineTo(urx, lly);
lineCanvas.lineTo(urx, ury - lineWidth);
lineCanvas.setColorStroke(BaseColor.BLACK);
lineCanvas.stroke();
lineCanvas.saveState();
lineCanvas.restoreState();
}
This is the order of the different direct content layers:
PdfPtable.BASECANVAS—Anything placed here will be under the table.
PdfPtable.BACKGROUNDCANVAS—This is the layer where the backgrounds are
drawn.
PdfPtable.LINECANVAS—This is the layer where the lines are drawn.
PdfPtable.TEXTCANVAS—This is the layer where the text goes. Anything placed
here will cover the table.
This was taken from the book "iText in Action - Second Edition."
You also ask about saveState() and restoreState(). This is explained in Chapter 2 of the iText 7 tutorial:
First we save the current graphics state with the saveState() method, then we change the state and draw whatever lines or shapes we want to draw, finally, we use the restoreState() method to return to the original graphics state. All the changes that we applied after saveState() will be undone. This is especially interesting if you change multiple values (line width, color,...) or when it's difficult to calculate the reverse change (returning to the original coordinate system).
Your code was too long for me to inspect, but I highly doubt that saveState()/restoreState() would be the cause of your problem.
I would try to avoid nesting tables as much as possible. It is usually much easier (and more efficient) to use colspan and rowspan.
If this doesn't solve your problem, please explain your problem in one sentence.
I want to create a 3D shadow effect on images that I place in a pdf. I am using itextpdf.
The question is similar to :
Adding shadow effect on iText elements
but with images and not texts.
My images are placed in table cells. As long as the table is not completed, no way to get the actual size of the image nor its coordinates in the page, this makes it tricky.
Any brilliant id ?
Thanks and regards,
Sylvain
4 hours later, I found a way to do it using PdfCellEvent and drawing what I need inside the cell's padding.
static class ShadowRectangle implements PdfPCellEvent {
public void cellLayout(PdfPCell cell, Rectangle rect,
PdfContentByte[] canvas) {
PdfContentByte lCb = canvas[PdfPTable.LINECANVAS];
// Paddings for the border
int paddingHorizontal = 8;
int paddingVertical = 8;
// Width of the shadow
int shadowwidth = 7;
// Calculate border location and size
float left = rect.getLeft() + paddingHorizontal;
float bottom = rect.getBottom() + paddingVertical;
float width = rect.getWidth() - 2 * paddingHorizontal;
float height = rect.getHeight() - 2 * paddingVertical;
lCb.saveState();
lCb.setColorFill(BaseColor.GRAY);
// Draw the shadow at the bottom
lCb.rectangle(left + shadowwidth, bottom - shadowwidth, width, shadowwidth);
lCb.fill();
// Draw the shadow at the right
lCb.rectangle(left + width, bottom - shadowwidth, shadowwidth, height);
lCb.fill();
//lCb.setColorStroke(BaseColor.RED);
// Draw the border
//lCb.rectangle(left, bottom, width, height);
lCb.stroke();
lCb.restoreState();
}
}
// And I fill the PdfPTable this way
PdfPTable lImages = new PdfPTable(NB_PICTURE_PER_ROW);
lImages.getDefaultCell().setBorder(Rectangle.NO_BORDER);
// ... in a loop
lImageBoucle = Image.getInstance(lFile.getCanonicalPath() + File.separator + lTabPhotos[i]);
PdfPCell lCell = new PdfPCell();
lCell.setImage(lImageBoucle);
lCell.setBorder(Rectangle.NO_BORDER);
lCell.setPadding(8);
PdfPCellEvent lShadowRectangle = new ShadowRectangle();
lCell.setCellEvent(lShadowRectangle);
lImages.addCell(lCell);
I am producing PDF file using itextsharp,
I am printing 2 strings leading1 & leading2.
The problem is when ever leading1 lenght increases, its effecting leading2 and its gets trimmed.
But I want to print leading1 & leading2 in next line, if no.of characters in leading1 is increased.
Basically leading2 is hardcoded as = YOU DIDIT. But leading1 is dynamic value.
So , I just want to know how to position and wrap long-text.
Here is my code...
Can anyone help me in doing this?
PdfContentByte cb = writer.getDirectContent();
cb.saveState();
cb.beginText();
cb.setFontAndSize(baseFontMedium, 10f);
// float x = 6.4392f * commonView.INCH;
float x = 6.47f * commonView.INCH;
float y = pageSize.getHeight() - (1.13f * commonView.INCH);
cb.setCMYKColorFillF(0f, 0f, 0f, 0.77f);
cb.setTextMatrix(1, 0, 0, 1, x, y);
cb.showText(leading1);
x += new Chunk(leading1, fontMedium10Pt).getWidthPoint();
cb.setCMYKColorFillF(1f, 0f, 0f, 0f);
cb.setTextMatrix(1, 0, 0, 1, x, y);
cb.showText(leading2);
cb.endText();
cb.restoreState()
You have chosen to add text using PDF syntax at the lowest level. This means that you need to calculate the length of every single piece of text that you are adding to your document, and then distribute the text by adding it in different showText() sequences, making sure to adjust the coordinates correctly.
That is hard.
However, you have also chosen to use iText, which means that you can have iText do this work for you. For instance: if you want to add a snippet of text inside a specific rectangle, then you can define a ColumnText object, define a Rectangle, add the text as a Paragraph and go()!
PdfContentByte cb = writer.getDirectContent();
ColumnText ct = new ColumnText(cb);
ct.setSimpleColumn(new Rectangle(36, 600, 200, 800));
ct.addElement(new Paragraph("I want to add this text in a rectangle defined by the coordinates llx = 36, lly = 600, urx = 200, ury = 800"));
int status = ct.go();
Now the text "I want to add this text in a rectangle defined by the coordinates llx = 36, lly = 600, urx = 200, ury = 800" will be wrapped inside a rectangular area defined by the coordinates llx = 36, lly = 600, urx = 200, ury = 800. The status variable will give you an indication whether or not the text was fully rendered (or if it didn't fit completely).
Further reading:
How to fit a String inside a rectangle?
How can I truncate text within a bounding box?
Separating redundant code from pdf generator function
...
If this example helps you, please help me understand what I can do to make sure that other developers do not make the same mistake you made, and explain what made you write the code you wrote instead of trying ColumnText first. Your information will help me in writing a better book.