Skip to content

Commit

Permalink
Merge pull request #27 from Amberg/workitems/bug_fix
Browse files Browse the repository at this point in the history
Bookmark bug fix
  • Loading branch information
Amberg authored Nov 7, 2024
2 parents 1fd50f7 + aa0dfa1 commit 2899d75
Show file tree
Hide file tree
Showing 11 changed files with 322 additions and 133 deletions.
2 changes: 1 addition & 1 deletion DocxTemplater.Images/DocxTemplater.Images.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
<PackageReference Include="SixLabors.ImageSharp" Version="[3.1.5,4.0.0)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DocxTemplater\DocxTemplater.csproj" />
Expand Down
2 changes: 1 addition & 1 deletion DocxTemplater.Markdown/DocxTemplater.Markdown.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Markdig" Version="0.37.0" />
<PackageReference Include="Markdig" Version="[0.37.0,1.0.0)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DocxTemplater\DocxTemplater.csproj" />
Expand Down
55 changes: 55 additions & 0 deletions DocxTemplater.Test/BookmarkInDifferentTableRowDoesNotcauseCrash.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

namespace DocxTemplater.Test
{
internal class BookmarkInDifferentTableRowDoesNotcauseCrash
{
[Test]
public void Test()
{
string content = @"<w:p xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
<w:r>
<w:t xml:space=""preserve"">This is sentence one.</w:t>
</w:r>
<w:bookmarkStart w:id=""0"" w:name=""testing123""/>
<w:r>
<w:t>This is sentence two. {{.}}</w:t>
</w:r>
</w:p>
<w:p xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
<w:r>
<w:t xml:space=""preserve"">This </w:t>
</w:r>
<w:bookmarkEnd w:id=""0""/>
<w:r>
<w:t>is sentence three.{{.}}</w:t>
</w:r>
</w:p>";

using var memStream = new MemoryStream();
using var wpDocument = WordprocessingDocument.Create(memStream, WordprocessingDocumentType.Document);
var mainPart = wpDocument.AddMainDocumentPart();
mainPart.Document = new Document
{
Body = new Body
{
InnerXml = content
}
};
wpDocument.Save();
memStream.Position = 0;
var docTemplate = new DocxTemplate(memStream);
docTemplate.BindModel("ds", "hi there");
var result = docTemplate.Process();
docTemplate.Validate();

// get body
var document = WordprocessingDocument.Open(result, false);
var body = document.MainDocumentPart.Document.Body;
Assert.That(body.Descendants<BookmarkEnd>().Count(), Is.EqualTo(0));
Assert.That(body.Descendants<BookmarkStart>().Count(), Is.EqualTo(0));
}
}
}
3 changes: 2 additions & 1 deletion DocxTemplater.Test/DocxTemplater.Test.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>12.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
Expand Down
3 changes: 3 additions & 0 deletions DocxTemplater.Test/PatternMatcherTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ static IEnumerable TestPatternMatch_Cases()
yield return new TestCaseData("{{var}:format(a,b)}").Returns(new[] { PatternType.Variable }).SetName("Multiple Arguments");
yield return new TestCaseData("{{/}}").Returns(new[] { PatternType.ConditionEnd });
yield return new TestCaseData("{ { / } }").Returns(new[] { PatternType.ConditionEnd });
yield return new TestCaseData("{?{ds.QrBills.idx == 2}}").Returns(new[] { PatternType.Condition });
yield return new TestCaseData("{?{ds.QrBills._Idx == 2}}").Returns(new[] { PatternType.Condition }).SetName("underscore in variable name");
yield return new TestCaseData("{?{ds.QrBills._Idx % 2 == 0}}").Returns(new[] { PatternType.Condition }).SetName("modulo in condition");
yield return new TestCaseData(
"NumericValue is greater than 0 - {{ds.Items.InnerCollection.InnerValue}:toupper()}{{else}}" +
"I'm here if if this is not the case{{/}}{{/ds.Items.InnerCollection}}{{/Items}}")
Expand Down
63 changes: 63 additions & 0 deletions DocxTemplater.Test/SpecialCollectionVariableTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;

