Files
skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/TrackChangesHelper.cs
shihao 6487becf60 Initial commit: add all skills files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 16:52:49 +08:00

100 lines
3.0 KiB
C#

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
namespace MiniMaxAIDocx.Core.OpenXml;
/// <summary>
/// Helpers for Track Changes (revision marks) operations.
/// </summary>
public static class TrackChangesHelper
{
/// <summary>
/// Wraps a run in a w:ins element to propose an insertion.
/// </summary>
public static InsertedRun ProposeInsertion(Run run, string author, DateTime date)
{
var ins = new InsertedRun
{
Author = author,
Date = date,
Id = run.Parent is Body body ? GetNextRevisionId(body).ToString() : "1"
};
run.Remove();
ins.Append(run);
return ins;
}
/// <summary>
/// Wraps a run in a w:del element, converting w:t to w:delText.
/// </summary>
public static DeletedRun ProposeDeletion(Run run, string author, DateTime date)
{
// Convert w:t elements to w:delText
foreach (var text in run.Elements<Text>().ToList())
{
var delText = new DeletedText { Text = text.Text, Space = SpaceProcessingModeValues.Preserve };
text.InsertAfterSelf(delText);
text.Remove();
}
var del = new DeletedRun
{
Author = author,
Date = date,
Id = run.Parent is Body body ? GetNextRevisionId(body).ToString() : "1"
};
run.Remove();
del.Append(run);
return del;
}
/// <summary>
/// Accepts an insertion by removing the w:ins wrapper and keeping content.
/// </summary>
public static void AcceptInsertion(OpenXmlElement insElement)
{
if (insElement is not InsertedRun) return;
var parent = insElement.Parent;
if (parent == null) return;
var children = insElement.ChildElements.ToList();
foreach (var child in children)
{
child.Remove();
insElement.InsertBeforeSelf(child);
}
insElement.Remove();
}
/// <summary>
/// Accepts a deletion by removing the entire w:del element and its content.
/// </summary>
public static void AcceptDeletion(OpenXmlElement delElement)
{
delElement.Remove();
}
/// <summary>
/// Finds the maximum existing revision ID in the document and returns the next one.
/// </summary>
public static int GetNextRevisionId(WordprocessingDocument doc)
{
var body = doc.MainDocumentPart?.Document?.Body;
if (body == null) return 1;
return GetNextRevisionId(body);
}
private static int GetNextRevisionId(OpenXmlElement root)
{
int maxId = 0;
foreach (var element in root.Descendants())
{
var idAttr = element.GetAttributes().FirstOrDefault(a => a.LocalName == "id");
if (idAttr.Value != null && int.TryParse(idAttr.Value, out int id) && id > maxId)
maxId = id;
}
return maxId + 1;
}
}