I try to draw text inside rectangle which fit rectangle size, like my previous question, I want text align center in rectangle.
The problem is display text has wrong Y coordinate, look like this one:
And here is my code:
PdfContentByte cb = writer.getDirectContent();
Rectangle rect = new Rectangle(100, 150, 100 + 120, 150 + 50);
cb.saveState();
ColumnText ct = new ColumnText(writer.getDirectContent());
Font font = new Font(BaseFont.createFont());
float maxFontSize;
// try to get max font size that fit in rectangle
font.setSize(maxFontSize);
ct.setText(new Phrase("test", font));
ct.setSimpleColumn(rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop());
ct.go();
// draw the rect
cb.setColorStroke(BaseColor.BLUE);
cb.rectangle(rect.getLeft(), rect.getBottom(), rect.getWidth(), rect.getHeight());
cb.stroke();
cb.restoreState();
I even draw text like this:
cb.saveState();
cb.beginText();
cb.moveText(rect.getLeft(), rect.getBottom());
cb.setFontAndSize(BaseFont.createFont(), maxSize);
cb.showText("test");
cb.endText();
cb.setColorStroke(BaseColor.BLUE);
cb.rectangle(rect.getLeft(), rect.getBottom(), rect.getWidth(), rect.getHeight());
cb.stroke();
And got the result:
So I wonder how can itext render text base on the coordinates? Because I use the same rectangle frame for text and rectangle bound.
I'm not sure if I understand your question correctly. I'm assuming you want to fit some text into a rectangle vertically, but I don't understand how you calculate the font size, and I don't see you setting the leading anywhere (which you can avoid by using ColumnText.showAligned()).
I've created an example named FitTextInRectangle which results in the PDF chunk_in_rectangle.pdf. Due to rounding factors (we're working with float values), the word test slightly exceeds the rectangle, but the code shows how to calculate a font size that makes the text fit more or less inside the rectangle.
In your code samples, the baseline is defined by the leading when using ColumnText (and the leading is wrong) or the bottom coordinate of the rectangle when using showText() (and you forgot to take into account value of the descender).
Related
I'm working with PDFBox and trying to rotate an image and have it position correctly on the screen. The design editor I'm using outputs the following information about images that may be useful.
Image bounding box top-left coords (I'm using the bottom left coords to better suit PDFBox coord space.)
Image rotation in degrees
Image width & height
The translation appears to be off.
// Rotation
AffineTransform rotation = new AffineTransform();
rotation.rotate(Math.toRadians(360 - element.getAngle()),
element.getLeft() + scaledWidth/2,
adjustedYPos + scaledHeight/2);
stream.transform(new Matrix(rotation));
// Position & scale
AffineTransform mat = new AffineTransform(scaledWidth,
0,
0,
scaledHeight,
element.getLeft(),
adjustedYPos);
// Draw the final image
stream.drawImage(pdfImage, new Matrix(mat));
Rotations are based on the center of the image as an anchor point.
You can correctly position images using code like this:
void placeImage(PDDocument document, PDPage page, PDImageXObject image, float bbLowerLeftX, float bbLowerLeftY, float width, float height, float angle) throws IOException {
try ( PDPageContentStream contentStream = new PDPageContentStream(document, page, AppendMode.APPEND, true, true) ) {
float bbWidth = (float)(Math.abs(Math.sin(angle))*height + Math.abs(Math.cos(angle))*width);
float bbHeight = (float)(Math.abs(Math.sin(angle))*width + Math.abs(Math.cos(angle))*height);
contentStream.transform(Matrix.getTranslateInstance((bbLowerLeftX + .5f*bbWidth), (bbLowerLeftY + .5f*bbHeight)));
contentStream.transform(Matrix.getRotateInstance(angle, 0, 0));
contentStream.drawImage(image, -.5f*width, -.5f*height, width, height);
}
}
(PlaceRotatedImage utility method)
This method accepts coordinates as they are meaningful in the context of PDF, i.e. coordinate values and dimensions according to the default user space coordinate system of the given page (y values increasing upwards, the origin arbitrary but fairly fairly often in the lower left), (bounding) box given by lower left corner, angles as in math in counterclockwise radians...
If you need the parameters differently, you can fairly easily adapt the method, though. If you e.g. get the upper left corner of the bounding box instead of the lower left, you can simply subtract the bounding box height determined in the method as bbHeight to calculate the lower left y coordinate used here.
You can use this method like this:
PDPage page = ...;
PDRectangle mediaBox = page.getMediaBox();
float bbLowerLeftX = 50;
float bbLowerLeftY = 100;
try ( PDPageContentStream contentStream = new PDPageContentStream(document, page) ) {
contentStream.moveTo(bbLowerLeftX, mediaBox.getLowerLeftY());
contentStream.lineTo(bbLowerLeftX, mediaBox.getUpperRightY());
contentStream.moveTo(mediaBox.getLowerLeftX(), bbLowerLeftY);
contentStream.lineTo(mediaBox.getUpperRightX(), bbLowerLeftY);
contentStream.stroke();
}
PDImageXObject image = PDImageXObject.createFromByteArray(document, IOUtils.toByteArray(resource), "Image");
placeImage(document, page, image, bbLowerLeftX, bbLowerLeftY, image.getWidth(), image.getHeight(), (float)(Math.PI/4));
placeImage(document, page, image, bbLowerLeftX, bbLowerLeftY, .5f*image.getWidth(), .5f*image.getHeight(), 0);
placeImage(document, page, image, bbLowerLeftX, bbLowerLeftY, .25f*image.getWidth(), .25f*image.getHeight(), (float)(9*Math.PI/8));
(PlaceRotatedImage test testPlaceByBoundingBox)
This code draws the left and bottom lines corresponding to the left and bottom side of the given lower left bounding box coordinates and draws an image at different magnifications and angles with the constant given lower left bounding box corner.
The result looks like this:
You can find more information on the calculation of the bounding box sizes in these answers:
Calculate Bounding box coordinates from a rotated rectangle
How to get width and height of the bounding box of a rotated rectangle
How to get size of a rotated rectangle
Find the Bounding Rectangle of Rotated Rectangle
...
I am trying to draw a rectangle around multiline text in iText.
The user will be able to enter some lines of text. The font size of the text might be different and it can be formatted (bold, underlined...).
I use this code to draw the text:
ColumnText ct = new ColumnText(cb);
Phrase phrase = new Phrase("Some String\nOther string etc...\n test");
ct.setSimpleColumn(myText......);
ct.addElement(phrase);
ct.go();
I know how to draw a rectangle, but I am not able to draw a rectangle outlining this text.
It sounds as if you are missing only a single piece of the puzzle to meet your requirement. That piece is called getYLine().
Please take a look at the DrawRectangleAroundText example. This example draws the same paragraph twice. The first time, it adds a rectangle that probably looks like the solution you already have. The second time, it adds a rectangle the way you want it to look:
The first time, we add the text like this:
ColumnText ct = new ColumnText(cb);
ct.setSimpleColumn(120f, 500f, 250f, 780f);
Paragraph p = new Paragraph("This is a long paragraph that doesn't"
+ "fit the width we defined for the simple column of the"
+ "ColumnText object, so it will be distributed over several"
+ "lines (and we don't know in advance how many).");
ct.addElement(p);
ct.go();
You define your column using the coordinates:
llx = 120;
lly = 500;
urx = 250;
ury = 780;
This is a rectangle with lower left corner (120, 500), a width of 130 and a height of 380. Hence you draw a rectangle like this:
cb.rectangle(120, 500, 130, 280);
cb.stroke();
Unfortunately, that rectangle is too big.
Now let's add the text once more at slightly different coordinates:
ct = new ColumnText(cb);
ct.setSimpleColumn(300f, 500f, 430f, 780f);
ct.addElement(p);
ct.go();
Instead of using (300, 500) as lower left corner for the rectangle, we ask the ct object for its current Y position using the getYLine() method:
float endPos = ct.getYLine() - 5;
As you can see, I subtract 5 user units, otherwise the bottom line of my rectangle will coincide with the baseline of the final line of text and that doesn't look very nice. Now I can use the endPos value to draw my rectangle like this:
cb.rectangle(300, endPos, 130, 780 - endPos);
cb.stroke();
I need to rotate a link rectangle using Java iText.
The original link rectangle appears in red. The rotated link rectangle appears in green.
My code:
PdfReader reader = new PdfReader( "input/blank.pdf" );
PdfStamper stamper = new PdfStamper( reader, new FileOutputStream( "output/blank_stamped.pdf" ) );
Rectangle linkLocation = new Rectangle( 100, 700, 100 + 200, 700 + 25 );
PdfName highlight = PdfAnnotation.HIGHLIGHT_INVERT;
PdfAnnotation linkRed = PdfAnnotation.createLink( stamper.getWriter(), linkLocation, highlight, "red" );
PdfAnnotation linkGreen = PdfAnnotation.createLink( stamper.getWriter(), linkLocation, highlight, "green" );
BaseColor baseColorRed = new BaseColor(255,0,0);
BaseColor baseColorGreen = new BaseColor(0,255,0);
linkRed.setColor(baseColorRed);
linkGreen.setColor(baseColorGreen);
double angleDegrees = 10;
double angleRadians = Math.PI*angleDegrees/180;
stamper.addAnnotation(linkRed, 1);
linkGreen.applyCTM(AffineTransform.getRotateInstance(angleRadians));
stamper.addAnnotation(linkGreen, 1);
stamper.close();
But this code does not rotate the recangle.
Please take a look at the following screen shot:
I have added 5 annotations to a simple Hello World file.
The first two are link annotations. Their position is defined by the rectangles linkLocation1 and linkLocation2:
Rectangle linkLocation1 = new Rectangle(30, 770, 120, 800);
PdfAnnotation link1 = PdfAnnotation.createLink(stamper.getWriter(),
linkLocation1, PdfAnnotation.HIGHLIGHT_INVERT, action);
link1.setColor(BaseColor.RED);
stamper.addAnnotation(link1, 1);
Rectangle linkLocation2 = new Rectangle(30, 670, 60, 760);
PdfAnnotation link2 = PdfAnnotation.createLink(stamper.getWriter(),
linkLocation2, PdfAnnotation.HIGHLIGHT_INVERT, action);
link2.setColor(BaseColor.GREEN);
stamper.addAnnotation(link2, 1);
The green rectangle looks like a rotated version of the red rectangle, but that's not really true: we just defined the "clickable" area that way. I don't understand why you'd want to get this effect by introducing a rotation. Why? Because a rotation always needs a rotating point. Suppose that you would introduce a rotation, what would be your rotation point? The (0, 0) coordinate? That would lead to strange results, wouldn't it?
Introducing a rotation for does make sense for some types of annotations though. In my example, I introduced three stamp annotations:
Rectangle linkLocation3 = new Rectangle(150, 770, 240, 800);
PdfAnnotation stamp1 = PdfAnnotation.createStamp(stamper.getWriter(), linkLocation3, "Landscape", "Confidential");
stamper.addAnnotation(stamp1, 1);
Rectangle linkLocation4 = new Rectangle(150, 670, 240, 760);
PdfAnnotation stamp2 = PdfAnnotation.createStamp(stamper.getWriter(), linkLocation4, "Portrait", "Confidential");
stamp2.setRotate(90);
stamper.addAnnotation(stamp2, 1);
Rectangle linkLocation5 = new Rectangle(250, 670, 340, 760);
PdfAnnotation stamp3 = PdfAnnotation.createStamp(stamper.getWriter(), linkLocation5, "Portrait", "Confidential");
stamp3.setRotate(45);
stamper.addAnnotation(stamp3, 1);
In this case, I introduce a rotation angle using the setRotate() method. This rotates the CONFIDENTIAL stamp inside the rectangle we defined. As you can see, this makes sense because the annotation does have actual content: the rotation has an impact on the way you read the word CONFIDENTIAL. In the case of the clickable area of the link annotation, there is no such content to be rotated.
If this doesn't answer your question, please rephrase your question because I don't think anyone can answer it in its current state.
Update
Please take a look at ISO-32000-1 aka the PDF specification. You'll discover that a rectangle is defined using 4 values: the x and y coordinate of the lower-left corner of the rectangle and the x and y coordinate of the upper-right corner of the rectangle. These are the two starting points of the horizontal and vertical sides. You want a rectangle that has sides that aren't horizontal/vertical. Obviously that isn't possible as you'd need the coordinates of 4 corner points to achieve that (8 values, not 4). You can achieve this using a polygon defined by QuadPoints.
See ITextShape Clickable Polygon or path
I'm starter with Canvas and Paint. I want to paint a text in a Canvas but it can be longer than the original Bitmap. So the text go out the Bitmap.
Is there some kind of automatic manager for this making a new line when the end is reached? or should I play with heights and distances? Thanks
yes, you can manage this with a StaticLayout or DynamicLayout
The best way is to draw text with StaticLayout:
// init StaticLayout for text
StaticLayout textLayout = new StaticLayout(
gText, paint, textWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
// get height of multiline text
int textHeight = textLayout.getHeight();
// get position of text's top left corner
float x = (bitmap.getWidth() - textWidth)/2;
float y = (bitmap.getHeight() - textHeight)/2;
// draw text to the Canvas center
canvas.save();
canvas.translate(x, y);
textLayout.draw(canvas);
canvas.restore();
See my blogpost for more details.
I would suggest that you also look at this code snippet found here:
https://stackoverflow.com/a/15092729/1759409
As it will manage the writing of your text within a certain width and height and automatically draws onto the canvas correctly.
Does anyone know, how to, in iText, add multiline text in bounding box (with coordinates specified).
I tried
cb.showTextAligned(
PdfContentByte.ALIGN_LEFT,
text,
bounds.getLeft(),
TOTAL_HEIGHT-bounds.getTop(),
0 );
But it does not support newlines.
I also tried
PdfContentByte cb = writer.getDirectContent();
cb.moveText(300,400);
document.add(new Paragraph("TEST paragraph\nNewline"));
This supports newlines but does not react to moveText, so I don't know how to put it at given position or better: bounding box.
I suspect chunks or PdfTemplate or maybe table might help, but i don't (yet) know how to put it together. TIA for help.
Try this:
ColumnText ct = new ColumnText(cb);
Phrase myText = new Phrase("TEST paragraph\nAfter Newline");
ct.setSimpleColumn(myText, 34, 750, 580, 317, 15, Element.ALIGN_LEFT);
ct.go();
parameters of SetSimpleColumn are:
the phrase
the lower left x corner (left)
the lower left y corner (bottom)
the upper right x corner (right)
the upper right y corner (top)
line height (leading)
alignment.
ColumnText ct = new ColumnText(content);
ct.setSimpleColumn(
new Phrase("Very Long Text"),
left=20, bottom=100, right=500, top=500,
fontSize=18, Element.ALIGN_JUSTIFIED);
ct.go(); // for drawing