namespace DocxTemplater.Test
{
internal class SpecialCollectionVariableTest
{
[Test]
public void TestIndexVariableInLoop()
{
var model = new[] { "Item1", "Item2", "Item3", "Item4" };
var template = "Items:{{#Items}}{{Items._Idx}}{{.}} {{/Items}}";

using var memStream = new MemoryStream();
using var wpDocument = WordprocessingDocument.Create(memStream, WordprocessingDocumentType.Document);
MainDocumentPart mainPart = wpDocument.AddMainDocumentPart();
mainPart.Document = new Document(new Body(new Paragraph(new Run(new Text(template)))));
wpDocument.Save();
memStream.Position = 0;
var docTemplate = new DocxTemplate(memStream);
docTemplate.BindModel("Items", model);
var result = docTemplate.Process();
result.Position = 0;
// compare body
result.Position = 0;
var document = WordprocessingDocument.Open(result, false);
var body = document.MainDocumentPart.Document.Body;
Assert.That(body.InnerXml, Is.EqualTo("<w:p xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\">" +
"<w:r>" +
"<w:t xml:space=\"preserve\">Items:</w:t><w:t xml:space=\"preserve\">1</w:t><w:t xml:space=\"preserve\">Item1</w:t>" +
"<w:t xml:space=\"preserve\"> </w:t><w:t xml:space=\"preserve\">2</w:t><w:t xml:space=\"preserve\">Item2</w:t>" +
"<w:t xml:space=\"preserve\"> </w:t><w:t xml:space=\"preserve\">3</w:t><w:t xml:space=\"preserve\">Item3</w:t>" +
"<w:t xml:space=\"preserve\"> </w:t><w:t xml:space=\"preserve\">4</w:t><w:t xml:space=\"preserve\">Item4</w:t>" +
"<w:t xml:space=\"preserve\"> </w:t></w:r></w:p>"));
}

[Test]
public void TestConditionWithIndexVariableInLoop()
{
var model = new[] { "Item1", "Item2", "Item3", "Item4" };
var template = "Items:{{#Items}}{?{Items._Idx % 2 == 0}}{{.}}{{/}}{{/Items}}";

using var memStream = new MemoryStream();
using var wpDocument = WordprocessingDocument.Create(memStream, WordprocessingDocumentType.Document);
MainDocumentPart mainPart = wpDocument.AddMainDocumentPart();
mainPart.Document = new Document(new Body(new Paragraph(new Run(new Text(template)))));
wpDocument.Save();
memStream.Position = 0;
var docTemplate = new DocxTemplate(memStream);
docTemplate.BindModel("Items", model);
var result = docTemplate.Process();
result.Position = 0;
// compare body
result.Position = 0;
var document = WordprocessingDocument.Open(result, false);
var body = document.MainDocumentPart.Document.Body;
Assert.That(body.InnerXml, Is.EqualTo("<w:p xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\"><w:r><w:t xml:space=\"preserve\">Items:" +
"</w:t><w:t xml:space=\"preserve\">Item2</w:t>" +
"<w:t xml:space=\"preserve\">Item4</w:t></w:r></w:p>"));
}
}
}
4 changes: 2 additions & 2 deletions DocxTemplater/DocxTemplater.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="3.1.0" />
<PackageReference Include="DynamicExpresso.Core" Version="2.16.1" />
<PackageReference Include="DocumentFormat.OpenXml" Version="[3.1.0,4.0.0)" />
<PackageReference Include="DynamicExpresso.Core" Version="[2.16.1,3.0.0)" />
<PackageReference Include="GitVersion.MsBuild" Version="5.12.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
8 changes: 7 additions & 1 deletion DocxTemplater/OpenXmlHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public static OpenXmlElement ElementBeforeInDocument<TElement>(this OpenXmlEleme
return null;
}

public static OpenXmlElement ElementAfterInDocument<TElement>(this OpenXmlElement element)
public static TElement ElementAfterInDocument<TElement>(this OpenXmlElement element)
where TElement : OpenXmlElement
{
var parent = element.Parent;
Expand Down Expand Up @@ -509,6 +509,11 @@ public static int CmToEmu(int centimeter)
return centimeter * 360000;
}

public static int MmToEmu(int millimeter)
{
return millimeter * 36000;
}

public static int PixelsToEmu(int pixels)
{
return pixels * 9525;
Expand All @@ -525,6 +530,7 @@ public static int LengthToEmu(int value, string unit)
return unit switch
{
"cm" => CmToEmu(value),
"mm" => MmToEmu(value),
"px" => PixelsToEmu(value),
"in" => InchToEmu(value),
_ => throw new ArgumentException("Unsupported unit: " + unit)
Expand Down
2 changes: 1 addition & 1 deletion DocxTemplater/PatterMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal static class PatternMatcher
(?<separator>:\s*s\s*:) |
(?<else>(?:else)|:) |
(?(condMarker) # if condition marker is set, we expect a condition
(?<condition>[a-zA-Z0-9+\-*\/><=\s\.\!&\|]+)? #condition name (without brackets)
(?<condition>[a-zA-Z0-9+\-*\/><=\s\.\!&\|_%]+)? #condition expression (without brackets)
|
(?:
(?<prefix>[\/\#])?(?<varname>[a-zA-Z0-9\._]+)? #variable name
Expand Down
16 changes: 10 additions & 6 deletions DocxTemplater/TemplateProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,20 @@ private static void Cleanup(OpenXmlCompositeElement element, bool removeEmptyEle
}
}

// make all Bookmark ids unique
uint id = 0;
foreach (var bookmarkStart in element.Descendants<BookmarkStart>())
// remove all bookmarks -> not useful for generated documents and complex to handle
// because of special cases in tables see
// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.bookmarkstart?view=openxml-3.0.1#remarks
foreach (var bookmark in element.Descendants<BookmarkStart>().ToList())
{
bookmarkStart.Id = $"{id++}";
bookmarkStart.NextSibling<BookmarkEnd>().Id = bookmarkStart.Id;
bookmark.RemoveWithEmptyParent();
}
foreach (var bookmark in element.Descendants<BookmarkEnd>().ToList())
{
bookmark.RemoveWithEmptyParent();
}

// make dock properties ids unique
id = 1;
uint id = 1;
var dockProperties = element.Descendants<DocProperties>().ToList();
var existingIds = new HashSet<uint>(dockProperties.Select(x => x.Id.Value).ToList());
foreach (var docPropertiesWithSameId in dockProperties.GroupBy(x => x.Id).Where(x => x.Count() > 1))
Expand Down
Loading

0 comments on commit 2899d75

Please sign in to comment.