У меня есть pdf. PDF содержит таблицу. Таблица содержит много ячеек (> 100). Я знаю точное положение (x, y) и размерность (w, h) каждой ячейки таблицы.
Мне нужно извлечь текст из ячеек, используя itextsharp. Используя PdfReaderContentParser + FilteredTextRenderListener (используя такой код http://itextpdf.com/examples/iia.php?id=279), я могу извлечь текст, но мне нужно запустить всю процедуру для каждой ячейки. В моем PDF-документе много ячеек, и программе требуется слишком много времени для запуска. Есть ли способ извлечь текст из списка «прямоугольник»? Мне нужно знать текст каждого прямоугольника. Я ищу что-то вроде PDFTextStripperByArea от PdfBox (вы можете определить столько областей, сколько вам нужно, и получить текст, используя .getTextForRegion («region-name»)).Извлечение текста из ячеек таблицы
ответ
Этот параметр не включается сразу в дистрибутив iTextSharp, но его легко реализовать. В следующем я использую имена класса, интерфейса и метода iText (Java), потому что я больше дома с Java. Они должны быть легко переведены в имена iTextSharp (C#).
Если вы используете LocationTextExtractionStrategy
, вы можете использовать его механизм posteriori TextChunkFilter
вместо механизма априорного FilteredRenderListener
, используемого в образце, к которому вы подключались. Этот механизм был введен в версии 5.3.3.
Для этого сначала проанализируйте содержимое всей страницы, используя LocationTextExtractionStrategy
без применения фильтра FilteredRenderListener
. Это заставляет объект стратегии собирать объекты TextChunk
для всех текстовых объектов PDF на странице, содержащей соответствующий сегмент базовой линии.
Тогда вы называете getResultantText
перегрузку этой стратегии с TextChunkFilter
аргумента (вместо обычной перегрузки без аргументов):
public String getResultantText(TextChunkFilter chunkFilter)
Вы называете это с другой TextChunkFilter
например, для каждой ячейки таблицы. Вы должны реализовать этот интерфейс фильтра, который не является слишком сложным, поскольку она определяет только один метод:
public static interface TextChunkFilter
{
/**
* @param textChunk the chunk to check
* @return true if the chunk should be allowed
*/
public boolean accept(TextChunk textChunk);
}
Так принять метод фильтра для данной ячейки необходимо проверить, является ли текст фрагмента в вопросе внутри клетки.
(вместо отдельных экземпляров для каждой ячейки вы можете, конечно, также создать один экземпляр, параметры, т.е. координаты ячейки, могут быть изменены между getResultantText
вызовов.)
PS: Как уже упоминалось в ОП, это TextChunkFilter
еще не портирован на iTextSharp. Это не должно быть трудно сделать, однако, только один небольшой интерфейс и один способ добавить к стратегии.
PPS: В комментарии sschuberth спросил
ли вы тогда еще называют
PdfTextExtractor.getTextFromPage()
при использованииgetResultantText()
, или же это как-то заменить этот вызов? Если да, то как вам тогда указать страницу для извлечения?
На самом деле PdfTextExtractor.getTextFromPage()
внутренне уже не использует без аргументов getResultantText()
перегрузки:
public static String getTextFromPage(PdfReader reader, int pageNumber, TextExtractionStrategy strategy, Map<String, ContentOperator> additionalContentOperators) throws IOException
{
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
return parser.processContent(pageNumber, strategy, additionalContentOperators).getResultantText();
}
Чтобы сделать использование TextChunkFilter
вы могли бы просто построить подобный удобный метод, например,
public static String getTextFromPage(PdfReader reader, int pageNumber, LocationTextExtractionStrategy strategy, Map<String, ContentOperator> additionalContentOperators, TextChunkFilter chunkFilter) throws IOException
{
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
return parser.processContent(pageNumber, strategy, additionalContentOperators).getResultantText(chunkFilter);
}
В контексте под рукой, хотя, в котором мы хотим разобрать содержимое страницы только один раз и применить несколько фильтров, один для каждой ячейки, мы можем обобщить это:
public static List<String> getTextFromPage(PdfReader reader, int pageNumber, LocationTextExtractionStrategy strategy, Map<String, ContentOperator> additionalContentOperators, Iterable<TextChunkFilter> chunkFilters) throws IOException
{
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
parser.processContent(pageNumber, strategy, additionalContentOperators)
List<String> result = new ArrayList<>();
for (TextChunkFilter chunkFilter : chunkFilters)
{
result.add(strategy).getResultantText(chunkFilter);
}
return result;
}
(Вы можете сделать этот взгляд более интересным, используя потоки коллекции Java 8 вместо старого цикла for
.)
Вот мой вопрос о том, как извлечь текст из табличной структуры в PDF, используя itextsharp. Он возвращает коллекцию строк, и каждая строка содержит набор интерпретируемых столбцов. Это может работать для вас, исходя из предпосылки, что между одним столбцом и рядом существует разрыв, который больше средней ширины одного символа. Я также добавил возможность проверки для обернутого текста в виртуальном столбце. Ваш пробег может отличаться.
using (PdfReader pdfReader = new PdfReader(stream))
{
for (int page = 1; page <= pdfReader.NumberOfPages; page++)
{
TableExtractionStrategy tableExtractionStrategy = new TableExtractionStrategy();
string pageText = PdfTextExtractor.GetTextFromPage(pdfReader, page, tableExtractionStrategy);
var table = tableExtractionStrategy.GetTable();
}
}
public class TableExtractionStrategy : LocationTextExtractionStrategy
{
public float NextCharacterThreshold { get; set; } = 1;
public int NextLineLookAheadDepth { get; set; } = 500;
public bool AccomodateWordWrapping { get; set; } = true;
private List<TableTextChunk> Chunks { get; set; } = new List<TableTextChunk>();
public override void RenderText(TextRenderInfo renderInfo)
{
base.RenderText(renderInfo);
string text = renderInfo.GetText();
Vector bottomLeft = renderInfo.GetDescentLine().GetStartPoint();
Vector topRight = renderInfo.GetAscentLine().GetEndPoint();
Rectangle rectangle = new Rectangle(bottomLeft[Vector.I1], bottomLeft[Vector.I2], topRight[Vector.I1], topRight[Vector.I2]);
Chunks.Add(new TableTextChunk(rectangle, text));
}
public List<List<string>> GetTable()
{
List<List<string>> lines = new List<List<string>>();
List<string> currentLine = new List<string>();
float? previousBottom = null;
float? previousRight = null;
StringBuilder currentString = new StringBuilder();
// iterate through all chunks and evaluate
for (int i = 0; i < Chunks.Count; i++)
{
TableTextChunk chunk = Chunks[i];
// determine if we are processing the same row based on defined space between subsequent chunks
if (previousBottom.HasValue && previousBottom == chunk.Rectangle.Bottom)
{
if (chunk.Rectangle.Left - previousRight > 1)
{
currentLine.Add(currentString.ToString());
currentString.Clear();
}
currentString.Append(chunk.Text);
previousRight = chunk.Rectangle.Right;
}
else
{
// if we are processing a new line let's check to see if this could be word wrapping behavior
bool isNewLine = true;
if (AccomodateWordWrapping)
{
int readAheadDepth = Math.Min(i + NextLineLookAheadDepth, Chunks.Count);
if (previousBottom.HasValue)
for (int j = i; j < readAheadDepth; j++)
{
if (previousBottom == Chunks[j].Rectangle.Bottom)
{
isNewLine = false;
break;
}
}
}
// if the text was not word wrapped let's treat this as a new table row
if (isNewLine)
{
if (currentString.Length > 0)
currentLine.Add(currentString.ToString());
currentString.Clear();
previousBottom = chunk.Rectangle.Bottom;
previousRight = chunk.Rectangle.Right;
currentString.Append(chunk.Text);
if (currentLine.Count > 0)
lines.Add(currentLine);
currentLine = new List<string>();
}
else
{
if (chunk.Rectangle.Left - previousRight > 1)
{
currentLine.Add(currentString.ToString());
currentString.Clear();
}
currentString.Append(chunk.Text);
previousRight = chunk.Rectangle.Right;
}
}
}
return lines;
}
private struct TableTextChunk
{
public Rectangle Rectangle;
public string Text;
public TableTextChunk(Rectangle rect, string text)
{
Rectangle = rect;
Text = text;
}
public override string ToString()
{
return Text + " (" + Rectangle.Left + ", " + Rectangle.Bottom + ")";
}
}
}
Эта стратегия делает число (некоторые из которых уже упоминал Шон), поэтому он не может использоваться для всех типов PDF-файлов с таблицами. Но для PDF-файлов, для которых предположения верны, содержимое таблицы извлекается без необходимости знать coor dinates. – mkl
Просто любопытно - есть ли ссылка для более универсального разбора с использованием iText? Я искал SE и не мог найти его - очень интересно узнать, есть ли что-то лучше с источником выборки. – Shaun
Ну, в связи с извлечением текста iText предлагает вам структуру, в которой вы можете разработать стратегии извлечения по вашему выбору сложности. Но многие пользователи предполагают, что две тестовые стратегии, связанные с iText, есть все. Вы найдете некоторые специализированные стратегии извлечения и POC для более продвинутых материалов, например. здесь в переполнении стека ответы, но не большое бесплатное решение, такое как tabula. Там могут быть некоторые закрытые исходные текстовые экстракторы, основанные на iText где-то там. – mkl
- 1. Извлечение текста из HTML таблицы
- 2. Извлечение таблицы структурированного текста из PDF-файла
- 3. Извлечение данных из массива ячеек
- 4. Извлечение текста из изображения
- 5. Извлечение текста из изображений
- 6. Извлечение текста из ячейки
- 7. Извлечение текста из html?
- 8. Дата Извлечение из текста
- 9. Извлечение текста из файла
- 10. Извлечение текста из строки
- 11. Извлечение даты из текста
- 12. Извлечение текста из HTML
- 13. Извлечение текста из вывода
- 14. Извлечение данных из текста
- 15. Извлечение данных из текста
- 16. имена Извлечение из текста
- 17. Извлечение строки из текста
- 18. Извлечение текста из флажков
- 19. Извлечение текста из PDF
- 20. Извлечение текста из WPF
- 21. извлечение текста из webview
- 22. Извлечение текста из файла PDF
- 23. JQuery отображает конкатенированный список текста из видимых ячеек HTML-таблицы
- 24. Извлечение ячеек в подставку.
- 25. Извлечение данных из таблицы
- 26. Извлечение таблицы из PDF
- 27. Извлечение таблицы из schema.rb
- 28. Извлечение объектов из таблицы
- 29. Извлечение строк из таблицы
- 30. Извлечение текста из таблицы MS Word без пуль [Powershell 4,0]
C# версия LocationTextExtractionStrategy не получаетResultantText (TextChunkFilter chunkFilter). :(Но ваша идея интересная. Я буду искать немного другой подход. –
К сожалению, вы правы. Интересно, поскольку эта функция была общедоступной в версии 5.3.4 (не 5.3.3, как утверждается в комментариях JavaDoc) iText/Java, которому больше года. Но я думаю, что очень просто перевести на C#, только один интерфейс и один метод ... – mkl
Вы все еще вызываете 'PdfTextExtractor.getTextFromPage()' при использовании 'getResultantText()', или он каким-то образом заменяет этот вызов? Если да, то как вам тогда указать страницу для извлечения на? – sschuberth