当前位置: 首页 > 知识库问答 >
问题:

在Itext7中使用自定义字体定位文本有些奇怪

邹斌
2023-03-14

我正在开发一个程序,它可以创建几个pdf文档,并将不同的文本放在同一个位置。文本应该放在一个特定的区域,如果它的宽度不适合它应该包装。它还有一个自定义字体,在该区域可能会有不同的对齐方式。它应该与顶部垂直对齐,因为当该区域布置为三条线,而我只有一条线时,它应该出现在顶部。最后,我需要在字体大小方面保持领先地位。

精确的文本定位很重要(例如,我需要“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像素。为了调试,我在段落中添加了边框,事情变得更加奇怪。

我的代码创建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();
}

我需要你的帮助。我做错了什么,或者是否有其他方法在特定区域使用对齐和字体布局文本?提前谢谢你。

更新21.09.17也尝试变体从这个问题与SedgwickAveDisplay:

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

结果与第二张截图上的结果相同。

共有2个答案

梁丘经艺
2023-03-14

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

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

导入com.sun.javafx.tk.工具包,字体来自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;
        }
    }

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

魏翰
2023-03-14

这是一个特定于字体的问题。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);

请注意,你必须非常小心这种低层次的布局,注意你正在做的事情,并尽可能少地使用它。

 类似资料:
  • 我正在使用IText7从html字符串生成pdf。现在,我需要对段落应用自定义颜色和自定义字体或字体系列。 如何使用Itext7实现这一点? 谢谢

  • 问题内容: 我正在尝试在使用wkhtmltopdf生成的PDF中使用自定义字体。我读到您不能使用google webfonts,而wkhtmltopdf使用truetype .ttf文件。谁能确认?因此,我从Google webfont下载了一个.ttf文件,并将其放入服务器中,然后使用了字体: 和字体系列: 现在应该以Jolly Lodger字体呈现的文本根本没有出现,页面为空白。 我究竟做错了

  • 问题内容: 我有一台Mac,并且安装了一种名为“ 7 Segment”的字体(它显示在Font Book中)。当我使用Helvetica(或类似字体)而不是浏览器的默认字体时,它仍然没有显示正确的字体。该页面仅需要在此计算机上显示。我将如何使用此页面上的字体?谢谢。 问题答案: 您需要使用css-property font-face来声明您的字体。 例:

  • 有人知道记事本++中使用的字体吗?它们是它自己的字体还是系统的字体,是否可以在记事本++(设置->样式配置器->字体样式)中使用不同的(非系统的,用户创建的)字体,或者只是编辑现有的字体?

  • 问题内容: 我看到一些新网站在其网站上使用自定义字体(常规Arial,Tahoma等除外)。 并且它们支持大量的浏览器。 怎么做到的?同时,如果可能的话,还会阻止人们自由下载字体。 问题答案: 通常,您可以在CSS中使用自定义字体。这是一个非常基本的示例: 然后,简单地在特定元素上使用字体: (是您的选择器)。 请注意,某些字体格式并非在所有浏览器上都有效;您可以使用fontsquirrel.co

  • 问题内容: 我正在尝试在JFrame中使用特殊字体,但遇到了问题。我有一个这样定义的JLabel: 并且我有一个名为CUSTOMFONT-MEDIUM.TTF(TrueType字体)的文件,但是编写了以下内容: 代码会编译,并且一切正常,除了不会显示“ lab”,因此没有文本。我想这是因为我从未指定字体大小,但是我所做的任何尝试都失败了。有人可以帮我吗? 问题答案: 您创建的字体必须先在中注册,以