How to draw a rectangle around multiline text - java

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();

Related

iText newline in paragraph or phrase not working [duplicate]

I have the following code to print a text onto a PDF document using iTextSharp:
canvas = stamper.GetOverContent(i)
watermarkFont = iTextSharp.text.pdf.BaseFont.CreateFont(iTextSharp.text.pdf.BaseFont.HELVETICA, iTextSharp.text.pdf.BaseFont.CP1252, iTextSharp.text.pdf.BaseFont.NOT_EMBEDDED)
watermarkFontColor = iTextSharp.text.BaseColor.RED
canvas.SetFontAndSize(watermarkFont, 11)
canvas.SetColorFill(watermarkFontColor)
Dim sText As String = "Line1" & vbCrLf & "Line2"
Dim nPhrase As New Phrase(sText)
ColumnText.ShowTextAligned(canvas, Element.ALIGN_TOP, nPhrase, 0, 50, 0)
However, only the first line ("Line1") is printed, the second line ("Line2") isn't.
Do I have to pass any flags to make that work?
ColumnText.ShowTextAligned has been implemented as a short cut for the use case of adding a single line at a given position with given alignment. Confer the source code documentation:
/** Shows a line of text. Only the first line is written.
* #param canvas where the text is to be written to
* #param alignment the alignment
* #param phrase the <CODE>Phrase</CODE> with the text
* #param x the x reference position
* #param y the y reference position
* #param rotation the rotation to be applied in degrees counterclockwise
*/
public static void ShowTextAligned(PdfContentByte canvas, int alignment, Phrase phrase, float x, float y, float rotation)
For more generic use cases please instantiate a ColumnText, set the content to draw and the outlines to draw in, and call Go().
As documented, the ShowTextAligned() method can only be used to draw a single line. If you want to draw two lines, you have two options:
Option 1: use the method twice:
ColumnText.ShowTextAligned(canvas, Element.ALIGN_TOP, new Phrase("line 1"), 0, 50, 0)
ColumnText.ShowTextAligned(canvas, Element.ALIGN_TOP, new Phrase("line 2"), 0, 25, 0)
Option 2: use ColumnText in a different way:
ColumnText ct = new ColumnText(canvas);
ct.SetSimpleColumn(rect);
ct.AddElement(new Paragraph("line 1"));
ct.AddElement(new Paragraph("line 2"));
ct.Go();
In this code snippet, rect is of type Rectangle. It defines the area where you want to add the text. See How to add text in PdfContentByte rectangle using itextsharp?

Java iText Rotate a Link Rectangle

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

how to do a word wrap on a short string with TextLayout in java Graphics2d?

How would you effectively do a word wrap on a short label String below with TextLayout in java?
My label are only two or three words long
Some examples:
1. Inflatable Greenhouse D10 ;
2. Command and Control Center A5;
3. Jason Boris;
I'd like to wrap the words in such a way to shape as like a square as possible, rather than one long rectangle.
So my question is: What does it take to wrap the building names to the 2nd line, instead of one long line? See pic below:
Is there a way to set the maximum number of characters to be contained in a line of text and wrap the remaining characters to the second line and so on (it would need to account for whitespace)?
For example, I'd like to wrap the name "Residential Quarter D12" into three lines.
Residential
Quarter
D12
and wrap "Command and Control D16" into four lines.
Command
and
Control
D16
Wouldn't it be nice if TextLayout can understand html codes like a regular JLabel!? Then it'll make things easy:
String label = "<html>" + "Inflatable" + "<br>" + "Greenhouse" + "<br>" + "D10" + "</html>";
Note: it doesn't have to be one word per line. But I'd like to have them "centered" on each line
What I have was the following method for generating a BufferedImage of the building name labels o or just the first and last name of a person.
private BufferedImage createLabelImage(
String label, Font font, FontRenderContext fontRenderContext, Color labelColor,
Color labelOutlineColor) {
// Determine bounds.
TextLayout textLayout1 = new TextLayout(label, font, fontRenderContext);
Rectangle2D bounds1 = textLayout1.getBounds();
// Get label shape.
Shape labelShape = textLayout1.getOutline(null);
// Create buffered image for label.
int width = (int) (bounds1.getWidth() + bounds1.getX()) + 4;
int height = (int) (bounds1.getHeight()) + 4;
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
// Get graphics context from buffered image.
Graphics2D g2d = (Graphics2D) bufferedImage.getGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.translate(2D - bounds1.getX(), 2D - bounds1.getY());
// Draw label outline.
Stroke saveStroke = g2d.getStroke();
g2d.setColor(labelOutlineColor);
g2d.setStroke(new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2d.draw(labelShape);
g2d.setStroke(saveStroke);
// Fill label
g2d.setColor(labelColor);
g2d.fill(labelShape);
// Dispose of image graphics context.
g2d.dispose();
return bufferedImage;
}
As you can see, this method can only create a BufferedImage form of label with one line of text only.
As I overlay these BufferedImage labels on a map, they look too long and they overlap one another.
That's why I need to make each label to shape like a square as possible.
Let me try to suggest an algoritm.
Split the label by space to get list of words and measure each word to get array
int[] wordWidths;
int minWidth=max(wordWidths);
int height=the row height const;
int minHeight=height;
int maxHeight=wordWidths.length*height;
int currentWidth=minWidht;
int currentHeight=maxHeight;
while(currentWidth<currentHeight || wordWidths.length>1) {
int mergedWidth=find minimal sum of neighbour words' widths
replace the 2 widths with the mergedWidth reducing the wordWidthssize
currentHeight=wordWidths.length*height;
}
Or you can try to rely on components. I would define a JTextArea instance assigning the label there and trying to play with the wrap reducing width 1 by 1 and measuring preferred height for the width.
When optimal size is achived you can call theWrappedJtextArea.printAll(g) to paint it on your BufferedImage's Graphics.

Correct text position center in rectangle iText

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).

itext multiline text in bounding box

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

Categories

Resources