/
afanasevn
/
PdfEncoder
Обзор
Документация
Войти
/
afanasevn
/
PdfEncoder
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/PdfEncoder.Infrastructure/Pdf/PdfClassifier.cs
116 строк
3 KB
IBS\NAfanasev
Parse results functional update
07 июл 2026, 00:06
07 июл 2026, 00:06
8e97443
Код
Авторство
О чём код?
using Microsoft.Extensions.Options; using PdfEncoder.Application.Abstractions; using PdfEncoder.Application.Options; using PdfEncoder.Domain.Enums; using UglyToad.PdfPig; using UglyToad.PdfPig.Content; using UglyToad.PdfPig.DocumentLayoutAnalysis.WordExtractor; namespace PdfEncoder.Infrastructure.Pdf; /// <summary> /// Классификация PDF kind по эвристикам PdfPig. /// </summary> public sealed class PdfClassifier(IOptions<PipelineOptions> pipelineOptions) : IPdfClassifier { private const int MinLettersForTextPage = 5; private const double TextBasedPageRatio = 0.8; /// <inheritdoc /> public Task<PdfClassificationResult> ClassifyAsync( ReadOnlyMemory<byte> content, CancellationToken ct) { ct.ThrowIfCancellationRequested(); using var document = PdfDocument.Open(content); var pageClassifications = new List<PageClassification>(document.NumberOfPages); var hasAcroForm = TryExtractFormFieldCount(document) > 0; for (var pageNumber = 1; pageNumber <= document.NumberOfPages; pageNumber++) { ct.ThrowIfCancellationRequested(); var page = document.GetPage(pageNumber); var letterCount = CountLetters(page); var imageMetrics = PdfEmbeddedImageCoverageCalculator.Analyze(page); pageClassifications.Add(new PageClassification { PageNumber = pageNumber, HasTextLayer = letterCount >= MinLettersForTextPage, LetterCount = letterCount, HasEmbeddedImages = imageMetrics.HasEmbeddedImages, ImageCoverageRatio = imageMetrics.CoverageRatio }); } var kind = ResolveKind(pageClassifications, hasAcroForm, pipelineOptions.Value); return Task.FromResult(new PdfClassificationResult { Kind = kind, Pages = pageClassifications, HasAcroForm = hasAcroForm }); } private static PdfDocumentKind ResolveKind( IReadOnlyList<PageClassification> pages, bool hasAcroForm, PipelineOptions pipelineOptions) { if (pages.Count == 0) { return PdfDocumentKind.Unknown; } if (hasAcroForm) { return PdfDocumentKind.Form; } var textPages = pages.Count(page => page.HasTextLayer); if (textPages == 0) { return PdfDocumentKind.Scanned; } if (pages.Any(page => PdfEmbeddedImageCoverageCalculator.RequiresFullPageOcr(page, pipelineOptions))) { return PdfDocumentKind.Mixed; } var ratio = (double)textPages / pages.Count; if (ratio >= TextBasedPageRatio) { return PdfDocumentKind.TextBased; } return PdfDocumentKind.Mixed; } private static int CountLetters(Page page) { var letters = 0; foreach (var word in page.GetWords(NearestNeighbourWordExtractor.Instance)) { foreach (var ch in word.Text) { if (char.IsLetter(ch)) { letters++; } } } return letters; } private static int TryExtractFormFieldCount(PdfDocument document) { if (!document.TryGetForm(out var form) || form is null) { return 0; } return form.Fields.Count(); } }