first commit

This commit is contained in:
lihansani
2026-07-13 10:04:36 +08:00
commit 577aa128bd
870 changed files with 25849 additions and 0 deletions

View File

@@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using DCIT.Core.Models;
using DCIT.Core.Utilities;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using W = DocumentFormat.OpenXml.Wordprocessing;
namespace DCIT.Core.Services
{
public class WordTextExtractionService
{
private readonly IDocConversionService _docConversionService;
public WordTextExtractionService()
: this(new OfficeDocConversionService())
{
}
public WordTextExtractionService(IDocConversionService docConversionService)
{
_docConversionService = docConversionService;
}
public ExtractedDocumentText ExtractVisibleText(string filePath)
{
var workingPath = filePath;
string temporaryDocxPath = null;
try
{
var extension = Path.GetExtension(filePath) ?? string.Empty;
if (extension.Equals(".doc", StringComparison.OrdinalIgnoreCase))
{
if (_docConversionService == null)
{
throw new NotSupportedException("当前环境未配置 .doc 转换服务。");
}
temporaryDocxPath = Path.Combine(
Path.GetTempPath(),
"DCIT",
"extract",
Guid.NewGuid().ToString("N") + ".docx");
Directory.CreateDirectory(Path.GetDirectoryName(temporaryDocxPath) ?? Path.GetTempPath());
_docConversionService.Convert(filePath, temporaryDocxPath);
workingPath = temporaryDocxPath;
}
using (var document = WordprocessingDocument.Open(workingPath, false))
{
var builder = new StringBuilder();
var rootElements = GetRelevantRootElements(document);
foreach (var root in rootElements)
{
AppendVisibleText(root, builder);
if (builder.Length > 0 && builder[builder.Length - 1] != '\n')
{
builder.AppendLine();
}
}
return new ExtractedDocumentText
{
FilePath = filePath,
VisibleText = builder.ToString(),
};
}
}
finally
{
TryDelete(temporaryDocxPath);
}
}
private static IEnumerable<OpenXmlPartRootElement> GetRelevantRootElements(WordprocessingDocument document)
{
var result = new List<KeyValuePair<string, OpenXmlPartRootElement>>();
var partQueue = new Queue<OpenXmlPartContainer>();
partQueue.Enqueue(document.MainDocumentPart);
var visited = new HashSet<OpenXmlPart>();
while (partQueue.Count > 0)
{
var container = partQueue.Dequeue();
foreach (var pair in container.Parts)
{
var part = pair.OpenXmlPart;
if (part == null || !visited.Add(part))
{
continue;
}
if (part.RootElement != null && IsRelevantPart(part))
{
result.Add(new KeyValuePair<string, OpenXmlPartRootElement>(GetStablePartKey(part), part.RootElement));
}
partQueue.Enqueue(part);
}
}
var ordered = result
.OrderBy(item => item.Key, StringComparer.OrdinalIgnoreCase)
.Select(item => item.Value)
.ToList();
if (document.MainDocumentPart != null && document.MainDocumentPart.Document != null)
{
ordered.Insert(0, document.MainDocumentPart.Document);
}
return ordered;
}
private static bool IsRelevantPart(OpenXmlPart part)
{
return part is HeaderPart
|| part is FooterPart
|| part is WordprocessingCommentsPart
|| part is FootnotesPart
|| part is EndnotesPart
|| part.GetType().Name.IndexOf("ChartPart", StringComparison.OrdinalIgnoreCase) >= 0;
}
private static string GetStablePartKey(OpenXmlPart part)
{
return part == null || part.Uri == null
? string.Empty
: part.Uri.ToString();
}
private static void AppendVisibleText(OpenXmlElement root, StringBuilder builder)
{
foreach (var element in root.Descendants())
{
var leaf = element as OpenXmlLeafTextElement;
if (leaf != null)
{
if (!IsVisibleLeaf(leaf))
{
continue;
}
var text = leaf.Text;
if (string.IsNullOrWhiteSpace(text))
{
continue;
}
builder.Append(text);
builder.Append(' ');
continue;
}
if (!VmlTextMetadataUtility.IsVmlShapeElement(element))
{
continue;
}
foreach (var binding in VmlTextMetadataUtility.CreateBindings(element))
{
if (string.IsNullOrWhiteSpace(binding.Text))
{
continue;
}
builder.Append(binding.Text);
builder.Append(' ');
}
}
}
private static bool IsVisibleLeaf(OpenXmlLeafTextElement leaf)
{
if (leaf is W.FieldCode || leaf is W.DeletedText)
{
return false;
}
if (leaf.Ancestors<W.DeletedRun>().Any())
{
return false;
}
var run = leaf.Ancestors<W.Run>().FirstOrDefault();
if (run != null && run.RunProperties != null && run.RunProperties.Vanish != null)
{
return false;
}
return true;
}
private static void TryDelete(string path)
{
try
{
if (!string.IsNullOrWhiteSpace(path) && File.Exists(path))
{
File.Delete(path);
}
}
catch
{
}
}
}
}