在Itext7

时间:2017-09-20 16:48:29

标签: java-8 itext7

我正在开发一个程序,该程序可以创建多个pdf文档,并将不同的文本放在它们的相同位置。 文本应该放在一个特定的区域,如果它不适合宽度,它应该包装。它还具有自定义字体,并且可以在该区域中进行不同的对齐。它应该垂直对齐顶部,因为当区域布局为三条线并且我只有一条时,它应该显示在顶部。最后,我需要保持font-size级别的领先优势。

在文本定位中精确定位非常重要(例如,我需要“Hello world”中左上角的“H”显示为0,100)。

现在,我正在使用

canvas.showTextAligned(paragraph, 0, 300,
                    TextAlignment.valueOf(alignment),
                    VerticalAlignment.TOP);

但是,当我尝试使用不同的字体实现它时,它具有与所需y = 300不同的偏移量。此外,偏移量因字体而异。对于Helvetica(使用50个fontSize),偏移量约为13像素,Oswald约为17像素,而SedgwickAveDisplay则为90像素。 为了调试目的,我在段落中添加了边框,事情变得更加奇怪。

黑体: ofsset sith Helvetica SedgwickAveDisplay: offset with SedgwickAveDisplay

我的代码创建pdf的完整片段如下:

public byte[] createBadgeInMemory(int i) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    PdfDocument newPdf = new PdfDocument(new PdfWriter(out));
    srcPdf.copyPagesTo(1,1,newPdf);
    PdfPage page = newPdf.getFirstPage();
    PdfCanvas pdfCanvas = new PdfCanvas(page);
    Canvas canvas = new Canvas(pdfCanvas, newPdf, pageSize);

    File defaultFont = new File("src/main/resources/fonts/Helvetica.otf");
    PdfFont font  = PdfFontFactory
            .createFont(fontPath == null ? defaultFont.getAbsolutePath() : fontPath,
                    PdfEncodings.IDENTITY_H, true);

    String value = "Example word";
    Paragraph paragraph = new Paragraph(value);
    float textWidth = font.getWidth("Example", 50);
    paragraph.setWidth(textWidth);
    switch (alignment) {
        case("CENTER"):
            textWidth /= 2;
            break;
        case("RIGHT"):
            break;
        default:
            textWidth = 0;
            break;
    }

    paragraph.setFont(font)
            .setFontSize(fontSize)
            .setFixedLeading(fontSize)
            .setFontColor(new DeviceRgb(red, green, blue))
            .setMargin(0)
            .setPadding(0);
    paragraph.setBorderTop(new DashedBorder(Color.BLACK, 0.5f))
            .setBorderBottom(new DashedBorder(Color.BLACK, 0.5f))
            .setBorderRight(new DashedBorder(Color.BLACK, 0.5f));
    paragraph.setHyphenation(new HyphenationConfig(0,
            "Example".length()));

    canvas.showTextAligned(paragraph,
            0 + textWidth,
            300,
            TextAlignment.valueOf(alignment),
            VerticalAlignment.TOP);

    newPdf.close();

    return out.toByteArray();
}

