@nguyenphp/antigravity-marketing 1.0.18 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +186 -78
- package/package.json +4 -3
- package/templates/.agent/skills/marketing-report-expert/SKILL.md +70 -0
- package/templates/.agent/skills/minimax-docx/LICENSE +21 -0
- package/templates/.agent/skills/minimax-docx/SKILL.md +274 -0
- package/templates/.agent/skills/minimax-docx/assets/styles/academic_styles.xml +250 -0
- package/templates/.agent/skills/minimax-docx/assets/styles/corporate_styles.xml +284 -0
- package/templates/.agent/skills/minimax-docx/assets/styles/default_styles.xml +449 -0
- package/templates/.agent/skills/minimax-docx/assets/xsd/aesthetic-rules.xsd +470 -0
- package/templates/.agent/skills/minimax-docx/assets/xsd/business-rules.xsd +130 -0
- package/templates/.agent/skills/minimax-docx/assets/xsd/common-types.xsd +159 -0
- package/templates/.agent/skills/minimax-docx/assets/xsd/wml-subset.xsd +589 -0
- package/templates/.agent/skills/minimax-docx/references/cjk_typography.md +357 -0
- package/templates/.agent/skills/minimax-docx/references/cjk_university_template_guide.md +184 -0
- package/templates/.agent/skills/minimax-docx/references/comments_guide.md +191 -0
- package/templates/.agent/skills/minimax-docx/references/design_good_bad_examples.md +829 -0
- package/templates/.agent/skills/minimax-docx/references/design_principles.md +819 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_element_order.md +308 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part1.md +4061 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part2.md +2820 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part3.md +3381 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_namespaces.md +82 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_units.md +72 -0
- package/templates/.agent/skills/minimax-docx/references/scenario_a_create.md +284 -0
- package/templates/.agent/skills/minimax-docx/references/scenario_b_edit_content.md +295 -0
- package/templates/.agent/skills/minimax-docx/references/scenario_c_apply_template.md +456 -0
- package/templates/.agent/skills/minimax-docx/references/track_changes_guide.md +200 -0
- package/templates/.agent/skills/minimax-docx/references/troubleshooting.md +506 -0
- package/templates/.agent/skills/minimax-docx/references/typography_guide.md +294 -0
- package/templates/.agent/skills/minimax-docx/references/xsd_validation_guide.md +158 -0
- package/templates/.agent/skills/minimax-docx/scripts/doc_to_docx.sh +40 -0
- package/templates/.agent/skills/minimax-docx/scripts/docx_preview.sh +37 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/MiniMaxAIDocx.Cli.csproj +19 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/Program.cs +18 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/AnalyzeCommand.cs +147 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ApplyTemplateCommand.cs +322 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/CreateCommand.cs +324 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/DiffCommand.cs +155 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/EditContentCommand.cs +487 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/FixOrderCommand.cs +108 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/MergeRunsCommand.cs +122 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ValidateCommand.cs +107 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/MiniMaxAIDocx.Core.csproj +15 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/CommentSynchronizer.cs +169 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/ElementOrder.cs +80 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/NamespaceConstants.cs +42 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/RunMerger.cs +81 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/StyleAnalyzer.cs +81 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/TrackChangesHelper.cs +99 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/UnitConverter.cs +23 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples.cs +1832 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch1.cs +910 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch2.cs +999 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch3.cs +1048 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch4.cs +1038 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/CharacterFormattingSamples.cs +1020 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/DocumentCreationSamples.cs +1121 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FieldAndTocSamples.cs +624 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FootnoteAndCommentSamples.cs +675 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/HeaderFooterSamples.cs +838 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ImageSamples.cs +917 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ListAndNumberingSamples.cs +826 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ParagraphFormattingSamples.cs +1199 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/StyleSystemSamples.cs +1487 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TableSamples.cs +1163 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TrackChangesSamples.cs +595 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/CjkHelper.cs +39 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/FontDefaults.cs +24 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/PageSizes.cs +20 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/BusinessRuleValidator.cs +224 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/GateCheckValidator.cs +148 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/ValidationResult.cs +23 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/XsdValidator.cs +69 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.slnx +4 -0
- package/templates/.agent/skills/minimax-docx/scripts/env_check.sh +196 -0
- package/templates/.agent/skills/minimax-docx/scripts/setup.ps1 +274 -0
- package/templates/.agent/skills/minimax-docx/scripts/setup.sh +504 -0
- package/templates/.agent/skills/minimax-multimodal-toolkit/SKILL.md +359 -0
- package/templates/.agent/skills/minimax-pdf/README.md +222 -0
- package/templates/.agent/skills/minimax-pdf/SKILL.md +201 -0
- package/templates/.agent/skills/minimax-pdf/design/design.md +381 -0
- package/templates/.agent/skills/minimax-pdf/scripts/cover.py +1579 -0
- package/templates/.agent/skills/minimax-pdf/scripts/fill_inspect.py +200 -0
- package/templates/.agent/skills/minimax-pdf/scripts/fill_write.py +242 -0
- package/templates/.agent/skills/minimax-pdf/scripts/make.sh +491 -0
- package/templates/.agent/skills/minimax-pdf/scripts/merge.py +112 -0
- package/templates/.agent/skills/minimax-pdf/scripts/palette.py +559 -0
- package/templates/.agent/skills/minimax-pdf/scripts/reformat_parse.py +374 -0
- package/templates/.agent/skills/minimax-pdf/scripts/render_body.py +1055 -0
- package/templates/.agent/skills/minimax-pdf/scripts/render_cover.cjs +111 -0
- package/templates/.agent/skills/minimax-xlsx/SKILL.md +138 -0
- package/templates/.agent/skills/minimax-xlsx/references/create.md +691 -0
- package/templates/.agent/skills/minimax-xlsx/references/edit.md +684 -0
- package/templates/.agent/skills/minimax-xlsx/references/fix.md +37 -0
- package/templates/.agent/skills/minimax-xlsx/references/format.md +768 -0
- package/templates/.agent/skills/minimax-xlsx/references/ooxml-cheatsheet.md +231 -0
- package/templates/.agent/skills/minimax-xlsx/references/read-analyze.md +97 -0
- package/templates/.agent/skills/minimax-xlsx/references/validate.md +772 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/formula_check.py +422 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/libreoffice_recalc.py +248 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/shared_strings_builder.py +163 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/style_audit.py +575 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_add_column.py +395 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_insert_row.py +274 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_pack.py +87 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_reader.py +362 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_shift_rows.py +396 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_unpack.py +130 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/[Content_Types].xml +9 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/_rels/.rels +6 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/_rels/workbook.xml.rels +19 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/sharedStrings.xml +33 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/styles.xml +160 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/workbook.xml +30 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/worksheets/sheet1.xml +70 -0
- package/templates/.agent/skills/pptx-generator/SKILL.md +249 -0
- package/templates/.agent/skills/pptx-generator/references/design-system.md +392 -0
- package/templates/.agent/skills/pptx-generator/references/editing.md +162 -0
- package/templates/.agent/skills/pptx-generator/references/pitfalls.md +112 -0
- package/templates/.agent/skills/pptx-generator/references/pptxgenjs.md +420 -0
- package/templates/.agent/skills/pptx-generator/references/slide-types.md +413 -0
- package/templates/.agent/skills/tutorial-video-expert/SKILL.md +88 -0
- package/templates/.agent/skills/ui-ux-pro-max/SKILL.md +170 -585
- package/templates/.agent/skills/vision-analysis/SKILL.md +174 -0
- package/templates/.agent/workflows/analyze.md +3 -0
- package/templates/.agent/workflows/brand-report.md +44 -0
- package/templates/.agent/workflows/report.md +49 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
using System.CommandLine;
|
|
2
|
+
using System.IO.Compression;
|
|
3
|
+
using System.Text.Json;
|
|
4
|
+
using System.Xml.Linq;
|
|
5
|
+
|
|
6
|
+
namespace MiniMaxAIDocx.Core.Commands;
|
|
7
|
+
|
|
8
|
+
public static class AnalyzeCommand
|
|
9
|
+
{
|
|
10
|
+
private static readonly XNamespace W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
11
|
+
private static readonly XNamespace WP = "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing";
|
|
12
|
+
|
|
13
|
+
public static Command Create()
|
|
14
|
+
{
|
|
15
|
+
var inputOption = new Option<string>("--input") { Description = "DOCX file to analyze", Required = true };
|
|
16
|
+
var jsonOption = new Option<bool>("--json") { Description = "Output as JSON" };
|
|
17
|
+
|
|
18
|
+
var cmd = new Command("analyze", "Analyze document structure and styles")
|
|
19
|
+
{
|
|
20
|
+
inputOption, jsonOption
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
cmd.SetAction((parseResult) =>
|
|
24
|
+
{
|
|
25
|
+
var input = parseResult.GetValue(inputOption)!;
|
|
26
|
+
var asJson = parseResult.GetValue(jsonOption);
|
|
27
|
+
|
|
28
|
+
if (!File.Exists(input))
|
|
29
|
+
{
|
|
30
|
+
Console.Error.WriteLine($"File not found: {input}");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
using var zip = ZipFile.OpenRead(input);
|
|
35
|
+
var docEntry = zip.GetEntry("word/document.xml");
|
|
36
|
+
if (docEntry == null)
|
|
37
|
+
{
|
|
38
|
+
Console.Error.WriteLine("Not a valid DOCX");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
XDocument doc;
|
|
43
|
+
using (var stream = docEntry.Open())
|
|
44
|
+
doc = XDocument.Load(stream);
|
|
45
|
+
|
|
46
|
+
var body = doc.Root?.Element(W + "body");
|
|
47
|
+
if (body == null) return;
|
|
48
|
+
|
|
49
|
+
// Sections
|
|
50
|
+
var sections = body.Descendants(W + "sectPr").ToList();
|
|
51
|
+
var sectionBreaks = sections.Select(s => (string?)s.Element(W + "type")?.Attribute(W + "val") ?? "nextPage").ToList();
|
|
52
|
+
|
|
53
|
+
// Headings
|
|
54
|
+
var headings = new List<object>();
|
|
55
|
+
foreach (var p in body.Descendants(W + "p"))
|
|
56
|
+
{
|
|
57
|
+
var style = (string?)p.Element(W + "pPr")?.Element(W + "pStyle")?.Attribute(W + "val");
|
|
58
|
+
if (style?.StartsWith("Heading", StringComparison.OrdinalIgnoreCase) == true)
|
|
59
|
+
{
|
|
60
|
+
var text = string.Concat(p.Descendants(W + "t").Select(t => t.Value));
|
|
61
|
+
headings.Add(new { style, text });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Tables
|
|
66
|
+
var tables = body.Descendants(W + "tbl").Select(tbl => new
|
|
67
|
+
{
|
|
68
|
+
rows = tbl.Elements(W + "tr").Count(),
|
|
69
|
+
cols = tbl.Elements(W + "tr").FirstOrDefault()?.Elements(W + "tc").Count() ?? 0
|
|
70
|
+
}).ToList();
|
|
71
|
+
|
|
72
|
+
// Images
|
|
73
|
+
var images = body.Descendants(W + "drawing").Count();
|
|
74
|
+
|
|
75
|
+
// Headers/footers
|
|
76
|
+
var headerRefs = sections.SelectMany(s => s.Elements(W + "headerReference")).Count();
|
|
77
|
+
var footerRefs = sections.SelectMany(s => s.Elements(W + "footerReference")).Count();
|
|
78
|
+
|
|
79
|
+
// Paragraphs and word count
|
|
80
|
+
var paragraphs = body.Descendants(W + "p").ToList();
|
|
81
|
+
var allText = string.Concat(body.Descendants(W + "t").Select(t => t.Value));
|
|
82
|
+
var wordCount = allText.Split(new[] { ' ', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries).Length;
|
|
83
|
+
|
|
84
|
+
// XML file sizes
|
|
85
|
+
var fileSizes = zip.Entries
|
|
86
|
+
.Where(e => e.FullName.StartsWith("word/") && e.FullName.EndsWith(".xml"))
|
|
87
|
+
.Select(e => new { file = e.FullName, size = e.Length })
|
|
88
|
+
.OrderByDescending(e => e.size)
|
|
89
|
+
.ToList();
|
|
90
|
+
|
|
91
|
+
// Styles
|
|
92
|
+
var styleNames = new List<string>();
|
|
93
|
+
var stylesEntry = zip.GetEntry("word/styles.xml");
|
|
94
|
+
if (stylesEntry != null)
|
|
95
|
+
{
|
|
96
|
+
using var stream = stylesEntry.Open();
|
|
97
|
+
var stylesDoc = XDocument.Load(stream);
|
|
98
|
+
styleNames = stylesDoc.Descendants(W + "style")
|
|
99
|
+
.Where(s => (string?)s.Attribute(W + "customStyle") == "1")
|
|
100
|
+
.Select(s => (string?)s.Attribute(W + "styleId") ?? "")
|
|
101
|
+
.Where(s => s != "")
|
|
102
|
+
.ToList();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
var analysis = new
|
|
106
|
+
{
|
|
107
|
+
sections = new { count = sections.Count, breakTypes = sectionBreaks },
|
|
108
|
+
headings,
|
|
109
|
+
tables = new { count = tables.Count, details = tables },
|
|
110
|
+
images,
|
|
111
|
+
headerFooter = new { headers = headerRefs, footers = footerRefs },
|
|
112
|
+
paragraphs = paragraphs.Count,
|
|
113
|
+
estimatedWordCount = wordCount,
|
|
114
|
+
xmlFileSizes = fileSizes,
|
|
115
|
+
customStyles = new { count = styleNames.Count, names = styleNames }
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
if (asJson)
|
|
119
|
+
{
|
|
120
|
+
Console.WriteLine(JsonSerializer.Serialize(analysis, new JsonSerializerOptions { WriteIndented = true }));
|
|
121
|
+
}
|
|
122
|
+
else
|
|
123
|
+
{
|
|
124
|
+
Console.WriteLine($"Sections: {sections.Count} ({string.Join(", ", sectionBreaks)})");
|
|
125
|
+
Console.WriteLine($"Headings: {headings.Count}");
|
|
126
|
+
foreach (var h in headings)
|
|
127
|
+
Console.WriteLine($" {h}");
|
|
128
|
+
Console.WriteLine($"Tables: {tables.Count}");
|
|
129
|
+
foreach (var t in tables)
|
|
130
|
+
Console.WriteLine($" {t.rows} rows x {t.cols} cols");
|
|
131
|
+
Console.WriteLine($"Images: {images}");
|
|
132
|
+
Console.WriteLine($"Headers: {headerRefs}");
|
|
133
|
+
Console.WriteLine($"Footers: {footerRefs}");
|
|
134
|
+
Console.WriteLine($"Paragraphs: {paragraphs.Count}");
|
|
135
|
+
Console.WriteLine($"Word count: ~{wordCount}");
|
|
136
|
+
Console.WriteLine($"Custom styles: {styleNames.Count}");
|
|
137
|
+
foreach (var s in styleNames)
|
|
138
|
+
Console.WriteLine($" {s}");
|
|
139
|
+
Console.WriteLine("XML file sizes:");
|
|
140
|
+
foreach (var f in fileSizes)
|
|
141
|
+
Console.WriteLine($" {f.file}: {f.size:N0} bytes");
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return cmd;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
using System.CommandLine;
|
|
2
|
+
using DocumentFormat.OpenXml;
|
|
3
|
+
using DocumentFormat.OpenXml.Packaging;
|
|
4
|
+
using DocumentFormat.OpenXml.Wordprocessing;
|
|
5
|
+
|
|
6
|
+
namespace MiniMaxAIDocx.Core.Commands;
|
|
7
|
+
|
|
8
|
+
/// <summary>
|
|
9
|
+
/// Scenario C: Apply formatting from a template DOCX to a source DOCX.
|
|
10
|
+
/// Copies styles, theme, numbering, headers/footers, and section properties
|
|
11
|
+
/// from the template while preserving all content from the source.
|
|
12
|
+
/// </summary>
|
|
13
|
+
public static class ApplyTemplateCommand
|
|
14
|
+
{
|
|
15
|
+
public static Command Create()
|
|
16
|
+
{
|
|
17
|
+
var inputOpt = new Option<string>("--input") { Description = "Source DOCX (content to keep)", Required = true };
|
|
18
|
+
var templateOpt = new Option<string>("--template") { Description = "Template DOCX (formatting to apply)", Required = true };
|
|
19
|
+
var outputOpt = new Option<string>("--output") { Description = "Output DOCX file path", Required = true };
|
|
20
|
+
var applyStylesOpt = new Option<bool>("--apply-styles") { Description = "Copy styles.xml from template" };
|
|
21
|
+
applyStylesOpt.DefaultValueFactory = _ => true;
|
|
22
|
+
var applyThemeOpt = new Option<bool>("--apply-theme") { Description = "Copy theme from template" };
|
|
23
|
+
applyThemeOpt.DefaultValueFactory = _ => true;
|
|
24
|
+
var applyNumberingOpt = new Option<bool>("--apply-numbering") { Description = "Copy numbering.xml from template" };
|
|
25
|
+
applyNumberingOpt.DefaultValueFactory = _ => true;
|
|
26
|
+
var applyHeadersFootersOpt = new Option<bool>("--apply-headers-footers") { Description = "Copy headers/footers from template" };
|
|
27
|
+
var applySectionsOpt = new Option<bool>("--apply-sections") { Description = "Apply section properties from template" };
|
|
28
|
+
applySectionsOpt.DefaultValueFactory = _ => true;
|
|
29
|
+
|
|
30
|
+
var cmd = new Command("apply-template", "Apply template formatting to a DOCX")
|
|
31
|
+
{
|
|
32
|
+
inputOpt, templateOpt, outputOpt, applyStylesOpt, applyThemeOpt,
|
|
33
|
+
applyNumberingOpt, applyHeadersFootersOpt, applySectionsOpt
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
cmd.SetAction((parseResult) =>
|
|
37
|
+
{
|
|
38
|
+
var inputPath = parseResult.GetValue(inputOpt)!;
|
|
39
|
+
var templatePath = parseResult.GetValue(templateOpt)!;
|
|
40
|
+
var outputPath = parseResult.GetValue(outputOpt)!;
|
|
41
|
+
var applyStyles = parseResult.GetValue(applyStylesOpt);
|
|
42
|
+
var applyTheme = parseResult.GetValue(applyThemeOpt);
|
|
43
|
+
var applyNumbering = parseResult.GetValue(applyNumberingOpt);
|
|
44
|
+
var applyHeadersFooters = parseResult.GetValue(applyHeadersFootersOpt);
|
|
45
|
+
var applySections = parseResult.GetValue(applySectionsOpt);
|
|
46
|
+
|
|
47
|
+
if (!File.Exists(inputPath)) { Console.Error.WriteLine($"Input file not found: {inputPath}"); return; }
|
|
48
|
+
if (!File.Exists(templatePath)) { Console.Error.WriteLine($"Template file not found: {templatePath}"); return; }
|
|
49
|
+
|
|
50
|
+
// Create output as a copy of the source
|
|
51
|
+
File.Copy(inputPath, outputPath, overwrite: true);
|
|
52
|
+
|
|
53
|
+
using var output = WordprocessingDocument.Open(outputPath, true);
|
|
54
|
+
using var template = WordprocessingDocument.Open(templatePath, false);
|
|
55
|
+
|
|
56
|
+
var outputMain = output.MainDocumentPart;
|
|
57
|
+
var templateMain = template.MainDocumentPart;
|
|
58
|
+
if (outputMain == null || templateMain == null)
|
|
59
|
+
{
|
|
60
|
+
Console.Error.WriteLine("Invalid document: missing main document part.");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
int appliedCount = 0;
|
|
65
|
+
|
|
66
|
+
if (applyStyles)
|
|
67
|
+
{
|
|
68
|
+
CopyStyles(templateMain, outputMain);
|
|
69
|
+
appliedCount++;
|
|
70
|
+
Console.WriteLine(" Applied: styles");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (applyTheme)
|
|
74
|
+
{
|
|
75
|
+
CopyTheme(templateMain, outputMain);
|
|
76
|
+
appliedCount++;
|
|
77
|
+
Console.WriteLine(" Applied: theme");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (applyNumbering)
|
|
81
|
+
{
|
|
82
|
+
CopyNumbering(templateMain, outputMain);
|
|
83
|
+
appliedCount++;
|
|
84
|
+
Console.WriteLine(" Applied: numbering");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (applyHeadersFooters)
|
|
88
|
+
{
|
|
89
|
+
CopyHeadersAndFooters(templateMain, outputMain);
|
|
90
|
+
appliedCount++;
|
|
91
|
+
Console.WriteLine(" Applied: headers/footers");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (applySections)
|
|
95
|
+
{
|
|
96
|
+
CopySectionProperties(templateMain, outputMain);
|
|
97
|
+
appliedCount++;
|
|
98
|
+
Console.WriteLine(" Applied: section properties");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
outputMain.Document.Save();
|
|
102
|
+
Console.WriteLine($"Applied {appliedCount} formatting component(s) from template to {outputPath}");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return cmd;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/// <summary>
|
|
109
|
+
/// Replaces the output's StyleDefinitionsPart with the template's version.
|
|
110
|
+
/// </summary>
|
|
111
|
+
private static void CopyStyles(MainDocumentPart template, MainDocumentPart output)
|
|
112
|
+
{
|
|
113
|
+
var templateStyles = template.StyleDefinitionsPart;
|
|
114
|
+
if (templateStyles == null) return;
|
|
115
|
+
|
|
116
|
+
if (output.StyleDefinitionsPart != null)
|
|
117
|
+
output.DeletePart(output.StyleDefinitionsPart);
|
|
118
|
+
|
|
119
|
+
var newStylesPart = output.AddNewPart<StyleDefinitionsPart>();
|
|
120
|
+
|
|
121
|
+
using var stream = templateStyles.GetStream(FileMode.Open, FileAccess.Read);
|
|
122
|
+
newStylesPart.FeedData(stream);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/// <summary>
|
|
126
|
+
/// Replaces the output's ThemePart with the template's version.
|
|
127
|
+
/// </summary>
|
|
128
|
+
private static void CopyTheme(MainDocumentPart template, MainDocumentPart output)
|
|
129
|
+
{
|
|
130
|
+
var templateTheme = template.ThemePart;
|
|
131
|
+
if (templateTheme == null) return;
|
|
132
|
+
|
|
133
|
+
if (output.ThemePart != null)
|
|
134
|
+
output.DeletePart(output.ThemePart);
|
|
135
|
+
|
|
136
|
+
var newThemePart = output.AddNewPart<ThemePart>();
|
|
137
|
+
|
|
138
|
+
using var stream = templateTheme.GetStream(FileMode.Open, FileAccess.Read);
|
|
139
|
+
newThemePart.FeedData(stream);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/// <summary>
|
|
143
|
+
/// Copies numbering definitions from template, remapping numbering IDs
|
|
144
|
+
/// referenced in the output document's paragraphs.
|
|
145
|
+
/// </summary>
|
|
146
|
+
private static void CopyNumbering(MainDocumentPart template, MainDocumentPart output)
|
|
147
|
+
{
|
|
148
|
+
var templateNumbering = template.NumberingDefinitionsPart;
|
|
149
|
+
if (templateNumbering == null) return;
|
|
150
|
+
|
|
151
|
+
var referencedNumIds = new HashSet<string>();
|
|
152
|
+
var body = output.Document.Body;
|
|
153
|
+
if (body != null)
|
|
154
|
+
{
|
|
155
|
+
foreach (var numId in body.Descendants<NumberingId>())
|
|
156
|
+
{
|
|
157
|
+
if (numId.Val?.Value != null)
|
|
158
|
+
referencedNumIds.Add(numId.Val.Value.ToString());
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (output.NumberingDefinitionsPart != null)
|
|
163
|
+
output.DeletePart(output.NumberingDefinitionsPart);
|
|
164
|
+
|
|
165
|
+
var newNumberingPart = output.AddNewPart<NumberingDefinitionsPart>();
|
|
166
|
+
|
|
167
|
+
using var stream = templateNumbering.GetStream(FileMode.Open, FileAccess.Read);
|
|
168
|
+
newNumberingPart.FeedData(stream);
|
|
169
|
+
|
|
170
|
+
if (referencedNumIds.Count > 0)
|
|
171
|
+
{
|
|
172
|
+
Console.WriteLine($" Note: {referencedNumIds.Count} numbering reference(s) in document content mapped to template definitions.");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// <summary>
|
|
177
|
+
/// Copies headers and footers from the template, remapping relationship IDs.
|
|
178
|
+
/// </summary>
|
|
179
|
+
private static void CopyHeadersAndFooters(MainDocumentPart template, MainDocumentPart output)
|
|
180
|
+
{
|
|
181
|
+
var outputBody = output.Document.Body;
|
|
182
|
+
if (outputBody == null) return;
|
|
183
|
+
|
|
184
|
+
// Remove existing header/footer parts from output
|
|
185
|
+
foreach (var hp in output.HeaderParts.ToList())
|
|
186
|
+
output.DeletePart(hp);
|
|
187
|
+
foreach (var fp in output.FooterParts.ToList())
|
|
188
|
+
output.DeletePart(fp);
|
|
189
|
+
|
|
190
|
+
// Remove existing header/footer references from all section properties
|
|
191
|
+
foreach (var sectPr in outputBody.Descendants<SectionProperties>())
|
|
192
|
+
{
|
|
193
|
+
foreach (var hr in sectPr.Elements<HeaderReference>().ToList())
|
|
194
|
+
hr.Remove();
|
|
195
|
+
foreach (var fr in sectPr.Elements<FooterReference>().ToList())
|
|
196
|
+
fr.Remove();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
var templateBody = template.Document?.Body;
|
|
200
|
+
if (templateBody == null) return;
|
|
201
|
+
|
|
202
|
+
var templateFinalSectPr = templateBody.Descendants<SectionProperties>().LastOrDefault();
|
|
203
|
+
if (templateFinalSectPr == null) return;
|
|
204
|
+
|
|
205
|
+
var outputFinalSectPr = outputBody.Descendants<SectionProperties>().LastOrDefault();
|
|
206
|
+
if (outputFinalSectPr == null)
|
|
207
|
+
{
|
|
208
|
+
outputFinalSectPr = new SectionProperties();
|
|
209
|
+
outputBody.Append(outputFinalSectPr);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Copy headers
|
|
213
|
+
foreach (var headerRef in templateFinalSectPr.Elements<HeaderReference>())
|
|
214
|
+
{
|
|
215
|
+
var templateHeaderPart = template.GetPartById(headerRef.Id!) as HeaderPart;
|
|
216
|
+
if (templateHeaderPart == null) continue;
|
|
217
|
+
|
|
218
|
+
var newHeaderPart = output.AddNewPart<HeaderPart>();
|
|
219
|
+
using (var stream = templateHeaderPart.GetStream(FileMode.Open, FileAccess.Read))
|
|
220
|
+
{
|
|
221
|
+
newHeaderPart.FeedData(stream);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
CopyPartRelationships(templateHeaderPart, newHeaderPart);
|
|
225
|
+
|
|
226
|
+
var newRefId = output.GetIdOfPart(newHeaderPart);
|
|
227
|
+
outputFinalSectPr.InsertAt(new HeaderReference
|
|
228
|
+
{
|
|
229
|
+
Type = headerRef.Type,
|
|
230
|
+
Id = newRefId
|
|
231
|
+
}, 0);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Copy footers
|
|
235
|
+
foreach (var footerRef in templateFinalSectPr.Elements<FooterReference>())
|
|
236
|
+
{
|
|
237
|
+
var templateFooterPart = template.GetPartById(footerRef.Id!) as FooterPart;
|
|
238
|
+
if (templateFooterPart == null) continue;
|
|
239
|
+
|
|
240
|
+
var newFooterPart = output.AddNewPart<FooterPart>();
|
|
241
|
+
using (var stream = templateFooterPart.GetStream(FileMode.Open, FileAccess.Read))
|
|
242
|
+
{
|
|
243
|
+
newFooterPart.FeedData(stream);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
CopyPartRelationships(templateFooterPart, newFooterPart);
|
|
247
|
+
|
|
248
|
+
var newRefId = output.GetIdOfPart(newFooterPart);
|
|
249
|
+
var lastHeaderRef = outputFinalSectPr.Elements<HeaderReference>().LastOrDefault();
|
|
250
|
+
if (lastHeaderRef != null)
|
|
251
|
+
lastHeaderRef.InsertAfterSelf(new FooterReference { Type = footerRef.Type, Id = newRefId });
|
|
252
|
+
else
|
|
253
|
+
outputFinalSectPr.InsertAt(new FooterReference { Type = footerRef.Type, Id = newRefId }, 0);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/// <summary>
|
|
258
|
+
/// Copies sub-relationships (images, etc.) from a source part to a target part.
|
|
259
|
+
/// </summary>
|
|
260
|
+
private static void CopyPartRelationships(OpenXmlPart source, OpenXmlPart target)
|
|
261
|
+
{
|
|
262
|
+
foreach (var rel in source.ExternalRelationships)
|
|
263
|
+
{
|
|
264
|
+
target.AddExternalRelationship(rel.RelationshipType, rel.Uri, rel.Id);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
foreach (var childPart in source.Parts)
|
|
268
|
+
{
|
|
269
|
+
try
|
|
270
|
+
{
|
|
271
|
+
var contentType = childPart.OpenXmlPart.ContentType;
|
|
272
|
+
if (contentType.StartsWith("image/"))
|
|
273
|
+
{
|
|
274
|
+
var newChild = target.AddNewPart<ImagePart>(contentType, childPart.RelationshipId);
|
|
275
|
+
using var stream = childPart.OpenXmlPart.GetStream(FileMode.Open, FileAccess.Read);
|
|
276
|
+
newChild.FeedData(stream);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (Exception ex)
|
|
280
|
+
{
|
|
281
|
+
Console.Error.WriteLine($"[WARN] Skipped non-image embedded part: {ex.Message}");
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/// <summary>
|
|
287
|
+
/// Copies page size, margins, columns, and document grid from template section properties.
|
|
288
|
+
/// </summary>
|
|
289
|
+
private static void CopySectionProperties(MainDocumentPart template, MainDocumentPart output)
|
|
290
|
+
{
|
|
291
|
+
var templateBody = template.Document?.Body;
|
|
292
|
+
var outputBody = output.Document?.Body;
|
|
293
|
+
if (templateBody == null || outputBody == null) return;
|
|
294
|
+
|
|
295
|
+
var templateSectPr = templateBody.Descendants<SectionProperties>().LastOrDefault();
|
|
296
|
+
if (templateSectPr == null) return;
|
|
297
|
+
|
|
298
|
+
var outputSectPr = outputBody.Descendants<SectionProperties>().LastOrDefault();
|
|
299
|
+
if (outputSectPr == null)
|
|
300
|
+
{
|
|
301
|
+
outputSectPr = new SectionProperties();
|
|
302
|
+
outputBody.Append(outputSectPr);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
CopyChildElement<PageSize>(templateSectPr, outputSectPr);
|
|
306
|
+
CopyChildElement<PageMargin>(templateSectPr, outputSectPr);
|
|
307
|
+
CopyChildElement<Columns>(templateSectPr, outputSectPr);
|
|
308
|
+
CopyChildElement<DocGrid>(templateSectPr, outputSectPr);
|
|
309
|
+
CopyChildElement<PageBorders>(templateSectPr, outputSectPr);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private static void CopyChildElement<T>(SectionProperties source, SectionProperties target) where T : OpenXmlElement
|
|
313
|
+
{
|
|
314
|
+
var sourceElement = source.GetFirstChild<T>();
|
|
315
|
+
if (sourceElement == null) return;
|
|
316
|
+
|
|
317
|
+
var existing = target.GetFirstChild<T>();
|
|
318
|
+
existing?.Remove();
|
|
319
|
+
|
|
320
|
+
target.Append((T)sourceElement.CloneNode(true));
|
|
321
|
+
}
|
|
322
|
+
}
|