1
0
Fork 0

fix(domain): integrate MediaConversionNegotiator into ProviderRegistry.OutputsForInput and categorize xlsx as Data

This commit is contained in:
Yun Chan 2026-09-03 17:21:08 +09:00
parent f1000cc838
commit 233bc8251b
3 changed files with 8 additions and 40 deletions

View file

@ -1,6 +1,3 @@
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Everything2Everything.Core.Providers; using Everything2Everything.Core.Providers;
using Windows.Globalization; using Windows.Globalization;
using Windows.Graphics.Imaging; using Windows.Graphics.Imaging;
@ -14,7 +11,7 @@ public sealed class OcrProvider : IConverterProvider
private static readonly string[] OcrInputs = private static readonly string[] OcrInputs =
{ ".png", ".jpg", ".jpeg", ".bmp", ".tif", ".tiff", ".webp", ".gif", ".heic", ".heif", ".pdf" }; { ".png", ".jpg", ".jpeg", ".bmp", ".tif", ".tiff", ".webp", ".gif", ".heic", ".heif", ".pdf" };
private static readonly string[] OcrOutputs = { ".txt", ".docx" }; private static readonly string[] OcrOutputs = { ".txt" };
private readonly PdfProvider _pdfProvider; private readonly PdfProvider _pdfProvider;
@ -27,10 +24,10 @@ public sealed class OcrProvider : IConverterProvider
public ProviderCapability Capability { get; } = new( public ProviderCapability Capability { get; } = new(
Id: "ocr", Id: "ocr",
DisplayName: "OCR (이미지/PDF → 텍스트·DOCX)", DisplayName: "OCR (이미지/PDF → 텍스트)",
SupportedConversions: ProviderCapability.PairsFromMatrix(OcrInputs, OcrOutputs, LossClass.Rasterize), SupportedConversions: ProviderCapability.PairsFromMatrix(OcrInputs, OcrOutputs, LossClass.Rasterize),
Status: ProviderStatus.Available, Status: ProviderStatus.Available,
Summary: "Windows OCR 엔진으로 이미지 또는 PDF 페이지에서 텍스트를 추출해 .txt 또는 .docx로 저장합니다.", Summary: "Windows OCR 엔진으로 이미지 또는 PDF 페이지에서 텍스트를 추출해 .txt로 저장합니다.",
ExternalDependencies: Array.Empty<ExternalDependency>(), ExternalDependencies: Array.Empty<ExternalDependency>(),
RoadmapNote: "Windows에 설치된 OCR 언어 팩을 사용 — 한국어/영어는 Windows 11 기본 포함."); RoadmapNote: "Windows에 설치된 OCR 언어 팩을 사용 — 한국어/영어는 Windows 11 기본 포함.");
@ -96,10 +93,6 @@ public sealed class OcrProvider : IConverterProvider
: string.Join(Environment.NewLine + Environment.NewLine + "---" + Environment.NewLine + Environment.NewLine, pageTexts); : string.Join(Environment.NewLine + Environment.NewLine + "---" + Environment.NewLine + Environment.NewLine, pageTexts);
await File.WriteAllTextAsync(path, combined, System.Text.Encoding.UTF8, cancellationToken).ConfigureAwait(false); await File.WriteAllTextAsync(path, combined, System.Text.Encoding.UTF8, cancellationToken).ConfigureAwait(false);
} }
else if (outExt == ".docx")
{
WriteDocx(path, pageTexts);
}
else else
{ {
return ConvertResult.Fail(sourcePath, $"지원하지 않는 출력 형식: {outExt}"); return ConvertResult.Fail(sourcePath, $"지원하지 않는 출력 형식: {outExt}");
@ -189,32 +182,4 @@ public sealed class OcrProvider : IConverterProvider
return result.OutputPaths.ToList(); return result.OutputPaths.ToList();
} }
private static void WriteDocx(string path, IReadOnlyList<string> pageTexts)
{
using var doc = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document);
var mainPart = doc.AddMainDocumentPart();
mainPart.Document = new Document();
var body = mainPart.Document.AppendChild(new Body());
for (var pageIndex = 0; pageIndex < pageTexts.Count; pageIndex++)
{
var pageText = pageTexts[pageIndex] ?? string.Empty;
foreach (var line in pageText.Split('\n', StringSplitOptions.None))
{
var paragraph = body.AppendChild(new Paragraph());
var run = paragraph.AppendChild(new Run());
run.AppendChild(new Text(line.TrimEnd('\r')) { Space = SpaceProcessingModeValues.Preserve });
}
if (pageIndex < pageTexts.Count - 1)
{
var pageBreakPara = body.AppendChild(new Paragraph());
var pageBreakRun = pageBreakPara.AppendChild(new Run());
pageBreakRun.AppendChild(new Break { Type = BreakValues.Page });
}
}
mainPart.Document.Save();
}
} }

View file

@ -29,13 +29,13 @@ public static class QueueFilterMatcher
or ".svg" or ".heic" or ".heif" or ".raw" or ".dng" or ".cr2" or ".cr3" or ".nef" or ".arw" or ".svg" or ".heic" or ".heif" or ".raw" or ".dng" or ".cr2" or ".cr3" or ".nef" or ".arw"
=> FilterCategory.Image, => FilterCategory.Image,
".pdf" or ".docx" or ".doc" or ".hwp" or ".hwpx" or ".txt" or ".md" or ".markdown" or ".html" or ".htm" or ".xlsx" or ".xls" ".pdf" or ".docx" or ".doc" or ".hwp" or ".hwpx" or ".txt" or ".md" or ".markdown" or ".html" or ".htm"
=> FilterCategory.Document, => FilterCategory.Document,
".mp4" or ".mkv" or ".webm" or ".mov" or ".avi" or ".mp3" or ".wav" or ".flac" or ".aac" or ".m4a" or ".ogg" or ".opus" ".mp4" or ".mkv" or ".webm" or ".mov" or ".avi" or ".mp3" or ".wav" or ".flac" or ".aac" or ".m4a" or ".ogg" or ".opus"
=> FilterCategory.Media, => FilterCategory.Media,
".csv" or ".json" or ".tsv" or ".xml" or ".yaml" or ".yml" ".csv" or ".json" or ".tsv" or ".xml" or ".yaml" or ".yml" or ".xlsx" or ".xls"
=> FilterCategory.Data, => FilterCategory.Data,
_ => FilterCategory.All, _ => FilterCategory.All,

View file

@ -1,3 +1,5 @@
using Everything2Everything.Core.Filters;
namespace Everything2Everything.Core.Providers; namespace Everything2Everything.Core.Providers;
public sealed class ProviderRegistry public sealed class ProviderRegistry
@ -63,6 +65,7 @@ public sealed class ProviderRegistry
var input = ConversionPair.Normalize(inputExtension); var input = ConversionPair.Normalize(inputExtension);
return _graph.ReachableOutputs(input, maxHops: 2, allowLossy: true) return _graph.ReachableOutputs(input, maxHops: 2, allowLossy: true)
.Where(o => !string.Equals(o, input, StringComparison.OrdinalIgnoreCase)) .Where(o => !string.Equals(o, input, StringComparison.OrdinalIgnoreCase))
.Where(o => MediaConversionNegotiator.CanConvert(input, o))
.OrderBy(e => e, StringComparer.OrdinalIgnoreCase) .OrderBy(e => e, StringComparer.OrdinalIgnoreCase)
.ToList(); .ToList();
} }