我也试过here的变种,但由于某种原因,矩形内的文字会在某些时候切掉(例如,如果我的区域宽度为100px,我输入的文字片段就知道我只知道100 px(在font.getWidth(value)的帮助下,我的文字剪切了近80像素)。 我还没有找到一种方法来对齐矩形内的文本。 这是Rectangle的结果。实线边框是矩形边框。如你所见,它在“Redundant”中剪切了字母“t”。它也应该在第二行包含“word”,但它不包含。 我从一个例子中复制了代码。 rectangle problems

我需要你的帮助。我做错了什么或者可能有另一种方法在对齐和字体的特定区域中布局文本? 先感谢您。

更新21.09.17 还尝试了this question与SedgwickAveDisplay:

的变体
paragraph.setHorizontalAlignment(HorizontalAlignment.LEFT);
            paragraph.setVerticalAlignment(VerticalAlignment.TOP);
            paragraph.setFixedPosition( 0, 300 - textHeight, "Example".length());
            doc.add(paragraph);

结果与第二个屏幕截图相同。

2 个答案:

答案 0 :(得分:0)

这是特定于字体的问题。 iText猜测有关字体字形的信息,即错误的bbox。

有一种快速而肮脏的方法可以调整此行为。您可以为文本创建自定义渲染器并调整其中的计算位置。这样一个类的一个例子如下:

class CustomTextRenderer extends TextRenderer {
    private CustomTextRenderer(Text text) {
        super(text);
    }

    @Override
    public LayoutResult layout(LayoutContext layoutContext) {
        LayoutResult result = super.layout(layoutContext);
        Rectangle oldBbox = this.occupiedArea.getBBox().clone();
        // you can also make the height more than font size or less if needed
        this.occupiedArea.setBBox(oldBbox.moveUp(oldBbox.getHeight() - fontSize).setHeight(fontSize)); 
        yLineOffset = fontSize * 0.8f; // here you config the proportion of the ascender
        return result;
    }

    @Override
    public IRenderer getNextRenderer() {
        return new CustomTextRenderer((Text) modelElement);
    }
}

为了应用新的渲染逻辑,您必须以下列方式使用它:

Text text = new Text(value);
text.setNextRenderer(new CustomTextRenderer(text));
Paragraph paragraph = new Paragraph(text);

请注意,您必须非常小心这种低级布局,请注意您正在做并尽可能少地使用它。

答案 1 :(得分:0)

最后,我创建了一个适合我的变体。

pdfCanvas.beginText()
                .setFontAndSize(font, fontSize)
                .setLeading(fontSize)
                .moveText(0, 300);

        numberOfLines = 0;
        sumOfShifts = 0;
        float maxWidth = computeStringWidth("Exaxple");
        String[] words = value.split("\\s");
        StringBuilder line = new StringBuilder();
        line.append(words[0]);
        float spaceWidth = computeStringWidth(" ") ;
        float lineWidth;
        for (int index = 1; index < words.length; index++) {
            String word = words[index];
            float wordWidth = computeStringWidth(word) ;
            lineWidth = computeStringWidth(line.toString()) ;
            if (lineWidth + spaceWidth + wordWidth <= maxWidth) {
                line.append(" ").append(word);
            } else {
                showTextAligned(alignment, pdfCanvas, line.toString(), lineWidth, maxWidth);
                line.delete(0, line.length());
                line.append(word);
            }
        }
        if(line.length() != 0) {
            lineWidth = computeStringWidth(line.toString()) ;
            showTextAligned(alignment, pdfCanvas, line.toString(), lineWidth, maxWidth);
        }

        pdfCanvas.endText();

作为computeStringWidth(String str),我使用了

Toolkit.getToolkit().getFontLoader().computeStringWidth(String str, Font font);
来自import com.sun.javafx.tk.Toolkit

来自javafx.scene.text.Font的字体。我之所以选择它,是因为我在应用的其他部分使用它。

showTextAligned(...)是我自己的方法,看起来像这样:

private void showTextAligned(String alignment,
                                  PdfCanvas pdfCanvas,
                                  String line,
                                  float lineWidth,
                                  float maxWidth) {
        switch (alignment) {
            case "CENTER": {
                float shift = (maxWidth - lineWidth) / 2 - sumOfShifts;
                pdfCanvas.moveText(shift, 0);
                if(numberOfLines == 0) pdfCanvas.showText(line);
                else pdfCanvas.newlineShowText(line);
                numberOfLines++;
                sumOfShifts += shift;
                break;
            }
            case "RIGHT": {
                float shift = maxWidth - lineWidth - sumOfShifts;
                pdfCanvas.moveText(shift, 0);
                if(numberOfLines == 0) pdfCanvas.showText(line);
                else pdfCanvas.newlineShowText(line);
                numberOfLines++;
                sumOfShifts += shift;
                break;
            }
            default:
                pdfCanvas.moveText(0, 0);
                if(numberOfLines == 0) pdfCanvas.showText(line);
                else pdfCanvas.newlineShowText(line);
                numberOfLines++;
                break;
        }
    }

在我的项目中,我使用了我的变体,因为它让我有机会更深入地使用连字符(例如,我可以在将来添加功能以避免将介词作为行中的最后一个词)。