34 lines
2.6 KiB
C#
34 lines
2.6 KiB
C#
using System.Text.RegularExpressions;
|
||
using StandardsWorkbench.Core;
|
||
using UglyToad.PdfPig;
|
||
|
||
namespace StandardsWorkbench.Pdf;
|
||
|
||
public sealed class PdfStandardService : IStandardPdfService
|
||
{
|
||
public Task<(StandardMetadata Metadata, IReadOnlyList<PdfPageText> Pages, IReadOnlyList<StandardClause> Clauses)> ImportAsync(string pdfPath, string code, string title, CancellationToken cancellationToken)
|
||
{
|
||
if (!File.Exists(pdfPath)) throw new FileNotFoundException("PDF ГОСТа не найден.", pdfPath);
|
||
return Task.Run(() =>
|
||
{
|
||
using var pdf = PdfDocument.Open(pdfPath); var pages = new List<PdfPageText>();
|
||
foreach (var page in pdf.GetPages()) { cancellationToken.ThrowIfCancellationRequested(); var text = page.Text ?? ""; pages.Add(new PdfPageText(page.Number, text, text.Trim().Length > 0, text.Trim().Length > 0 ? "Extracted" : "OCR_REQUIRED", text.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries))); }
|
||
var standardId = Regex.Replace(code.ToUpperInvariant(), "[^A-ZА-Я0-9]+", "-").Trim('-'); var clauses = ExtractClauses(standardId, pages); var meta = new StandardMetadata { StandardId = standardId, Code = code, Title = title, SourceFileSha256 = ProfileStore.Sha256(pdfPath), PageCount = pages.Count, TextLayerDetected = pages.Any(p => p.TextLayerDetected), ImportedAt = DateTime.UtcNow };
|
||
return (meta, (IReadOnlyList<PdfPageText>)pages, (IReadOnlyList<StandardClause>)clauses);
|
||
}, cancellationToken);
|
||
}
|
||
|
||
private static List<StandardClause> ExtractClauses(string id, IReadOnlyList<PdfPageText> pages)
|
||
{
|
||
var result = new List<StandardClause>(); string? currentId = null; var currentText = new List<string>(); var start = 1;
|
||
void Flush(int end) { if (currentId is not null) result.Add(new StandardClause(id, currentId, "", string.Join(Environment.NewLine, currentText), start, end, 0.85)); }
|
||
foreach (var page in pages) foreach (var line in page.Text.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) { var match = Regex.Match(line, "^(\\d+(?:\\.\\d+){0,5})[.)]?\\s+(.+)$"); if (match.Success) { Flush(page.PageNumber - 1); currentId = match.Groups[1].Value; currentText = [line]; start = page.PageNumber; } else if (currentId is not null) currentText.Add(line); }
|
||
Flush(pages.Count); return result;
|
||
}
|
||
}
|
||
|
||
public sealed class NoOpOcrService : IPdfOcrService
|
||
{
|
||
public Task<PdfPageText> RecognizePageAsync(string pdfPath, int pageNumber, CancellationToken cancellationToken) => Task.FromResult(new PdfPageText(pageNumber, "", false, "OCR_REQUIRED", []));
|
||
}
|