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

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

陶沛
2023-03-14

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

起源:

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

我的解决方案是:

  1. 查找位置为矩形的所有文本

具体来说,我实现了IEventListener接口来获取渲染信息,并使用此渲染信息来查找带有位置矩形的文本。

但我遇到了一些问题:

  1. 使用“渲染信息”,我无法获得文本的确切位置(起点准确,但终点有时不准确)

有没有比当前解决方案更好的方法来实现我的目标?

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

更新:

第一个问题的例子:

我只是记录文本和它们在渲染中遇到的位置,并在每个文本块周围画一个矩形。代码是:

主要的。JAVA

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

SimplePositionTextEventListener。java(实现IEventListener):

private List<SimpleTextWithRectangle> textWithRectangleList = new ArrayList<>();

private void renderText(TextRenderInfo renderInfo) {
    if (renderInfo.getText().trim().length() == 0)
        return;
    LineSegment ascent = renderInfo.getAscentLine();
    LineSegment descent = renderInfo.getDescentLine();

    float initX = descent.getStartPoint().get(0);
    float initY = descent.getStartPoint().get(1);
    float endX = ascent.getEndPoint().get(0);
    float endY = ascent.getEndPoint().get(1);

    Rectangle rectangle = new Rectangle(initX, initY, endX - initX, endY - initY);

    SimpleTextWithRectangle textWithRectangle = new SimpleTextWithRectangle(rectangle, renderInfo.getText());
    textWithRectangleList.add(textWithRectangle);
}

public List<SimpleTextWithRectangle> getResultantTextWithPosition() {
    return textWithRectangleList;
}

@Override
public void eventOccurred(IEventData data, EventType type) {
    renderText((TextRenderInfo) data);
}

@Override
public Set<EventType> getSupportedEvents() {
    return Collections.unmodifiableSet(new LinkedHashSet<>(Collections.singletonList(EventType.RENDER_TEXT)));
}

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文件

共有1个答案

王昊
2023-03-14

不正确的框坐标是iText 7 CMap处理中的错误的结果。

当解析类型0字体(例如GBK-EUC-H)的命名编码CMap时,使用该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)使用CMapCidByte在以下位置构建映射:

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

也许这样做是因为最常用的查找方向是相反的。只要原始映射是内射的,即所有键都映射到不同的值,这是可以的。

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

<21> <7e> 814 

<aaa1> <aafe> 814 
<aba1> <abc0> 908 

因此,在导入此编码时,后面的映射会覆盖字符代码0x21的许多映射。。0x7e。

在手头的文档中,页脚文本中确实使用了编码为GBK-EUC-H的字体。因此,对于这种字体,许多单字节代码为0x21。。iText的字体信息中缺少0x7e。

这一系列代码以单间距字体对比例西文字符进行编码,尤其是替代代码0xaaa1。。0xaafe和0xaba1。。0xabc0编码的西文字符与等距字符相同。

在示例文档的页脚区域中,这些比例拉丁字符被使用。由于缺少映射,一些iText 7代码路径中的这些字符被替换字符符号替换(例如文本提取本身不返回西文字符,而是“”),在一些路径中,它们完全丢失(例如,当计算文本块的长度时,这些西文字符被忽略)。

因此,字符块的长度计算不正确,因此边界框的大小和位置都不正确。

这也解释了为什么每一行上错误放置的边界框从该行第一次出现西文字符时开始,以及为什么在西文字符最多的行上缺少最大的框大小。

 类似资料:
  • 是否有方法返回字符在JTextField中的位置。我的意思是,如果我有一个JTextField,其中有一些值。例如,该字段包含值ABCDEFJ。用户决定将光标放在字符“C”之后,以输入新值。是否有一种方法来获得他输入新角色的位置。在本例中,将返回3。

  • 下面是我的函数,它从glyph空间到用户空间进行计算 下面是绘制提取的矩形的函数: 我不知道我做错了什么。有什么想法吗?

  • 问题内容: 有没有一种我可以用来简单地找到文件位置的方法?我正在尝试允许用户选择一个文件并打开它,但是我必须让JFileChooser仅选择该文件并将该位置发送到另一种方法。最好的方法是什么? 问题答案: javadoc show中的示例显示了执行此操作的步骤: 那是在做什么。取得结果并将其传递给另一种方法。

  • 问题内容: 我正在运行Python 2.7。 我有三个文本文件:,,和。现在,包含我要搜索的几行并将该部分替换为中的内容。这是一个简单的示例: data.txt find.txt replace.txt 所以,在上面的例子中,我要搜索的所有出现,以及在数据和更换这些线路。 我在使用正确的方法时遇到了一些麻烦,因为我的内存大约为1MB,所以我想尽可能地提高效率。一种愚蠢的方法是将所有内容连接成一个长

  • 本文向大家介绍如何使用HTML5地理位置查找位置?,包括了如何使用HTML5地理位置查找位置?的使用技巧和注意事项,需要的朋友参考一下 HTML5 Geolocation API使您可以与自己喜欢的网站共享位置。JavaScript可以捕获您的纬度和经度,并且可以发送到后端Web服务器,并进行精美的位置感知操作,例如查找本地商家或在映射上显示您的位置。 地理位置API使用全局导航器对象的新属性,即