Как заменить пробел словом при извлечении данных из PDF с помощью PDFBox - PullRequest
0 голосов
/ 02 марта 2019

Я хочу заменить любой пустой столбец словом;например, слово BLK при извлечении данных PDF.

приведенные ниже таблицы являются примером ожидаемой таблицы и фактического результата.

Исходная таблица

+--------------------------------------+
|# |NAME        |TEL        |GENDER    |
|---------------------------|----------|
|1 |JOHN        |096587498  |M         |
|2 |VILLA       |           |F         |
+--------------------------------------+

Ожидаемый результат

# NAME TEL GENDER
1 JOHN 096587498 M
2 VILLA BLK F

Фактический результат

# NAME TEL GENDER
1 JOHN 096587498 M
2 VILLA F

Фактический результат из класса PDFTextStripper .

захват pdf enter image description here

1 Ответ

0 голосов
/ 04 марта 2019

PDFTextStripper не видит графические строки в PDF, он просто видит текстовые символы.Таким образом, в вашей строке # 2 он видит «2», «Villa» и «F» с промежутками между ними.Таким образом, только с этим классом вы не получите то, что хотите.

В общем, у вас есть следующие опции, использующие PDFBox:

  • Сначала вы можете попытаться распознатьобласти ячеек таблицы в вашем PDF-файле путем анализа инструкций векторной графики на странице, а затем извлечения текстовой ячейки за ячейкой.

    Этот ответ обеспечивает подтверждение концепции.Осторожно: этот ответ сфокусирован на примере документа, предоставленного ФП по этому вопросу.В частности, ожидается, что линии будут нарисованы в виде тонких заполненных прямоугольников;для общего решения код, собирающий строки таблицы, должен быть расширен, чтобы также распознавать линии, нарисованные иначе.

    Этот подход, очевидно, требует, чтобы строки и столбцы таблицы были разделены линиями (или расширением альтернативно цветами фона иличто-то похожее);это не всегда так.

    В случае вашего примера документа код работает "из коробки":

    [A1] # 
    
    [A2] Name 
    
    [A3] Tel 
    
    [A4] Gender 
    
    [B1] 1 
    
    [B2] John 
    
    [B3] 096875959 
    
    [B4] M 
    
    [C1] 2 
    
    [C2] Villa 
    
    [C3]  
    
    [C4] F 
    

    (вывод ExtractBoxedText testtestExtractBoxedTextsTestWPhromma)

  • Вы можете извлечь текст, пытаясь отразить макет PDF.Если вам известна общая схема рассматриваемой таблицы (столбец n идет отсюда туда ...), вы можете получить содержимое ячейки таблицы.

    Этот ответ предоставляет подтверждение.о концепции для извлечения текста с учетом макета.Осторожно, код основан на PDFBox 1.8.x, могут потребоваться некоторые изменения.

    Этот подход требует знания макета столбца таблицы;это не всегда дается.

    В случае вашего примера документа код работает из коробки:

                     #              Name                                                            Tel                                    Gender
                     1              John                                                            096875959                              M
                     2              Villa                                                                                                  F
    

    (вывод ExtractTextWithLayout test testExtractTestWPhromma)

  • Для PDF-файлов с тегами вы можете попытаться извлечь текст, включающий теги, отражающие структуру таблицы (если они правильно помечены).

    Как вашпример документа помечен, я покажу быстрое и грязное подтверждение концепции ниже.

    Этот подход требует, чтобы PDF был правильно помечен;это не всегда так.

Извлечение содержимого с помощью тегов

Если ваш PDF-файл правильно помечен, вы можете извлечь содержимое, включая теги разметки, например:

PDDocument document = PDDocument.load(SOURCE);

Map<PDPage, Map<Integer, PDMarkedContent>> markedContents = new HashMap<>();

