尝试从 pdf 中提取字形 ID 时缺少一些字形 ID

Some glyph ID's missing while trying to extract glyph ID from pdf

由于 Devanagiri 字形映射到 unicode 字符不正确,我使用以下代码提取字形 ID 并形成我自己的映射以将 ID 映射到正确的 unicode 字符。

public class ExtractCharacterCodes {
public static void testExtractFromSingNepChar() throws IOException {
    PDDocument document = PDDocument.load(new File("C:/PageSeparator/pattern3.pdf"));
    PDFTextStripper stripper = new PDFTextStripper() {
        @Override
        protected void writeString(String text, List<TextPosition> textPositions) throws IOException {
            for (TextPosition textPosition : textPositions) {
                writeString(String.format("%s%s", textPosition.getUnicode(), Arrays.toString(textPosition.getCharacterCodes())));
            }
        }
    };
    //stripper.setSortByPosition(true);
    String text = stripper.getText(document);

    System.out.printf("\n*\n* singNepChar.pdf\n*\n%s\n", text);
}

public static void main(String[] args) throws IOException {
    ExtractCharacterCodes.testExtractFromSingNepChar();
}

}

正在申请此 pdf Nepali pdf

我得到了以下信息:स[1434]नु[1418] [3]त[1414]स्[7021]क[1399]र[1426]ी[1440]क[1399]ा[1438] [3]म[1424]खु्[6990]य[1425] [3]अ[1383]ा[4285]ा[1438]र[1426]ो[1451]प[1420]ी[1440] [3]'[39]ग[1401]ो[1451]रे[1426]'[39] [32] क[1399]ा[1438]ठ[1410]म[1424]ा[1438]ड[1411]ौं[7301]क[1399]ो[1451] [3]ग[1401]ौ[1452]र[1426]ी[1440]घ[1402]ा[1438]ट[1409]ब[1422]ा[1438]ट[1409] [3]प[1420]क्र[7059]ा[1438]उ[1387] [32] ज[1406]न[1418]क[1399]र[1426]ा[1438]ज[1406] [3]स[1434]ा[1438]प[1420]क[1399]ो[1451]ट[1409]ा[1438]त[1414]स्[1439]स्[7021]ब[1422]र[1426] [3]:[29] [3]क[1399]स्[1439]ि[1431]न[1418] [3]अ[1383]स्[1439]ध[1417]क[1399]ा[1438]र[1426]ी[1440] [32]|[124] [32]ज[1406]े[1447]ष्ठ[7399] [3] ८[1481],[44] [32]२[1475]०[1473]७[1480]५[1478] [32] 等等

如您所见,我有一个字符串“सुन”被分隔为स[1434],नु[1418]。我开始制作自己的字形 ID 到字符的映射,但在这种情况下,缺少一个字形 ID。应该是स[1434],न[1441],ु[1418]。我如何获得这个?

原因是 PDFTextStripper 不仅将它从底层解析器检索到的 TextPosition 对象组织成行并添加隐含的空格,它还在转发到之前对它们进行了一些额外的预处理writeString。特别是

  • 抑制重复的重叠字形:创建穷人粗体效果的一种方法是用微小的偏移绘制两次字形,这些重复被抑制;而且它
  • 将包含变音符号的 TextPosition 对象与包含相应基本字形的 TextPosition 合并到表示组合的 Unicode 代码点的 TextPosition

前一个处理步骤可以使用 PDFTextStripper.setSuppressDuplicateOverlappingText(false) 禁用,但后者不能。

您观察到的效果是由于后面的处理步骤。

如果您想在不进行任何预处理的情况下获得字形,即不进行重复抑制和变音符号合并,也不将它们组织成行并添加隐含空格,您可以覆盖 processTextPosition 而不是 writeString:

PDDocument document = PDDocument.load(resource);
PDFTextStripper stripper = new PDFTextStripper() {
    @Override
    protected void processTextPosition(TextPosition textPosition) {
        try {
            writeString(String.format("%s%s", textPosition.getUnicode(), Arrays.toString(textPosition.getCharacterCodes())));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
};

String text = stripper.getText(document);

(ExtractCharacterCodes 测试 testExtractFromPattern3)

您的示例文档现在的结果是

स[1434]ु[1441]न[1418] [3]त[1414]स्[7021]क[1399]र[1426]ी[1440]क[1399]ा[1438] [3]...

如果您仍希望 PDFTextStripper 将字形组织成行并添加隐含的空格,则必须修补 class(或您自己的副本)并在它的 processTextPosition 实现通过替换

来禁用变音符号合并
// test if we overlap the previous entry.
// Note that we are making an assumption that we need to only look back
// one TextPosition to find what we are overlapping.
// This may not always be true. */
TextPosition previousTextPosition = textList.get(textList.size() - 1);
if (text.isDiacritic() && previousTextPosition.contains(text))
{
    previousTextPosition.mergeDiacritic(text);
}
// If the previous TextPosition was the diacritic, merge it into this
// one and remove it from the list.
else if (previousTextPosition.isDiacritic() && text.contains(previousTextPosition))
{
    text.mergeDiacritic(previousTextPosition);
    textList.remove(textList.size() - 1);
    textList.add(text);
}
else
{
    textList.add(text);
}

通过一个简单的

textList.add(text);

顺便说一句,您的测试文件暴露了 PDFBox 确定基本字形以合并变音符号的错误:“स[1434]ु[1441]न[1418]”应呈现为“सुन”,即元音符号u“ु”与字母sa“स”组合,但PDFBox将其与后续字母na“न”组合为“सनु”。

原因是它决定了结合变音符号的字母是由它的来源决定的,这里确实在后一个字母na“न”的范围内,但是作为元音符号字形呈现在其原点之前(它被绘制在具有负x坐标的区域中),PDFBox确定了错误的关联: