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](https://i.stack.imgur.com/83bvj.png)
равен
<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.)