I need to add certain properties to each letter (different size of letters, different font, etc)
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
Document doc = new Document(pdfDoc);
PdfFont helveticaFont = PdfFontFactory.createFont(StandardFonts.HELVETICA);
PdfFont helveticaBoldFont = PdfFontFactory.createFont(StandardFonts.HELVETICA_BOLD);
Paragraph p = new Paragraph();
String s = "all text is written in red, except the letters b and g; they are written in blue and green.";
for (int i = 0; i < s.length(); i++) {
p.add(returnCorrectColor(s.charAt(i), helveticaFont, helveticaBoldFont));
}
p.setHyphenation(new HyphenationConfig("en", "US", 2, 2)); // doesnt work
doc.add(p);
doc.close();
Helper method:
private static Text returnCorrectColor(char letter, PdfFont helveticaFont, PdfFont helveticaBoldFont) {
if (letter == 'b') {
return new Text("b")
.setFontColor(ColorConstants.BLUE)
.setFont(helveticaBoldFont)
.setFontSize(15);
} else {
return new Text(String.valueOf(letter))
.setFontColor(ColorConstants.RED)
.setFont(helveticaFont)
.setFontSize(12);
}
}
How can I add correct hyphen with such conditions?
iText 7 does hyphenation at Text
level at the moment. Word break is also done at the Text
level by default. This is somewhat of a limitation for cases like yours when you want to add specific styling or properties to parts of words.
To illustrate the problem better, let's add some background to the paragraph and some relatively small width to force some words being split:
Paragraph p = new Paragraph().setBackgroundColor(ColorConstants.GRAY).setWidth(200);
If we run the example, we see the following result:
So the word letters
was just forcefully split into parts which is not what we typically want.
Note that I mentioned that word break is done at the Text
level by default. It's possible to override that behavior with p.setProperty(Property.RENDERING_MODE, RenderingMode.HTML_MODE);
which will result in treating words closer to what HTML does. This is the result we would get:
So it's better in a sense that no unexpected word splits happened, but still there is no hyphenation.
While 100% correct hyphenation will be hard to achieve, because hyphenation happens on the Text
level, if you combine as much of the text as possible into a single Text
element, the hyphenation will happen if possible. So let's adjust our code that splits the text into Text
elements a bit. On top of helping to solve the hyphenation problem, it's also going to make the whole layout process more CPU and memory-efficient as we will create fewer Text
instances:
private static java.util.List<Text> splitTextIntoElements(String text, PdfFont helveticaFont, PdfFont helveticaBoldFont) {
java.util.List<Text> result = new java.util.ArrayList<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
if (sb.length() != 0 && getCharCategory(text.charAt(i)) != getCharCategory(sb.charAt(sb.length() - 1))) {
result.add(createTextChunk(sb, helveticaFont, helveticaBoldFont));
sb.setLength(0);
}
sb.append(text.charAt(i));
}
if (sb.length() != 0) {
result.add(createTextChunk(sb, helveticaFont, helveticaBoldFont));
}
return result;
}
private static final int BLUE_CATEGORY = 1;
private static final int RED_CATEGORY = 2;
private static int getCharCategory(char ch) {
if (ch == 'b') {
return BLUE_CATEGORY;
}
return RED_CATEGORY;
}
private static Text createTextChunk(StringBuilder sb, PdfFont helveticaFont, PdfFont helveticaBoldFont) {
int chunkCategory = getCharCategory(sb.charAt(sb.length() - 1));
Text chunk = new Text(sb.toString());
applyTextProperties(chunk, chunkCategory, helveticaFont, helveticaBoldFont);
return chunk;
}
private static void applyTextProperties(Text text, int category, PdfFont helveticaFont, PdfFont helveticaBoldFont) {
if (category == BLUE_CATEGORY) {
text.setFontColor(ColorConstants.BLUE)
.setFont(helveticaBoldFont)
.setFontSize(15);
} else {
text.setFontColor(ColorConstants.RED)
.setFont(helveticaFont)
.setFontSize(12);
}
}
This is what we get if we run the code:
The hyphenation was not added, but fortunately it's just because there was not enough width to hyphenate word letters
. If we use 205
points instead of 200
as our width, we get the desired result:
Final code (helper methods are attached above):
Document doc = new Document(pdfDocument);
PdfFont helveticaFont = PdfFontFactory.createFont(StandardFonts.HELVETICA);
PdfFont helveticaBoldFont = PdfFontFactory.createFont(StandardFonts.HELVETICA_BOLD);
Paragraph p = new Paragraph().setBackgroundColor(ColorConstants.GRAY).setWidth(205);
p.setProperty(Property.RENDERING_MODE, RenderingMode.HTML_MODE);
String s = "all text is written in red, except the letters b and g; they are written in blue and green.";
for (Text chunk : splitTextIntoElements(s, helveticaFont, helveticaBoldFont)) {
p.add(chunk);
}
p.setHyphenation(new HyphenationConfig("en", "US", 2, 2));
doc.add(p);