Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Insert Documents or Document Templates using Document Assembler #93

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
69704e9
Added inline HTML support in Document Assembler with associated tests…
MalcolmJohnston Nov 11, 2024
4199871
Ran csharpier!
MalcolmJohnston Nov 11, 2024
562f5cd
Fixed behaviour of HtmlConverter to allow returning of multiple eleme…
MalcolmJohnston Nov 11, 2024
e7198e6
Fixed issue with duplicate package references as I had added via nuge…
MalcolmJohnston Nov 11, 2024
676acd7
feat: merge master
sergey-tihon Nov 15, 2024
ae73d4f
fix: no more paket
sergey-tihon Nov 15, 2024
2605f71
Removed HTML Agility Pack.
MalcolmJohnston Nov 19, 2024
9dcc093
Work in progress. Builds!
MalcolmJohnston Nov 19, 2024
33499a5
Updated to use HtmlToWmlConverter type approach.
MalcolmJohnston Nov 26, 2024
929040a
Fixed issue with Document Assembler not copying across paragraph prop…
MalcolmJohnston Jan 2, 2025
f8189d5
Ran csharpier
MalcolmJohnston Jan 2, 2025
cd75d48
Implementation and tests for Document and DocumentTemplate elements.
MalcolmJohnston Jan 9, 2025
330d6a8
Updates after running csharpier.
MalcolmJohnston Jan 10, 2025
ae7afb9
Added Document and DocumentTemplate documentation.
MalcolmJohnston Jan 10, 2025
6ba18d7
Fixed merge conflicts.
MalcolmJohnston Jan 10, 2025
bef6339
Merge branch 'master' into feature/document-and-document-templates
MalcolmJohnston Jan 10, 2025
bf821fe
Reverting merge as I have made a hash of this
MalcolmJohnston Jan 10, 2025
e19d7b1
Fixed mess I made of merge, tests running and passing, csharpier re-run.
MalcolmJohnston Jan 10, 2025
f43d5ce
Merge branch 'master' into feature/document-and-document-templates
sergey-tihon Jan 26, 2025
de031fa
fix: docs
sergey-tihon Feb 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion Clippit.Tests/Word/DocumentAssemblerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using Clippit.Word;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Validation;
using Xunit;
using static System.Runtime.InteropServices.JavaScript.JSType;

Expand Down Expand Up @@ -264,6 +266,52 @@ public void DA259(string name, string data, bool err)
Assert.Equal(6, brCount);
}

[Theory]
[InlineData("DA286-DocumentTemplate-Base-Main.docx", "DA286-DocumentTemplate-Base.xml", false)]
[InlineData(
"DA286-DocumentTemplate-MirroredMargins-Main.docx",
"DA286-DocumentTemplate-MirroredMargins.xml",
false
)]
[InlineData("DA286-DocumentTemplate-NoBreaks-Main.docx", "DA286-DocumentTemplate-NoBreaks.xml", false)]
[InlineData("DA286-DocumentTemplate-HeaderFooter-Main.docx", "DA286-DocumentTemplate-HeaderFooter.xml", false)]
[InlineData("DA286-Document-SolarSystem-Main.docx", "DA286-Document-SolarSystem.xml", false)]
public void DA286(string templateName, string data, bool err)
{
var templateDocx = new FileInfo(Path.Combine(_sourceDir.FullName, templateName));
var dataFile = new FileInfo(Path.Combine(_sourceDir.FullName, data));

var wmlTemplate = new WmlDocument(templateDocx.FullName, true);
var xmldata = XElement.Load(dataFile.FullName);

// set the directory for TemplatePath attributes
var ns = xmldata.GetDefaultNamespace();
foreach (var ele in xmldata.XPathSelectElements("//*[@TemplatePath]"))
{
var templatePath = ele.Attribute(ns + "TemplatePath").Value;
templatePath = Path.Combine(_sourceDir.FullName, templatePath);
ele.Attribute(ns + "TemplatePath").Value = templatePath;
}

// set the directory for Path attributes
foreach (var ele in xmldata.XPathSelectElements("//*[@Path]"))
{
var path = ele.Attribute(ns + "Path").Value;
path = Path.Combine(_sourceDir.FullName, path);
ele.Attribute(ns + "Path").Value = path;
}

var afterAssembling = DocumentAssembler.AssembleDocument(wmlTemplate, xmldata, out bool templateError);
var assembledDocx = new FileInfo(
Path.Combine(TempDir, templateDocx.Name.Replace(".docx", "-processed-by-DocumentAssembler.docx"))
);
afterAssembling.SaveAs(assembledDocx.FullName);

Validate(assembledDocx);

Assert.Equal(err, templateError);
}

[Theory]
[InlineData("DA024-TrackedRevisions.docx", "DA-Data.xml")]
public void DA102_Throws(string name, string data)
Expand All @@ -275,7 +323,7 @@ public void DA102_Throws(string name, string data)
var xmldata = XElement.Load(dataFile.FullName);

