I am converting POSPrint-data to a PDF. At one Point i need to strech the Text over 2 Lines, but with the width of the normal text. I'm trying to archive that like this:
CONTENT.beginText();
Matrix textMatrix = new Matrix();
textMatrix.scale(1f, 2f);
CONTENT.setTextMatrix(textMatrix);
CONTENT.newLineAtOffset(50, 50);
CONTENT.setCharacterSpacing(line.getLineSpacing());
CONTENT.showText(restOfLine);
CONTENT.endText();
sadly this results in the text not showing up at all. If i remove the lines for adding the textmatrix, or setting the matrix scale values to 1 this workes without any problem:
CONTENT.beginText();
Matrix textMatrix = new Matrix();
textMatrix.scale(1f, 1f);
CONTENT.setTextMatrix(textMatrix);
CONTENT.newLineAtOffset(50, 50);
CONTENT.setCharacterSpacing(line.getLineSpacing());
CONTENT.showText(restOfLine);
CONTENT.endText();
or
CONTENT.beginText();
CONTENT.newLineAtOffset(50, 50);
CONTENT.setCharacterSpacing(line.getLineSpacing());
CONTENT.showText(restOfLine);
CONTENT.endText();
Does anybody know why this happens?
I use PDFBox 2.0.25
If you are scaling the TextMatrix, also the position of the matrix is affected. To fix this unwanted behavior of moving the scaled text, you have to divide the textposition also by the scale.
CONTENT.beginText();
Matrix textMatrix = new Matrix();
textMatrix.scale(1f, 2f);
CONTENT.setTextMatrix(textMatrix);
CONTENT.newLineAtOffset(50, 50/2f); //devide y position by scale
CONTENT.setCharacterSpacing(line.getLineSpacing());
CONTENT.showText(restOfLine);
CONTENT.endText();
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:
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.
The first "foo" is normal, but the second one is so huge I can only see the base of the "f". The font-size is the default.
BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = image.createGraphics();
Shape ellipse = new Ellipse2D.Double(0, 0, 1.0, 1.0);
g2.setPaint(Color.RED);
g2.drawString("foo", 100, 100);
g2.scale(500.0f / length, 500.0f / length);
g2.drawString("foo", 1, 1);
Changing the font size will not help because it only allows int sizes and the size that would make sense for the scale is something like 0.02.
The reason I need to draw the text while in scaled space is because I am drawing a grid of nodes and I want to scale the coordinates to the number of nodes in each dimension. That way I do not have to do complicated calculations.
I need the text to label the edges.
Update: I am able to get the text by doing the following sequence each time I want to draw the text: saving the transform, translating to the desired location in scaled space, unscaling, drawing the text at (0, 0), and restoring the transform.
You can use Font method
public Font deriveFont(float size)
to obtain desired font size font. Guess the 0.02 shoud be fine.
I'm playing around with the 2.0.0-SNAPSHOT, and I want to set the page to landscape and also rotate my picture. So I've done page.setRotation(90);
There seems to be a bug with using PDFBox and AffineTransform
This code doesn't do anything like I'd expect:
AffineTransform at = new AffineTransform(w, 0, 0, h, 20, 20);
at.translate(0.5, 1);
at.rotate(Math.toRadians(90));
Width and Height have to be tiny to keep the image on the page, rotate by itself squishes the image, and translate before rotate seems to scale the image huge.
Is this a bug, or am I just not understanding PDFBox?
Don't do an extra translation, instead put the translation when creating the AT. Remember that the rotation is around the bottom-left axis, so add the width w to the x-position.
PDPage page = new PDPage();
document.addPage(page);
page.setRotation(90);
PDPageContentStream contentStream = new PDPageContentStream(document, page);
int x = 150;
int y = 300;
// draw unrotated
contentStream.drawXObject(ximage, x, y, ximage.getWidth() / 2, ximage.getHeight() / 2);
// draw 90° rotated, placed on the right of the first image
AffineTransform at = new AffineTransform(ximage.getHeight() / 2, 0, 0, ximage.getWidth() / 2, x + ximage1.getWidth(), y);
at.rotate(Math.toRadians(90));
contentStream.drawXObject(ximage, at);
This will draw the image twice, once normally and once rotated 90°, and positioned to the right. "/2" is used to scale 50%, you can of course use another factor. Note that "/2" is not used for the initial x position, because the (scaled) width is needed twice. Once to position to the old position (because of the axis!), and once to move it to the right so that the images don't overlap.
Note also that getHeight() and getWidth() are reversed, for the page rotation.
AffineTransform at = new AffineTransform(w, 0, 0, h, 20, 20);
at.translate(0.5, 1);
at.rotate(Math.toRadians(90));
Width and Height have to be tiny to keep the image on the page, rotate by itself squishes the image, and translate before rotate seems to scale the image huge.
Is this a bug, or am I just not understanding PDFBox?
It is not a bug, it simply is math. You merely have to be aware that if you have an AffineTransform at and then call at.translate(...) or at.rotate(...), you do not set the translation / rotation part of the affine transformation to the given values but instead replace your transformation by the composition of the former transformation and the translation / rotation.
This means that e.g.
AffineTransform at = new AffineTransform(w, 0, 0, h, 20, 20);
at.translate(0.5, 1);
is not the same as
AffineTransform at = new AffineTransform(w, 0, 0, h, 20.5, 21);
as you might have expected, but instead
AffineTransform at = new AffineTransform(w, 0, 0, h, 20 + w/2, 20 + h);
This is why Width and Height have to be tiny to keep the image on the page - translate(0.5, 1) pushes very far otherwise.
As the order in which you compose the transformation matters, you might be happier if you created your transformation in this order:
AffineTransform at = AffineTransform.getTranslateInstance(0.5, 1);
at.rotate(Math.toRadians(90));
af.concatenate(new AffineTransform(w, 0, 0, h, 20, 20));
PS: As Tilman said: Remember that the rotation is around the bottom-left, so this composition will rotate off-screen, too. Simply add h+20 to the x coordinate of the initial translation.
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.