【问题标题】:Placing an image over text, by using the text postiton in a PDF using PDFBox.通过使用 PDFBox 在 PDF 中使用文本 postiton 将图像放置在文本上。
【发布时间】:2018-06-04 23:11:04
【问题描述】:

结果是图像未正确放置在文本上。我是不是把文本位置弄错了?

这是一个关于如何获取每个坐标的 x/y 坐标和大小的示例 PDF中的字符

public class MyClass extends PDFTextStripper {

    pdocument = PDDocument.load(new File(fileName));

    stripper = new GetCharLocationAndSize();
    stripper.setSortByPosition(true);
    stripper.setStartPage(0);
    stripper.setEndPage(pdocument.getNumberOfPages());
    Writer dummy = new OutputStreamWriter(new 
    ByteArrayOutputStream());
    stripper.writeText(pdocument, dummy);


 /*
 * Override the default functionality of PDFTextStripper.writeString()
 */
@Override
protected void WriteString(String string, List<TextPosition> 
textPositions) throws IOException {

     String imagePath = "image.jpg";
     PDImageXObject pdImage = 
     PDImageXObject.createFromFile(imagePath,pdocument);

     PDPageContentStream contentStream = new 
     PDPageContentStream(pdocument, stripper.getCurrentPage(), true, 
     true);

     for (TextPosition text : textPositions) {

         if (text.getUnicode().equals("a")) {
         contentStream.drawImage(pdImage, text.getXDirAdj(), 
         text.getYDirAdj(), text.getWidthDirAdj(),text.getHeightDir()); 
       }
       }
    contentStream.close();
    pdocument.save("newdoc.pdf");
    }
    }

【问题讨论】:

    标签: pdfbox


    【解决方案1】:

    检索合理坐标

    您使用text.getXDirAdj()text.getYDirAdj() 作为内容流中的xy 坐标。这是行不通的,因为 PDFBox 在文本提取期间使用的坐标已转换为他们喜欢用于文本提取目的的坐标系,参见。 JavaDocs:

    /**
     * This will get the text direction adjusted x position of the character.
     * This is adjusted based on text direction so that the first character
     * in that direction is in the upper left at 0,0.
     *
     * @return The x coordinate of the text.
     */
    public float getXDirAdj()
    
    /**
     * This will get the y position of the text, adjusted so that 0,0 is upper left and it is
     * adjusted based on the text direction.
     *
     * @return The adjusted y coordinate of the character.
     */
    public float getYDirAdj()
    

    对于TextPosition text,您应该改用

    text.getTextMatrix().getTranslatex()
    

    text.getTextMatrix().getTranslateY()
    

    但即使是这些数字也可能需要更正,参见。 this answer,因为 PDFBox 已将矩阵乘以平移,使裁剪框的左下角成为原点。

    因此,如果PDRectangle cropBox是当前页面的裁剪框,则使用

    text.getTextMatrix().getTranslatex() + cropBox.getLowerLeftX()
    

    text.getTextMatrix().getTranslateY() + cropBox.getLowerLeftY()
    

    (PDFBox 的这个坐标规范化是一个 PITA,适用于任何真正想要使用文本坐标的人......)

    其他问题

    您的代码还有一些其他问题,其中一个问题在您共享的文档中变得清晰:您在未重置图形上下文的情况下附加到页面内容流:

    PDPageContentStream contentStream = new PDPageContentStream(pdocument,
            stripper.getCurrentPage(), true, true);
    

    具有此签名的构造函数假定您不想重置上下文。使用带有额外 boolean 参数的那个并将其设置为 true 以请求上下文重置:

    PDPageContentStream contentStream = new PDPageContentStream(pdocument,
            stripper.getCurrentPage(), true, true, true);
    

    现在上下文已重置,位置再次正常。

    不过,这两个构造函数都已弃用,因此不应使用。在开发分支中,它们已经被删除。而是使用

    PDPageContentStream contentStream = new PDPageContentStream(pdocument,
            stripper.getCurrentPage(), AppendMode.APPEND, true, true);
    

    不过,这引入了另一个问题:您为每个 writeString 调用创建一个新的 PDPageContentStream。如果每次都重置上下文,saveGraphicsState/restoreGraphicsState 对的嵌套可能会变得非常深。因此,您应该为每个页面只创建一个这样的内容流,并在该页面的所有writeString 调用中使用它。

    因此,您的文本剥离器子类可能如下所示:

    class CoverCharByImage extends PDFTextStripper {
        public CoverCharByImage(PDImageXObject pdImage) throws IOException {
            super();
            this.pdImage = pdImage;
        }
    
        final PDImageXObject pdImage;
        PDPageContentStream contentStream = null;
    
        @Override
        public void processPage(PDPage page) throws IOException {
            super.processPage(page);
            if (contentStream != null) {
                contentStream.close();
                contentStream = null;
            }
        }
    
        @Override
        protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
            if (contentStream == null)
                contentStream = new PDPageContentStream(document, getCurrentPage(), AppendMode.APPEND, true, true);
    
            PDRectangle cropBox = getCurrentPage().getCropBox();
    
            for (TextPosition text : textPositions) {
                if (text.getUnicode().equals("a")) {
                    contentStream.drawImage(pdImage, text.getTextMatrix().getTranslateX() + cropBox.getLowerLeftX(),
                            text.getTextMatrix().getTranslateY() + cropBox.getLowerLeftY(),
                            text.getWidthDirAdj(), text.getHeightDir());
                }
            }
        }
    }
    

    (CoverCharacterByImage 内部类)

    它可以这样使用:

    PDDocument pdocument = PDDocument.load(...);
    
    String imagePath = ...;
    PDImageXObject pdImage = PDImageXObject.createFromFile(imagePath, pdocument);
    
    CoverCharByImage stripper = new CoverCharByImage(pdImage);
    stripper.setSortByPosition(true);
    Writer dummy = new OutputStreamWriter(new ByteArrayOutputStream());
    stripper.writeText(pdocument, dummy);
    pdocument.save(...);
    

    (CoverCharacterByImage 测试testCoverLikeLez)

    导致

    等等

    【讨论】:

    • 适用于大多数 pdf。发现一组pdf,从 text.getTextMatrix().getTranslateY() + cropBox.getLowerLeftY() 和 text.getTextMatrix().getTranslateX() + cropBox.getLowerLeftX() 得到的坐标不准确。
    • @Lez 请分享示例。我很感兴趣是否还有其他的规范化让一个人的生活变得艰难......
    • 这是一个示例文件[drive.google.com/file/d/1SCoB1RyvQSNy3aVjj_KZ70IO2ksN6qfM/…。感觉它与Skia/PDF m55的软件编码有关
    • @Lez 谢谢!我会看看,但很可能不会在下周开始之前。
    • @Lez 我对您的代码进行了更深入的了解,并在我的答案中添加了更改,这些更改也是使其与您的 EMPLOYMENTCONTRACTTEMPLATE.pdf 等 PDF 一起运行所必需的。顺便说一句,要解决的另一个问题是对旋转文本的支持......
    猜你喜欢
    • 2015-09-16
    • 1970-01-01
    • 2021-09-03
    • 2012-01-25
    • 1970-01-01
    • 2014-04-27
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多