WmlDocument afterAssembling;
Assert.Throws<OpenXmlPowerToolsException>(() =>
_ = Assert.Throws<OpenXmlPowerToolsException>(() =>
{
afterAssembling = DocumentAssembler.AssembleDocument(
wmlTemplate,
Expand Down Expand Up @@ -368,6 +416,9 @@ private void Validate(FileInfo fi)
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:noVBand' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:oddHBand' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:oddVBand' attribute is not declared.",
"The 'http://schemas.microsoft.com/office/word/2012/wordml:restartNumberingAfterBreak' attribute is not declared.",
"The 'http://schemas.microsoft.com/office/word/2016/wordml/cid:durableId' attribute is not declared.",
"Attribute 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:val' should have unique value. Its current value",
};
}
}
Expand Down
29 changes: 29 additions & 0 deletions Clippit/Word/Assembler/FileDataExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace Clippit.Word.Assembler
{
internal static class FileDataExtensions
{
internal static XElement GetBase64EncodedDocumentElement(this byte[] bytes)
{
// return a Content Control with byte[] base64 encoded
/*<w:sdt><w:sdtContent>
<w:p w:rsidR="00FB5781" w:rsidRDefault="00AE77E8">
<w:r><w:t>&lt;Document Data="BASE64" /&gt;</w:t></w:r></w:p></w:sdtContent></w:sdt>
*/

var xmlString = $"<Document Data=\"{Convert.ToBase64String(bytes)}\" />";
XElement sdt = new XElement(
W.sdt,
new XElement(W.sdtContent, new XElement(W.p, new XElement(W.r, new XElement(W.t, xmlString))))
);

return sdt;
}
}
}
2 changes: 2 additions & 0 deletions Clippit/Word/Assembler/HtmlConverter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Collections;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using Clippit.Html;
using Clippit.Internal;
using DocumentFormat.OpenXml.Packaging;
Expand Down
17 changes: 14 additions & 3 deletions Clippit/Word/Assembler/PASchemaSet.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
using System.Xml.Schema;
using System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Schema;

namespace Clippit.Word.Assembler
{
internal class PASchemaSet
{
public string XsdMarkup;
public XmlSchemaSet SchemaSet;
private readonly string XsdMarkup;
public readonly XmlSchemaSet SchemaSet;

internal PASchemaSet(string xsdMarkup)
{
this.XsdMarkup = xsdMarkup;
this.SchemaSet = new XmlSchemaSet();

XmlSchema schema = XmlSchema.Read(XmlReader.Create(new StringReader(XsdMarkup)), null);
this.SchemaSet.Add(schema);
}
}
}
11 changes: 11 additions & 0 deletions Clippit/Word/Assembler/RunReplacementInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Xml.Linq;

namespace Clippit.Word.Assembler
{
internal class RunReplacementInfo
{
public XElement Xml { get; set; }
public string XmlExceptionMessage { get; set; }
public string SchemaValidationMessage { get; set; }
}
}
85 changes: 84 additions & 1 deletion Clippit/Word/Assembler/XPathExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections;
using System.Collections;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;

Expand Down Expand Up @@ -58,5 +59,87 @@ internal static string EvaluateXPathToString(this XElement element, string xPath
_ => selectedData.First(),
};
}

internal static bool TryEvalueStringToByteArray(this XElement element, string pathOrXPath, out byte[] bytes)
{
bytes = [];

try
{
var fileInfo = element.EvaluateStringToFileInfo(pathOrXPath);
if (fileInfo != null)
{
using (
var fs = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
)
{
using (var ms = new MemoryStream())
{
fs.CopyTo(ms);
bytes = ms.ToArray();
}
}

return true;
}
}
catch { }

return false;
}

private static FileInfo EvaluateStringToFileInfo(this XElement element, string pathOrXPath)
{
object xPathSelectResult;
try
{
xPathSelectResult = element.XPathEvaluate(pathOrXPath);
if ((xPathSelectResult is IEnumerable) && !(xPathSelectResult is string))
{
var selectedData = ((IEnumerable)xPathSelectResult).Cast<XObject>().SingleOrDefault();
if (selectedData != null)
{
if (selectedData.NodeType == XmlNodeType.Text)
{
XText text = selectedData as XText;
return new FileInfo(text.Value);
}
else if (selectedData.NodeType == XmlNodeType.Attribute)
{
XAttribute att = selectedData as XAttribute;
return new FileInfo(att.Value);
}
else if (selectedData.NodeType == XmlNodeType.Element)
{
// the element should have one child text node
XElement ele = selectedData as XElement;
XText text = ele.Nodes()
.Where(x => x.NodeType == XmlNodeType.Text)
.Select(x => x as XText)
.SingleOrDefault();

if (text != null)
{
return new FileInfo(text.Value);
}
}
}
}
}
catch (XPathException) // suppress the xpath exception
{ }

// check whether the xPath is actually just a file path
try
{
return new FileInfo(pathOrXPath);
}
// supress exceptions that may occur if the path is actually xPath
catch (ArgumentNullException) { }
catch (NotSupportedException) { }
catch (ArgumentException) { }

return null;
}
}
}
Loading
Loading