Skip to content

Commit

Permalink
update RemoveControlsAndKeepContent to support nested table rows
Browse files Browse the repository at this point in the history
  • Loading branch information
smartphonedesign committed Apr 22, 2024
1 parent 094cb44 commit c19afa2
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 13 deletions.
59 changes: 46 additions & 13 deletions OpenXMLTemplates/Documents/TemplateDocument.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using OpenXMLTemplates.Utils;
Expand Down Expand Up @@ -113,25 +114,57 @@ internal void AddControl(ContentControl control, bool isFirstOrder)

public void RemoveControlsAndKeepContent()
{
foreach (var control in allContentControls)
{
var sdtElement = control.SdtElement;

var contentElement = sdtElement.Descendants()
.FirstOrDefault(d => d is SdtContentBlock || d is SdtContentRun);
if (contentElement != null)
foreach (var contentElementChildElement in contentElement.ChildElements.ToList())
{
contentElementChildElement.Remove();
sdtElement.InsertBeforeSelf(contentElementChildElement);
}
var contentControls = WordprocessingDocument.MainDocumentPart.Document.Descendants<SdtElement>().ToList();

sdtElement.Remove();
// Process each content control from innermost to outermost
foreach (var sdt in contentControls.OrderByDescending(s => s.Ancestors<SdtElement>().Count()))
{
if (sdt is SdtBlock sdtBlock)
{
HandleSdtContent(sdtBlock, sdtBlock.SdtContentBlock);
}
else if (sdt is SdtRun sdtRun)
{
HandleSdtContent(sdtRun, sdtRun.SdtContentRun);
}
else if (sdt is SdtRow sdtRow)
{
HandleSdtRow(sdtRow);
}
}

allContentControls.Clear();
innerContentControls.Clear();
firstOrderContentControls.Clear();
}

private static void HandleSdtContent(SdtElement sdt, OpenXmlElement content)
{
if (content != null)
{
var parent = sdt.Parent;
var elementsToMove = content.ChildElements.ToArray(); // Make a copy to avoid modifying the collection during iteration
foreach (var elem in elementsToMove)
{
parent.InsertBefore(elem.CloneNode(true), sdt);
}
// Remove the content control itself
sdt.Remove();
}
}
private static void HandleSdtRow(SdtRow sdtRow)
{
if (sdtRow.SdtContentRow != null)
{
var tableRow = sdtRow.SdtContentRow.GetFirstChild<TableRow>();
if (tableRow != null)
{
var parent = sdtRow.Parent; // This should be the Table
parent.InsertBefore(tableRow.CloneNode(true), sdtRow);
sdtRow.Remove();
}
}
}

}
}
Binary file added OpenXMLTemplatesTest/ControlRemovalTest/Doc.docx
Binary file not shown.
57 changes: 57 additions & 0 deletions OpenXMLTemplatesTest/ControlRemovalTest/Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System.IO;
using System.Linq;
using NUnit.Framework;
using OpenXMLTemplates;
using OpenXMLTemplates.ControlReplacers;
using OpenXMLTemplates.Documents;
using OpenXMLTemplates.Engine;
using OpenXMLTemplates.Variables;
namespace OpenXMLTempaltesTest.ControlRemovalTest
{
public class Tests
{
private TemplateDocument GetDoc => new TemplateDocument(this.CurrentFolder() + "Doc.docx");
private string GetData => File.ReadAllText(this.CurrentFolder() + "data.json");

[Test]
public void TestControlRemoval()
{
using var doc = GetDoc;
var data = GetData;

var src = new VariableSource();
src.LoadDataFromJson(data);

var engine = new DefaultOpenXmlTemplateEngine
{
KeepContentControlAfterReplacement = false
};
engine.ReplaceAll(doc, src);

doc.SaveAs(this.CurrentFolder() + "result.docx");

// confirm new content has been included in document
string? docText = null;

Check warning on line 34 in OpenXMLTemplatesTest/ControlRemovalTest/Tests.cs

View workflow job for this annotation

GitHub Actions / Build and test

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 34 in OpenXMLTemplatesTest/ControlRemovalTest/Tests.cs

View workflow job for this annotation

GitHub Actions / Build and test

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

using (StreamReader sr = new StreamReader(doc.WordprocessingDocument.MainDocumentPart.GetStream()))
{
docText = sr.ReadToEnd();
}

Assert.IsTrue(docText != null);
var replacedText = src.GetVariable<string>("nested.[1].nestedList.[1]");
Assert.IsTrue(docText.Contains(replacedText));

// confirm controls have been removed
Assert.AreEqual(0,
doc.WordprocessingDocument.ContentControls().Count(cc =>
cc.GetContentControlTag() != null && cc.GetContentControlTag().StartsWith("repeatingitem")));

Assert.AreEqual(0,
doc.WordprocessingDocument.ContentControls().Count(cc =>
cc.GetContentControlTag() != null && cc.GetContentControlTag() == "repeating_nestedList"));

doc.WordprocessingDocument.AssertValid();
}
}
}
35 changes: 35 additions & 0 deletions OpenXMLTemplatesTest/ControlRemovalTest/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"items": [
"Item 1",
"Item 2",
"Item 3",
"Item 4"
],
"complexItems": [
{
"name": "First Name",
"address": "First Address"
},
{
"name": "Second name",
"address": "Second address"
}
],
"nested": [
{
"name": "List 1",
"nestedList": [
"item1-1",
"item1-2",
"item1-3"
]
},
{
"name": "List 2",
"nestedList": [
"item2-1",
"item2-2"
]
}
]
}
Binary file added OpenXMLTemplatesTest/ControlRemovalTest/~$Doc.docx
Binary file not shown.
6 changes: 6 additions & 0 deletions OpenXMLTemplatesTest/OpenXMLTemplatesTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
<ProjectReference Include="..\OpenXMLTemplates\OpenXMLTemplates.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="ControlRemovalTest\data.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="ControlRemovalTest\Doc.docx">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="ControlReplacersTests\PictureControlReplacerTests\data.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down

0 comments on commit c19afa2

Please sign in to comment.