first commit

This commit is contained in:
lihansani
2026-07-13 09:39:56 +08:00
commit 74e4738cfc
158 changed files with 15005 additions and 0 deletions

View File

@@ -0,0 +1,342 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using DocumentFormat.OpenXml;
namespace DCIT.Core.Utilities
{
internal static class VmlTextMetadataUtility
{
private static readonly Regex Base64WhitespaceRegex = new Regex(@"\s+", RegexOptions.Compiled);
public static bool IsVmlShapeElement(OpenXmlElement element)
{
return element != null
&& string.Equals(element.LocalName, "shape", StringComparison.OrdinalIgnoreCase)
&& string.Equals(element.NamespaceUri, "urn:schemas-microsoft-com:vml", StringComparison.OrdinalIgnoreCase);
}
public static IEnumerable<VmlTextBinding> CreateBindings(OpenXmlElement shape)
{
if (!IsVmlShapeElement(shape))
{
yield break;
}
foreach (var binding in CreateEquationBindings(shape))
{
yield return binding;
}
foreach (var binding in CreateChartBindings(shape))
{
yield return binding;
}
}
private static IEnumerable<VmlTextBinding> CreateEquationBindings(OpenXmlElement shape)
{
var attribute = FindAttribute(shape, "equationxml");
if (!attribute.HasValue || string.IsNullOrWhiteSpace(attribute.Value.Value))
{
yield break;
}
var actualAttribute = attribute.Value;
XDocument document;
try
{
var decoded = WebUtility.HtmlDecode(actualAttribute.Value);
if (string.IsNullOrWhiteSpace(decoded))
{
yield break;
}
document = XDocument.Parse(decoded, LoadOptions.PreserveWhitespace);
}
catch
{
yield break;
}
var textNodes = document
.Descendants()
.Where(item => string.Equals(item.Name.LocalName, "t", StringComparison.OrdinalIgnoreCase))
.ToList();
if (textNodes.Count == 0)
{
yield break;
}
foreach (var textNode in textNodes)
{
var initialText = textNode.Value ?? string.Empty;
if (initialText.Length == 0)
{
continue;
}
yield return new VmlTextBinding
{
Text = initialText,
IsEquationText = true,
Apply = updatedText =>
{
textNode.Value = updatedText ?? string.Empty;
SetAttribute(shape, actualAttribute, SerializeXml(document));
},
};
}
}
private static IEnumerable<VmlTextBinding> CreateChartBindings(OpenXmlElement shape)
{
var attribute = FindAttribute(shape, "gfxdata");
if (!attribute.HasValue || string.IsNullOrWhiteSpace(attribute.Value.Value))
{
yield break;
}
var actualAttribute = attribute.Value;
ChartPackageContext context;
try
{
context = ChartPackageContext.TryCreate(shape, actualAttribute);
}
catch
{
context = null;
}
if (context == null || context.XmlEntries.Count == 0)
{
yield break;
}
foreach (var entry in context.XmlEntries)
{
foreach (var textNode in entry.Document
.Descendants()
.Where(item => IsChartVisibleTextNode(item))
.ToList())
{
var initialText = textNode.Value ?? string.Empty;
if (initialText.Length == 0)
{
continue;
}
yield return new VmlTextBinding
{
Text = initialText,
IsChartText = true,
Apply = updatedText =>
{
textNode.Value = updatedText ?? string.Empty;
context.Save();
},
};
}
}
}
private static bool IsChartVisibleTextNode(XElement element)
{
if (element == null)
{
return false;
}
var localName = element.Name.LocalName;
return string.Equals(localName, "t", StringComparison.OrdinalIgnoreCase)
|| string.Equals(localName, "v", StringComparison.OrdinalIgnoreCase);
}
private static OpenXmlAttribute? FindAttribute(OpenXmlElement element, string localName)
{
if (element == null || string.IsNullOrWhiteSpace(localName))
{
return null;
}
foreach (var attribute in element.GetAttributes())
{
if (string.Equals(attribute.LocalName, localName, StringComparison.OrdinalIgnoreCase))
{
return attribute;
}
}
return null;
}
private static void SetAttribute(OpenXmlElement element, OpenXmlAttribute sourceAttribute, string value)
{
element.SetAttribute(new OpenXmlAttribute(
sourceAttribute.Prefix,
sourceAttribute.LocalName,
sourceAttribute.NamespaceUri,
value ?? string.Empty));
}
private static string SerializeXml(XDocument document)
{
using (var writer = new Utf8StringWriter())
using (var xmlWriter = XmlWriter.Create(writer, new XmlWriterSettings
{
OmitXmlDeclaration = document.Declaration == null,
Encoding = new UTF8Encoding(false),
Indent = false,
NewLineHandling = NewLineHandling.None,
}))
{
document.Save(xmlWriter);
xmlWriter.Flush();
return writer.ToString();
}
}
private sealed class ChartPackageContext
{
private readonly OpenXmlElement _shape;
private readonly OpenXmlAttribute _attribute;
private readonly List<EntryState> _allEntries;
private ChartPackageContext(OpenXmlElement shape, OpenXmlAttribute attribute, List<EntryState> allEntries, List<EntryState> xmlEntries)
{
_shape = shape;
_attribute = attribute;
_allEntries = allEntries;
XmlEntries = xmlEntries;
}
public List<EntryState> XmlEntries { get; private set; }
public static ChartPackageContext TryCreate(OpenXmlElement shape, OpenXmlAttribute attribute)
{
var decoded = WebUtility.HtmlDecode(attribute.Value ?? string.Empty);
if (string.IsNullOrWhiteSpace(decoded))
{
return null;
}
var normalizedBase64 = Base64WhitespaceRegex.Replace(decoded, string.Empty);
if (normalizedBase64.Length == 0)
{
return null;
}
var bytes = Convert.FromBase64String(normalizedBase64);
var allEntries = new List<EntryState>();
var xmlEntries = new List<EntryState>();
using (var input = new MemoryStream(bytes))
using (var zip = new ZipArchive(input, ZipArchiveMode.Read, false))
{
foreach (var entry in zip.Entries)
{
var state = new EntryState
{
FullName = entry.FullName,
LastWriteTime = entry.LastWriteTime,
};
using (var entryStream = entry.Open())
using (var memory = new MemoryStream())
{
entryStream.CopyTo(memory);
state.RawBytes = memory.ToArray();
}
if (entry.FullName.IndexOf("drs/charts/", StringComparison.OrdinalIgnoreCase) >= 0
&& entry.FullName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
try
{
state.Document = XDocument.Parse(
Encoding.UTF8.GetString(state.RawBytes),
LoadOptions.PreserveWhitespace);
}
catch
{
state.Document = null;
}
if (state.Document != null)
{
xmlEntries.Add(state);
}
}
allEntries.Add(state);
}
}
return new ChartPackageContext(shape, attribute, allEntries, xmlEntries);
}
public void Save()
{
using (var output = new MemoryStream())
{
using (var zip = new ZipArchive(output, ZipArchiveMode.Create, true))
{
foreach (var entry in _allEntries)
{
var targetEntry = zip.CreateEntry(entry.FullName, CompressionLevel.Optimal);
targetEntry.LastWriteTime = entry.LastWriteTime;
using (var targetStream = targetEntry.Open())
{
var bytes = entry.Document == null
? (entry.RawBytes ?? Array.Empty<byte>())
: Encoding.UTF8.GetBytes(SerializeXml(entry.Document));
targetStream.Write(bytes, 0, bytes.Length);
}
}
}
SetAttribute(_shape, _attribute, Convert.ToBase64String(output.ToArray()));
}
}
}
private sealed class Utf8StringWriter : StringWriter
{
public override Encoding Encoding
{
get { return new UTF8Encoding(false); }
}
}
private sealed class EntryState
{
public string FullName { get; set; }
public DateTimeOffset LastWriteTime { get; set; }
public byte[] RawBytes { get; set; }
public XDocument Document { get; set; }
}
}
internal sealed class VmlTextBinding
{
public string Text { get; set; }
public bool IsEquationText { get; set; }
public bool IsChartText { get; set; }
public Action<string> Apply { get; set; }
}
}