Java 如何在iText 7中查找文本位置和边界

Java 如何在iText 7中查找文本位置和边界,java,itext7,Java,Itext7,正如评论所说,这项工作很难,所以我想一步一步地解决它,看看它的局限性。首先,我将关注下面的第一个问题。 产地: 我想替换PDF文件中的文本以进行翻译,例如,将英文PDF转换为中文PDF 我的解决办法是: 查找所有文本及其位置矩形 用白色填充所有矩形 将翻译后的文本绘制回相应的矩形(使用正确的重新计算的字体大小) 具体来说,我实现了IEventListener接口来获取渲染信息,并使用此渲染信息查找带有位置矩形的文本 但我遇到了一些问题: 使用“渲染信息”(render info),我无法获得文本

正如评论所说,这项工作很难,所以我想一步一步地解决它,看看它的局限性。首先,我将关注下面的第一个问题。

产地:

我想替换PDF文件中的文本以进行翻译,例如,将英文PDF转换为中文PDF

我的解决办法是:

  • 查找所有文本及其位置矩形
  • 用白色填充所有矩形
  • 将翻译后的文本绘制回相应的矩形(使用正确的重新计算的字体大小)
  • 具体来说,我实现了IEventListener接口来获取渲染信息,并使用此渲染信息查找带有位置矩形的文本

    但我遇到了一些问题:

  • 使用“渲染信息”(render info),我无法获得文本的确切位置(起点准确,但终点有时不准确)
  • 字体大小因语言和字体而异,例如,一种语言的一种字体的字体大小18可能比另一种语言的另一种字体占用更多的空间
  • 很难合并行或识别段落(不同行中的文本应翻译为一个块)
  • 有没有比当前解决方案更好的方法来实现我的目标

    或者,任何人都可以就上述问题提供一些建议

    已更新

    第一个问题的例子:

    我只记录文本及其在渲染中遇到的位置,并在每个文本块周围绘制一个矩形。代码是:

    Main.java中的Main

    PdfDocument pdfDoc = new PdfDocument(new PdfReader(srcFileName), new PdfWriter(destFileName));
    SimplePositionalTextEventListener listener = new SimplePositionalTextEventListener();
    new PdfCanvasProcessor(listener).processPageContent(pdfDoc.getFirstPage());
    List<SimpleTextWithRectangle> result = listener.getResultantTextWithPosition();
    
    int R = 0, G = 0, B = 0;
    for(SimpleTextWithRectangle textWithRectangle: result) {
        R += 40; R = R % 256;
        G += 20; G = G % 256;
        B += 80; B = B % 256;
        PdfCanvas canvas = new PdfCanvas(pdfDoc.getPage(pageNumber));
        canvas.setStrokeColor(new DeviceRgb(R, G, B));
        canvas.rectangle(textWithRectangle.getRectangle());
        canvas.stroke();
    }
    
    pdfDoc.close();
    
    SimpleTextWithRectangle.java

    private Rectangle rectangle;
    private String text;
    
    public SimpleTextWithRectangle(Rectangle rectangle, String text) {
        this.rectangle = rectangle;
        this.text = text;
    }
    
    public Rectangle getRectangle() {
        return rectangle;
    }
    
    该文件是:

    处理后,标题为: 正如我们所见,有些隐藏文本可以在“渲染信息”中找到,但在PDF阅读器应用程序中不可见。如果我们深入研究每个文本块,我们可以看到
    renderInfo.getText()
    有时不能完全匹配我们在PDF中看到的文本

    处理后,页脚为:

    正如我们所看到的,矩形边界不能完全覆盖文本,这就是我在问题1中提到的

    不正确的框坐标是iText 7 CMap处理中的错误造成的

    虫子 解析类型0字体的命名编码CMap时,例如GBK-EUC-H,使用此
    CMapEncoding
    构造函数的
    else
    分支:

    public CMapEncoding(String cmap, String uniMap) {
        this.cmap = cmap;
        this.uniMap = uniMap;
        if (cmap.equals(PdfEncodings.IDENTITY_H) || cmap.equals(PdfEncodings.IDENTITY_V)) {
            cid2Uni = FontCache.getCid2UniCmap(uniMap);
            isDirect = true;
            this.codeSpaceRanges = IDENTITY_H_V_CODESPACE_RANGES;
        } else {
            cid2Code = FontCache.getCid2Byte(cmap);
            code2Cid = cid2Code.getReversMap();
            this.codeSpaceRanges = cid2Code.getCodeSpaceRanges();
        }
    }
    
    现在
    FontCache.getCid2Byte(cmap)
    使用
    cmapcibyte
    在以下位置构建映射:

    public static CMapCidByte getCid2Byte(String cmap) {
        CMapCidByte cidByte = new CMapCidByte();
        return parseCmap(cmap, cidByte);
    }
    
    cmapcibyte
    (可能还有其他CMap类)的一个特点是它存储映射逆:

    private Map<Integer, byte[]> map = new HashMap<>();
    [...]
    void addChar(String mark, CMapObject code) {
        if (code.isNumber()) {
            byte[] ser = decodeStringToByte(mark);
            map.put((int)code.getValue(), ser);
        }
    }
    
    private Map=new HashMap();
    [...]
    void addChar(字符串标记,CMapObject代码){
    if(code.isNumber()){
    字节[]ser=解码字符串字节(标记);
    map.put((int)code.getValue(),ser);
    }
    }
    
    也许这样做是因为最常用的查找方向是相反的。只要原始映射是内射的,也就是说,所有的键都映射到不同的值,这是可以的

    不幸的是,CMAP不需要是内射的。例如,对于GBK-EUC-H我们有cidrange条目

    814
    


    你的问题太宽泛了。您遇到和描述的困难是PDF格式固有的。你正在尝试做一些以前没有人做过的事情。广告1:这显然不是故意的。请分享发生这种情况的PDF示例,并指出结束位置错误的文本。(在某些特殊情况下,在字符框外绘制glyph。这很难解决。但如果您的示例不是这样,那么解决方案可能是可行的。)补充2:这可能是真的,“字体大小”的定义有一定的主观成分。但是,由于您没有显示示例,我们无法判断您是否正确确定字体大小。广告3:这是你任务中很自然的一部分。“有没有比当前解决方案更好的方法来实现我的目标?”-好吧,你可以通过使用白色填充所有矩形来改善结果,实际上是删除文本对象。不过,这需要一些更低级的操作。“以上问题的一些建议”-这很难,因为1和2有点含糊不清(没有示例,没有代码),3只是对项目子任务的描述。实现目标的更好方法是:您的PDF很可能是从其他源格式生成的,如MS Word或HTML。以其他格式进行翻译,然后再次转换为PDF。如果你可以访问源文件,那么这是最简单的方法。我删除了我的否决票,因为更新后这个问题变得更加有趣。不过,我同意@AmedeeVanGasse的观点,即最好在源代码处翻译PDF,然后从头开始创建新的PDF。关于检测段落,请参见Joris Schellekens的search for Stack Overflow答案。他在非结构化PDF的结构识别方面做了很多工作。谢谢你的解释。我已经研究了PDF编码,并试图避免错误的大小,但失败了。你能为这个例子提供一个解决方案吗?我担心这个bug必须在iText源代码中修复,如果不有效地重新编程所讨论的大部分类,我看不到立即的解决方法。此外,适当的修复还必须包括检查是否存在也隐式假定编码为内射的任何相关代码块。当逐步浏览代码时,我得到的印象是,例如,对于某些文本提取操作,数据通过编码前后映射。这也必须改变,因为它可能会改变非内射编码的内容。
    private Map<Integer, byte[]> map = new HashMap<>();
    [...]
    void addChar(String mark, CMapObject code) {
        if (code.isNumber()) {
            byte[] ser = decodeStringToByte(mark);
            map.put((int)code.getValue(), ser);
        }
    }