Использование PdfPig для чтения текста из заказанного PDF-файла, как описано в разделе Получить текст построчно из PDF с помощью C# ответ
static void GetWordsInReadingOrder(UglyToad.PdfPig.Content.Page page, StringBuilder builder)
{
var words = page.GetWords().ToList();
var wordsList = page.GetWords().GroupBy(x => x.BoundingBox.Bottom);
foreach (var word in wordsList)
{
bool esimene = true;
foreach (var item in word)
{
if (!esimene)
builder.Append(' ');
esimene = false;
builder.Append($"{item.Text}");
}
builder.AppendLine();
}
}
Разбивает одну строку PDF на несколько строк, если есть небольшая разница в x.BoundingBox.Bottom. Как добавить некоторый допуск, чтобы при небольшой разнице в позициях Y элементы отображались в одной строке? Вероятно, следует добавить округление
var wordsList = page.GetWords().GroupBy(x => x.BoundingBox.Bottom);
Я создал этот код, чтобы учитывать расстояние от начала строки и сортировать до и после присвоения. Может ли это улучшиться?
var sorted = (from r in page.GetWords()
orderby r.BoundingBox.Bottom, r.BoundingBox.Left
select r).ToList();
if (sorted.Count == 0)
return;
List<WordsWithLines> lines = new List<WordsWithLines>();
const double rowHeight = 10;
double lnLastTop = 0;
int lnLastRow = 1;
double csvReaVpos = sorted.First().BoundingBox.Bottom;
foreach (var r in sorted)
{
if (r.BoundingBox.Bottom >= (lnLastTop + 0.5 * rowHeight) || (r.BoundingBox.Bottom - csvReaVpos) > rowHeight)
{
++lnLastRow;
csvReaVpos = r.BoundingBox.Bottom;
}
lnLastTop = r.BoundingBox.Bottom;
lines.Add(new WordsWithLines()
{
RowNo = lnLastRow,
WordElement = r
});
}
var wordsList = from r in lines
orderby r.RowNo descending, r.WordElement.BoundingBox.Left
select r;
int rowNo = wordsList.First().RowNo;
bool first = true;
foreach (var word in wordsList)
{
if (rowNo != word.RowNo)
{
builder.AppendLine();
first = true;
rowNo = word.RowNo;
}
if (!first)
builder.Append(' ');
builder.Append($"{Core.CharTran(word.WordElement.Text, " ", "_")}");
first = false;
}
Если я правильно понимаю, вы хотели бы иметь какой-то способ помещать объекты в «ведра», если какое-то десятичное/двойное значение «достаточно близко». Для этого я могу предложить довольно простую реализацию:
Просмотрите все рассматриваемые десятичные значения, сначала создайте первое ведро с первым элементом и для каждого следующего элемента проверьте, существует ли соответствующее ведро (на основе максимальной разницы между ними), если такого ведра не существует, создайте новое.
Ниже приведена реализация с примером метода расширения:
public static class Bucketing
{
public static IEnumerable<IGrouping<double, T>> BucketGroupBy<T>(
this IEnumerable<T> collection,
Func<T, double> selector,
double bucketRange)
{
var buckets = GetBuckets(
collection.Select(selector).ToArray(),
(x, y) => Math.Abs(x - y) < bucketRange);
var newSelector = (T x) =>
{
var decimalValue = selector(x);
var bucket = buckets.First(i => i.Value.Contains(decimalValue));
return bucket.Key;
};
return collection.GroupBy(newSelector);
}
public static Dictionary<double, double[]> GetBuckets(
double[] numbers,
Func<double, double, bool> bucketPredicate)
{
var buckets = new Dictionary<double, List<double>>();
foreach (var number in numbers)
{
var existingBucket = buckets.FirstOrDefault(x => bucketPredicate(x.Key, number));
if (existingBucket.Value is not null)
{
existingBucket.Value.Add(number);
continue;
}
buckets.Add(number, new List<double> { number });
}
return buckets.ToDictionary(x => x.Key, x => x.Value.ToArray());
}
}
Я добавил свой код в решение. Будет ли ваш класс Bucketing создавать одинаковые строки? Также после присвоения номеров строк слова должны быть переупорядочены, чтобы расположить их в правильном порядке. Как улучшить этот код?
Пожалуйста, не используйте теги PDFBox, если речь не идет о PDFBox.