How can I truncate text within a bounding box? - java

I am writing content to a PdfContentByte object directly using PdfContentByte.showTextAligned, I'd like to know how I can stop the text overflowing a given region when writing.
If possible it would be great if iText could also place an ellipsis character where the text does not fit.
I can't find any method on ColumnText that will help either. I do not wish the content to wrap when writing.

Use this:
int status = ColumnText.START_COLUMN;
ColumnText ct = new ColumnText(cb);
ct.setSimpleColumn(rectangle);
status = ct.go();
Make sure that you define rectangle in a way so that only one line fits, use ColumnText.hasMoreText(status) to find out if you need to add an ellipsis character.

Related

Android Text with Spannable image on top/bottom of text

I am developing a simple devotional app, which has a Kannada (a language in India) sentence to be displayed. I am successful in using typeface and displaying the content.
In few places I have word which has a line on top/bottom of the word as shown below. I tried with a spannable image but I am still not able to achieve it properly.
This is a sample of the code which I am referring to. Here I am using a small icon to display it in between the string.
Spannable span1 = new SpannableString("The imageplace");
Drawable android = TestImageActivity.this.getResources().getDrawable(R.drawable.end);
android.setBounds(5, 0, 20, 5);
ImageSpan image = new ImageSpan(android, ImageSpan.ALIGN_BASELINE);
span1.setSpan(image, 3, 4, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
tvTextImage3.setText(span1);
ImageSpan extends ReplacementSpan so any characters you are spanning won't get rendered, as the TextLayout is expecting that the span itself will be doing all the rendering.
What I would recommend is implementing your own ReplacementSpan subclass. Since it looks like your graphics are associated with one character, you would wrap the single character.
In the getSize override, you would use start and end to index into text and get the character(s) you are spanning, then use paint.getTextBounds() to measure the width of the text and return that value. You want the width calculation to work in a way that the width of the span doesn't affect the default spacing of the text.
Another thing this method might need to do is change the FontMetrics by increasing the ascent and descent in order to give you some space to draw the lines.
In the draw override, you use the paint to render the text that isn't being rendered within the span. The paint and font metrics should already have the proper values so that your text render looks like the surrounding text. Of course, you'll also render the line graphics you want.
For some sample code, take a look at my answer to a similar question. This has all the pieces I just discussed.
If you want me to write some code for this, you'll need to provide some code that gives me a starting point with some actual Kannada text along with what the lines are and where they go. I don't even know if Kannada text is LTR or RTL; that might affect how the span subclass is coded. Preferably the text would correspond to the image you posted so I can see how it should look when it's working.

Do I have to set the offset or cursor on everyline in PDFBox?

I'm trying to learn PDFBox v2.0 but there seems to be few useful examples and tutorials to get started.
I want to create a simple PDF with text only (no images and fancy stuff) and it looks as the following:
1- Introduction
This is an intro to the document
2- Data
2.1- DataPart1
This is some text here....
2.2- DataPart2
This is another text right here!
3- More Information
Some important informational text here...
I have written the following code:
PDPage firstPage = getNewPage();
PDPageContentStream firstContentStream = new PDPageContentStream(document, firstPage);
firstContentStream.setFont(HEADING_FONT, HEADING_FONT_SIZE);
firstContentStream.setNonStrokingColor(Color.BLUE);
firstContentStream.beginText();
firstContentStream.newLineAtOffset(MARGIN, firstPage.getMediaBox().getHeight() - MARGIN);
firstContentStream.showText("1- Introduction");
firstContentStream.endText();
firstContentStream.setFont(TEXT_FONT, TEXT_FONT_SIZE);
firstContentStream.setNonStrokingColor(Color.BLACK);
firstContentStream.beginText();
firstContentStream.showText("This document lists all the impacts that have been observed during the QA validation of the version v3.1 build");
firstContentStream.endText();
firstContentStream.close();
document.addPage(firstPage);
The text "This is the document intro" appears at the end of the page!
My question: do I have to set the cursor or the offset on every line???!
This seems like hard work. Does this mean I have to compute the string width and compare it to the page width and jump to new lines whenever the text fills the entire line?
I really don't understand.
And also how do I make it generate new pages whenever the current page is full?
Could you please give one simple example? Thanks in advance
Can't I just do something like:
contentStream.addTextOnNewLine("Some text here...");
First of all please be aware that PDFBox has a very low-level text drawing API only, i.e. most methods you call directly correspond to a single instruction in the PDF content stream. In particular PDFBox does not come with a (publicly accessible) layout'ing functionality but you essentially have to do the layout'ing in your code. This is both a freedom (you are not bound by the limits of an existing machinery) and a burden (you have to do everything yourself).
In your case that means:
Does this mean I have to compute the string width and compare it to the page width and jump to new lines whenever the text fills the entire line?
Yes. Confer the answers #Tilman linked for you
How to generate multiple lines in PDF using Apache pdfbox
Creating a new PDF document using PDFBOX API
and search some more on stackoverflow, there are further answer on different variations of that theme, e.g. for justified text blocks here.
But there is a detail in your code which in your case makes things possibly harder than necessary:
My question: do I have to set the cursor or the offset on every line???!
While you do have to re-position for every new line, it suffices to re-position by offset from the start of the previous line if you still are in the same text object, i.e. if you have not called endText() and beginText() in-between. E.g.:
PDPage firstPage = getNewPage();
PDPageContentStream firstContentStream = new PDPageContentStream(document, firstPage);
firstContentStream.setFont(HEADING_FONT, HEADING_FONT_SIZE);
firstContentStream.setNonStrokingColor(Color.BLUE);
firstContentStream.beginText();
firstContentStream.newLineAtOffset(MARGIN, firstPage.getMediaBox().getHeight() - MARGIN);
firstContentStream.showText("1- Introduction");
// removed firstContentStream.endText();
firstContentStream.setFont(TEXT_FONT, TEXT_FONT_SIZE);
firstContentStream.setNonStrokingColor(Color.BLACK);
// removed firstContentStream.beginText();
firstContentStream.newLineAtOffset(0, -TEXT_FONT_SIZE); // reposition, tightly packed text lines
firstContentStream.showText("This document lists all the impacts that have been observed during the QA validation of the version v3.1 build");
firstContentStream.endText();
firstContentStream.close();
document.addPage(firstPage);
newLineAtOffset(0, -TEXT_FONT_SIZE) sets the text insertion position at the same x coordinate as the start of the previous line and TEXT_FONT_SIZE units lower.
(Using TEXT_FONT_SIZE here will result in pretty tightly set text lines; you may want to use a higher value, e.g. 1.4 times the font size.)
Considering your description you might want to use a positive x offset value for the line after a heading, though.
The text "This is the document intro" appears at the end of the page!
This happens because the PDF instruction generated by firstContentStream.beginText() resets the text insertion position to the origin (0, 0) which by default is the lower left corner.
And also how do I make it generate new pages whenever the current page is full?
Well, it does not happen automatically, you explicitly have to do that when you want to switch to a new page. And you do that more or less exactly like you created your first page.

Java- textwrap with drawString()

The normal syntax for java.awt.Graphics.drawString() includes defining the X and Y coordinates for the string. I want to draw a string that has a width and height attribute for text wrapping. Is there a way to specify a java.awt.Rectangle for the string you are trying to draw? Would I have to write something to decide where the word wrap should cut it off or is their an easier way?
Thanks!
This is actually a little bit complicated and somewhat convoluated, but essentially, you need to make use of the LineBreakMeasurer class to help you break the text down into manageable chunks based on the available width
See Drawing Multiple Lines of Text for more details and an example
I don't think there's a built in way to do that, but you can implement your own wrapping if you get the width of the string with something like below:
FontMetrics fontMetrics = new FontMetrics(Graphics.getFont());
int width = fontMetrics.stringWidth("Potentially needs wrapping");
Then split the message into multiple strings as necessary.

Can I generate superscipt in iText using input only?

I'm involved in a project where we generate pdf-files using iText. I want to generate superscipt text and I know that methods exists for converting chunks of text to superscript however, I am in a situation where it is desirable to decide based on the input whether or not a piece of text should be superscript or not. An optimal solution would be something like: 2<superscript>nd</superscript> to generate the text "2nd".
Is this, or anything like this possible?
It is worth noting that I do not write code directly against iText, there are layers between. This is why I need to decide what is superscript and not based on the input.
If you can't modify the code that's using iText, then no, there's no way. iText doesn't parse the text you put in a Chunk.
In order to get a similar behaviour to what you want to achieve, you would need to do HTML to PDF conversion, with iText's companion XMLWorker for example.
When you find a <superscript> tag you can simply create a Chunk with correct properties:
public Chunk addSuperscript(String text) {
float leading = determineCurrentLeading();
Font f = determineCurrentFont();
Font supFont = new Font(f);
supFont.setSize(f.getSize() / 2f);
Chunk c = new Chunk(text, supFont);
c.setTextRise(leading / 2f);
return c;
}
Then the chunk will be added to a Paragraph, a PdfPCell,... Depending on your implementation, you can determine the current leading and font used.
Hope this will help you.

How can I change the font size of annotations in iTextPdf?

I'm programming a system that generates a PDF file containing user-submitted comments. I want to reduce the size of the comments, i.e. the font size of each annotation. I'm using the Java version of iTextPdf.
My code:
PdfAnnotation annotation = PdfAnnotation.createText(pdfStamper.getWriter(),
new Rectangle(x, valor, x+100f, valor+100f), "authors",
comentario.getComentario(), true, "Comment");
annotation.setColor(Color.ORANGE);
Can I reduce the font size like this?
annotation.setFontSize("2px");
I've never used iTextPdf, but its API says that the PdfAnnotation class has no setFontSize() method.
However, it does contain a setDefaultAppearanceString() method, which takes a PdfContentByte as an argument. And PdfContentByte has a setFontAndSize() method.
From what I can see in the API, I would say: no, it's not possible.
Maybe you can use the setAppearance method. On the iText website is an example which creates an PdfAppearance object that owns a setFontAndSize method.
You have two options.
You can set the default font & size, as Lord Torgamus described.
You can use the Rich Text value of the annotation, which you have to directly access using the methods PdfAnnotation inherited from PdfDictionary:
annotation.put(PdfName.RC, new PdfString( "<font size=\"whatever\">" +
comentario.getComentario() +
"</font>" ) );
Note that iText cannot render Rich Text field appearances (at least not yet), so you have to turn off appearance generation so the resulting PDF will ask the viewer to do it:
myStamper.setGenerateAppearances( false );
Yep, have to use a stamper.
It's probably just easier to go with Torg's suggestion... but mind the rabbit. It doesn't play well with others.
PS: Splash's setAppearance() trick would work, but then you'd have to draw the whole thing yourself... background, borders, and text layout (which is much easier thanks to ColumnText).

Categories