for (PDPage page : document.getPages()) {
    PDFMarkedContentExtractor extractor = new PDFMarkedContentExtractor();
    extractor.processPage(page);

    Map<Integer, PDMarkedContent> theseMarkedContents = new HashMap<>();
    markedContents.put(page, theseMarkedContents);
    for (PDMarkedContent markedContent : extractor.getMarkedContents()) {
        theseMarkedContents.put(markedContent.getMCID(), markedContent);
    }
}

PDStructureNode root = document.getDocumentCatalog().getStructureTreeRoot();
showStructure(root, markedContents);

( ExtractMarkedContent test testExtractTestWPhromma)

с использованием этих двух вспомогательных методов

void showStructure(PDStructureNode node, Map<PDPage, Map<Integer, PDMarkedContent>> markedContents) {
    String structType = null;
    PDPage page = null;
    if (node instanceof PDStructureElement) {
        PDStructureElement element = (PDStructureElement) node;
        structType = element.getStructureType();
        page = element.getPage();
    }
    Map<Integer, PDMarkedContent> theseMarkedContents = markedContents.get(page);
    System.out.printf("<%s>\n", structType);
    for (Object object : node.getKids()) {
        if (object instanceof COSArray) {
            for (COSBase base : (COSArray) object) {
                if (base instanceof COSDictionary) {
                    showStructure(PDStructureNode.create((COSDictionary) base), markedContents);
                } else if (base instanceof COSNumber) {
                    showContent(((COSNumber)base).intValue(), theseMarkedContents);
                } else {
                    System.out.printf("?%s\n", base);
                }
            }
        } else if (object instanceof PDStructureNode) {
            showStructure((PDStructureNode) object, markedContents);
        } else if (object instanceof Integer) {
            showContent((Integer)object, theseMarkedContents);
        } else {
            System.out.printf("?%s\n", object);
        }

    }
    System.out.printf("</%s>\n", structType);
}

void showContent(int mcid, Map<Integer, PDMarkedContent> theseMarkedContents) {
    PDMarkedContent markedContent = theseMarkedContents != null ? theseMarkedContents.get(mcid) : null;
    List<Object> contents = markedContent != null ? markedContent.getContents() : Collections.emptyList();
    StringBuilder textContent =  new StringBuilder();
    for (Object object : contents) {
        if (object instanceof TextPosition) {
            textContent.append(((TextPosition)object).getUnicode());
        } else {
            textContent.append("?" + object);
        }
    }
    System.out.printf("%s\n", textContent);
}

( ExtractMarkedContent вспомогательные методы)

Вывод для вашего примера PDF

enter link description here

равен

<null>
<Document>
<Table>
<THead>
<TR>
<TH>
<P>
# 
</P>
</TH>
<TH>
<P>
Name 
</P>
</TH>
<TH>
<P>
Tel 
</P>
</TH>
<TH>
<P>
Gender 
</P>
</TH>
</TR>
</THead>
<TBody>
<TR>
<TH>
<P>
1 
</P>
</TH>
<TD>
<P>
John 
</P>
</TD>
<TD>
<P>
096875959 
</P>
</TD>
<TD>
<P>
M 
</P>
</TD>
</TR>
<TR>
<TH>
<P>
2 
</P>
</TH>
<TD>
<P>
Villa 
</P>
</TD>
<TD>
<P>

</P>
</TD>
<TD>
<P>
F 
</P>
</TD>
</TR>
</TBody>
</Table>
<P>

</P>
</Document>
</null>

Вы узнаете пустую ячейку:

<TD>
<P>

</P>
</TD>

Это доказательство концепции извлекает стандартный вывод.Вы, очевидно, можете альтернативно собирать данные в строителе строк или потоке, или вы можете сразу же заполнить данные <Table> в пользовательских структурах, ведь они уже разделены в ячейках.

Остерегайтесь Это только подтверждение концепции.Там, где код выводит данные, подобные этому System.out.printf("?%s\n", ...);, может потребоваться определенная обработка.Также другие пограничные условия, вероятно, не учитываются должным образом.(На самом деле я реализовал его только для правильного извлечения содержимого вашего примера PDF.)

...