Чтение IText в формате PDF, например pdftotext -layout?

Я ищу самый простой способ реализовать Java-решение, которое тихо, похожее на вывод

pdftotext -layout FILE 

на машинах linux. (И, конечно, это тоже должно быть дешево)

Я просто попробовал некоторые fragmentы кода IText, PDFBox и PDFTextStream. Наиболее точным решением является PDFTextStream, который использует VisualOutputTarget для получения отличного представления моего файла.

Таким образом, макет столбца распознается правильно, и я могу работать с ним. Но должно быть также решение для IText, или?

Каждый простой fragment, который я нашел, создает простые упорядоченные строки, которые являются беспорядком (беспорядок строки / столбца / строки). Есть ли какое-то решение, которое может быть проще и может не включать в себя собственную Страtagsю? Или есть страtagsя открытого источника, которую я могу использовать?

// Я следовал инструкциям mkl и написал собственный объект страtagsи следующим образом:

 package com.test.pdfextractiontest.itext; import ... public class MyLocationTextExtractionStrategy implements TextExtractionStrategy { /** set to true for debugging */ static boolean DUMP_STATE = false; /** a summary of all found text */ private final List locationalResult = new ArrayList(); public MyLocationTextExtractionStrategy() { } @Override public void beginTextBlock() { } @Override public void endTextBlock() { } private boolean startsWithSpace(final String str) { if (str.length() == 0) { return false; } return str.charAt(0) == ' '; } private boolean endsWithSpace(final String str) { if (str.length() == 0) { return false; } return str.charAt(str.length() - 1) == ' '; } private List filterTextChunks(final List textChunks, final TextChunkFilter filter) { if (filter == null) { return textChunks; } final List filtered = new ArrayList(); for (final TextChunk textChunk : textChunks) { if (filter.accept(textChunk)) { filtered.add(textChunk); } } return filtered; } protected boolean isChunkAtWordBoundary(final TextChunk chunk, final TextChunk previousChunk) { final float dist = chunk.distanceFromEndOf(previousChunk); if (dist  chunk.getCharSpaceWidth() / 2.0f) { return true; } return false; } public String getResultantText(final TextChunkFilter chunkFilter) { if (DUMP_STATE) { dumpState(); } final List filteredTextChunks = filterTextChunks(this.locationalResult, chunkFilter); Collections.sort(filteredTextChunks); final StringBuffer sb = new StringBuffer(); TextChunk lastChunk = null; for (final TextChunk chunk : filteredTextChunks) { if (lastChunk == null) { sb.append(chunk.text); } else { if (chunk.sameLine(lastChunk)) { if (isChunkAtWordBoundary(chunk, lastChunk) && !startsWithSpace(chunk.text) && !endsWithSpace(lastChunk.text)) { sb.append(' '); } final Float dist = chunk.distanceFromEndOf(lastChunk)/3; for(int i = 0; i<Math.round(dist); i++) { sb.append(' '); } sb.append(chunk.text); } else { sb.append('\n'); sb.append(chunk.text); } } lastChunk = chunk; } return sb.toString(); } 

eturn a String с полученным текстом. * / @Override public String getResultantText () {

  return getResultantText(null); } private void dumpState() { for (final TextChunk location : this.locationalResult) { location.printDiagnostics(); System.out.println(); } } @Override public void renderText(final TextRenderInfo renderInfo) { LineSegment segment = renderInfo.getBaseline(); if (renderInfo.getRise() != 0) { final Matrix riseOffsetTransform = new Matrix(0, -renderInfo.getRise()); segment = segment.transformBy(riseOffsetTransform); } final TextChunk location = new TextChunk(renderInfo.getText(), segment.getStartPoint(), segment.getEndPoint(), renderInfo.getSingleSpaceWidth(),renderInfo); this.locationalResult.add(location); } public static class TextChunk implements Comparable { /** the text of the chunk */ private final String text; /** the starting location of the chunk */ private final Vector startLocation; /** the ending location of the chunk */ private final Vector endLocation; /** unit vector in the orientation of the chunk */ private final Vector orientationVector; /** the orientation as a scalar for quick sorting */ private final int orientationMagnitude; private final TextRenderInfo info; private final int distPerpendicular; private final float distParallelStart; private final float distParallelEnd; /** the width of a single space character in the font of the chunk */ private final float charSpaceWidth; public TextChunk(final String string, final Vector startLocation, final Vector endLocation, final float charSpaceWidth,final TextRenderInfo ri) { this.text = string; this.startLocation = startLocation; this.endLocation = endLocation; this.charSpaceWidth = charSpaceWidth; this.info = ri; Vector oVector = endLocation.subtract(startLocation); if (oVector.length() == 0) { oVector = new Vector(1, 0, 0); } this.orientationVector = oVector.normalize(); this.orientationMagnitude = (int) (Math.atan2(this.orientationVector.get(Vector.I2), this.orientationVector.get(Vector.I1)) * 1000); final Vector origin = new Vector(0, 0, 1); this.distPerpendicular = (int) startLocation.subtract(origin).cross(this.orientationVector).get(Vector.I3); this.distParallelStart = this.orientationVector.dot(startLocation); this.distParallelEnd = this.orientationVector.dot(endLocation); } public Vector getStartLocation() { return this.startLocation; } public Vector getEndLocation() { return this.endLocation; } public String getText() { return this.text; } public float getCharSpaceWidth() { return this.charSpaceWidth; } private void printDiagnostics() { System.out.println("Text (@" + this.startLocation + " -> " + this.endLocation + "): " + this.text); System.out.println("orientationMagnitude: " + this.orientationMagnitude); System.out.println("distPerpendicular: " + this.distPerpendicular); System.out.println("distParallel: " + this.distParallelStart); } public boolean sameLine(final TextChunk as) { if (this.orientationMagnitude != as.orientationMagnitude) { return false; } if (this.distPerpendicular != as.distPerpendicular) { return false; } return true; } public float distanceFromEndOf(final TextChunk other) { final float distance = this.distParallelStart - other.distParallelEnd; return distance; } public float myDistanceFromEndOf(final TextChunk other) { final float distance = this.distParallelStart - other.distParallelEnd; return distance; } @Override public int compareTo(final TextChunk rhs) { if (this == rhs) { return 0; // not really needed, but just in case } int rslt; rslt = compareInts(this.orientationMagnitude, rhs.orientationMagnitude); if (rslt != 0) { return rslt; } rslt = compareInts(this.distPerpendicular, rhs.distPerpendicular); if (rslt != 0) { return rslt; } return Float.compare(this.distParallelStart, rhs.distParallelStart); } private static int compareInts(final int int1, final int int2) { return int1 == int2 ? 0 : int1 < int2 ? -1 : 1; } public TextRenderInfo getInfo() { return this.info; } } @Override public void renderImage(final ImageRenderInfo renderInfo) { // do nothing } public static interface TextChunkFilter { public boolean accept(TextChunk textChunk); } } 

Как вы можете видеть большинство, это то же самое, что и исходный class. Я просто добавил это:

  final Float dist = chunk.distanceFromEndOf(lastChunk)/3; for(int i = 0; i<Math.round(dist); i++) { sb.append(' '); } 

к методу getResultantText для расширения пробелов с пробелами. Но вот проблема:

расстояние кажется неточным или неточным. результат выглядит

это: это:

кто-нибудь есть идея, как рассчитать лучшее или значение расстояния? я думаю, потому что исходный тип шрифта ArialMT и мой редактор находится в курьерах, но для работы с этим листом рекомендуется, чтобы я мог разбить таблицу на нужное место, чтобы получить мои данные. это сложно из-за плавающего начала и конца значения usw.

: – /

Проблема с вашим входом в такие места, как это

  final Float dist = chunk.distanceFromEndOf(lastChunk)/3; for(int i = 0; i 

заключается в том, что он предполагает, что текущая позиция в StringBuffer точно соответствует концу lastChunk предполагая ширину ширины символа из 3 единиц пользовательского пространства. Этого не должно быть, обычно каждое добавление символов уничтожает такую ​​бывшую переписку. Например, эти две линии имеют разную ширину при использовании пропорционального шрифта:

ililili

MWMWMWM

в то время как в StringBuffer они занимают одну и ту же длину.

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

Кроме того, ваш код полностью игнорирует свободное пространство в начале строк.

Ваши результаты должны улучшиться, если вместо оригинального метода getResultantText(TextChunkFilter заменить:

 public String getResultantText(TextChunkFilter chunkFilter){ if (DUMP_STATE) dumpState(); List filteredTextChunks = filterTextChunks(locationalResult, chunkFilter); Collections.sort(filteredTextChunks); int startOfLinePosition = 0; StringBuffer sb = new StringBuffer(); TextChunk lastChunk = null; for (TextChunk chunk : filteredTextChunks) { if (lastChunk == null){ insertSpaces(sb, startOfLinePosition, chunk.distParallelStart, false); sb.append(chunk.text); } else { if (chunk.sameLine(lastChunk)) { if (isChunkAtWordBoundary(chunk, lastChunk)) { insertSpaces(sb, startOfLinePosition, chunk.distParallelStart, !startsWithSpace(chunk.text) && !endsWithSpace(lastChunk.text)); } sb.append(chunk.text); } else { sb.append('\n'); startOfLinePosition = sb.length(); insertSpaces(sb, startOfLinePosition, chunk.distParallelStart, false); sb.append(chunk.text); } } lastChunk = chunk; } return sb.toString(); } void insertSpaces(StringBuffer sb, int startOfLinePosition, float chunkStart, boolean spaceRequired) { int indexNow = sb.length() - startOfLinePosition; int indexToBe = (int)((chunkStart - pageLeft) / fixedCharWidth); int spacesToInsert = indexToBe - indexNow; if (spacesToInsert < 1 && spaceRequired) spacesToInsert = 1; for (; spacesToInsert > 0; spacesToInsert--) { sb.append(' '); } } public float pageLeft = 0; public float fixedCharWidth = 6; 

pageLeft - координата левой границы страницы. Страtagsя этого не знает и, следовательно, должна быть рассказана явно; во многих случаях, однако, 0 является правильным значением.

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

fixedCharWidth - предполагаемая ширина символа. В зависимости от написания в PDF-документе другое значение может быть более полезным. В вашем случае значение 3 кажется лучше, чем у меня 6.

В этом коде есть много возможностей для улучшения. Например

  • Он предполагает, что нет текстовых fragmentов, охватывающих несколько столбцов таблицы. Это предположение очень часто является правильным, но я видел странные PDF-файлы, в которых нормальный интервал между словами был реализован с использованием отдельных fragmentов текста при некотором смещении, но межколоночное расстояние было представлено одним пробелом в одном fragmentе (spanning конец одного столбца и начало следующего)! Ширину этого символа пробела манипулировали установкой слова-интервала состояния графики PDF.

  • Он игнорирует разное количество вертикального пространства.

Interesting Posts

Объединение двух фреймов данных с использованием нечеткого / приближенного соответствия строк в R

Как отключить даты до сегодняшнего дня в DatePickerDialog Android?

Как запустить .exe при загрузке и поддерживать его независимо от того, кто входит в систему Windows?

Совместное использование 2 мониторов между двумя компьютерами в разных конфигурациях

Получить отредактированный TreeNode от CellEditorListener

Лучшая практика для структуры рабочего каталога проекта Django

Включить подсказку углового ui для пользовательских событий

Android-L CardView Visual Touch Обратная связь

Как удалить ведущие нули из буквенно-цифрового текста?

Как программно заблокировать экран в Android?

Что означает «rc» в «.bashrc» и т. Д.?

Как правильно очистить объекты Interop Excel?

Установка Ubuntu на двойную загрузку с mSATA

Проверить петли сверху или снизу? (while vs. do while)

Как добавить ROW_NUMBER в запрос LINQ или Entity?

Давайте будем гением компьютера.