Тика и PDFBox неправильно добавляют новые строки в PDF
Я обнаружил проблему при разборе PDF-документов, отправленных на веб-службу для NLP. Мы используем Tika 1.19.1 для извлечения простого текста.
Некоторые люди пишут свои документы неправильно или неправильно акцентированы (на самом деле, большинство людей, которые используют наш сервис, забывают добавить точку в конце абзаца), поэтому мы считаем новую строку концом предложения, не имеет значения, у него есть точка в конце или нет. Когда они загружают файлы в формате docx, все генерируется в ускоренном порядке: все идет в одну строку, ЕСЛИ пользователь не вводит символ новой строки вручную, его не заботит макет страницы. Это ожидаемое поведение.
Когда люди загружают файлы PDF (до сих пор каждый файл был сгенерирован MS Word), Тика генерирует дополнительные новые строки в конце нарисованной линии PDF.
Ожидаемый результат:
Это тестовый документ.
Первая строка: все содержимое этого файла должно отображаться в трех строках текста, как было написано автором. Lorem Ipsum Dolor Sit Amet, Concetetur Adipiscing Elit. Маурис в посуэре дуй. Quisque Sed Enim Commodo, Porttitor Ante Eget, Semper NISL. Сед в рутрум магна. Потенциал Suspendisse. Nulla accumsan neque eu nisi condimentum accumsan. Etiam condimentum ullamcorper nulla. Suspendisse Vel Turpis Nunc. Suspendisse eget ex quam. Lorem Ipsum Dolor Sit Amet, Concetetur Adipiscing Elit. Приостановить последовательное резюме ex в facilisis.
Вторая строка: это новая строка после первой. Неважно, имеет ли он пустую черту над ним или сейчас (хотя это не было когда написано), но важно только то, что строка заканчивается здесь, а не раньше.
Третья строка: если это не так, это означает, что Тика угадывает переводы строк, чтобы сделать.txt читабельным, однако, это разрушит обработку естественного языка, которая опирается на переводы строк для определения конца предложений, которые не были должным образом отмечены.
Тем не менее, фактический вывод выглядит так:
Это тестовый документ.
Первая строка: все содержимое этого файла должно отображаться в трех строках текста, как это было написано
Автор. Lorem Ipsum Dolor Sit Amet, Concetetur Adipiscing Elit. Маурис в посуэре дуй.
Quisque Sed Enim Commodo, Porttitor Ante Eget, Semper NISL. Сед в рутрум магна.
Потенциал Suspendisse. Nulla accumsan neque eu nisi condimentum accumsan. Etiam
condimentum ullamcorper nulla. Suspendisse Vel Turpis Nunc. Suspendisse eget ex quam.
Lorem Ipsum Dolor Sit Amet, Concetetur Adipiscing Elit. Suspendisse Conquat Vitae ex в
facilisis.
Вторая строка: это новая строка после первой. Неважно, есть ли над ним пустая строка или
сейчас (хотя это было не тогда, когда написано), но главное, что строка заканчивается здесь,
и не раньше.
Третья строка: если это не так, это означает, что Тика угадывает переводы строки, чтобы сделать.txt читабельным, однако, это
испортит обработку естественного языка, которая опирается на новые строки для определения конца предложений
которые не были должным образом акцентированы.
Если вы копируете в блокнот из сгенерированного PDF-файла, он НЕ добавляет "плохих строк": копирование при вставке вручную создает ожидаемый результат, поэтому я предполагаю, что PDF-файл был создан правильно.
Примеры: так как очень просто создать рабочий пример, я опишу процесс вместо его загрузки на сервер, где он в конечном итоге умрет.
Чтобы создать пример для воспроизведения проблемы, сгенерируйте 300-500 символов lorem-ipsum в одной строке. Скопируйте эту строку в MS Word и сохраните файл. Тика будет извлекать его одной строкой. Используя MS Word, сохраните документ в формате PDF. Теперь он будет использовать столько строк, сколько использовался ваш макет страницы.
Я чувствую, что синтаксический анализатор "слишком умен" и пытается разбить строки на печатные строки, что очень удобно для чтения человеком, но так плохо для НЛП:S
Упрощенный код Тика:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.pdf.PDFParser;
import org.apache.tika.sax.BodyContentHandler;
import org.xml.sax.SAXException;
public class PDFParse {
public static void main(final String[] args) throws IOException,TikaException, SAXException {
BodyContentHandler handler = new BodyContentHandler();
Metadata metadata = new Metadata();
FileInputStream inputstream = new FileInputStream(new File("D:\\Trabajo\\Document Parser\\cv_test.pdf"));
ParseContext pcontext = new ParseContext();
//parsing the document using PDF parser
PDFParser pdfparser = new PDFParser();
pdfparser.parse(inputstream, handler, metadata,pcontext);
//getting the content of the document
System.out.println("Contents of the PDF :" + handler.toString());
//getting metadata of the document
System.out.println("Metadata of the PDF:");
String[] metadataNames = metadata.names();
for(String name : metadataNames) {
System.out.println(name+ " : " + metadata.get(name));
}
}
}
PD: Использование следующего упрощенного кода с PDFBox приводит к тем же ошибкам:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.pdfbox.cos.COSDocument;
import org.apache.pdfbox.pdfparser.PDFParser;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.io.RandomAccessRead;
import org.apache.pdfbox.io.RandomAccessFile;
public class PDFReader{
public static void main(String args[]) {
PDFTextStripper pdfStripper = null;
PDDocument pdDoc = null;
COSDocument cosDoc = null;
File file = new File("D:\\Trabajo\\Document Parser\\lorem.pdf");
try {
// PDFBox 2.0.8 require org.apache.pdfbox.io.RandomAccessRead
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
PDFParser parser = new PDFParser(randomAccessFile);
//PDFParser parser = new PDFParser(new FileInputStream(file));
parser.parse();
cosDoc = parser.getDocument();
pdfStripper = new PDFTextStripper();
pdDoc = new PDDocument(cosDoc);
pdfStripper.setStartPage(1);
pdfStripper.setEndPage(5);
String parsedText = pdfStripper.getText(pdDoc);
System.out.println(parsedText);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}