具有不同大小文本的 iText 7 连字符

iText 7 hyphen with different size Text

我需要为每个字母添加某些属性(不同大小的字母、不同的字体等)

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

辅助方法:

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

如何在这种情况下添加正确的连字符?

iText 7 目前在 Text 级别进行断字。默认情况下,分词也在 Text 级别完成。当您想为部分单词添加特定样式或属性时,这对于像您这样的情况有点限制。

为了更好地说明问题,让我们为段落添加一些背景和一些相对较小的宽度以强制拆分某些单词:

Paragraph p = new Paragraph().setBackgroundColor(ColorConstants.GRAY).setWidth(200);

如果我们运行这个例子,我们会看到以下结果:

所以 letters 这个词被强行分成了几个部分,这不是我们通常想要的。

请注意,我提到在 Text 级别 默认情况下 会进行分词。可以使用 p.setProperty(Property.RENDERING_MODE, RenderingMode.HTML_MODE); 覆盖该行为,这将导致处理单词更接近 HTML 所做的。这是我们将得到的结果:

所以从某种意义上说,没有意外的分词发生更好,但仍然没有断字。

虽然很难实现 100% 正确的断字,因为断字发生在 Text 级别,如果您将尽可能多的文本组合到一个 Text 元素中,断字如果可能的话会发生。因此,让我们稍微调整一下将文本拆分为 Text 个元素的代码。除了帮助解决断字问题之外,它还会使整个布局过程更 CPU 和内存效率更高,因为我们将创建更少的 Text 个实例:

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

这就是我们 运行 代码得到的结果:

没有添加断字,幸好这只是因为没有足够的宽度来断字 letters。如果我们使用 205 点而不是 200 作为我们的宽度,我们会得到想要的结果:

最终代码(辅助方法附在上面):

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