From 9212e5be6576d5a0293072d894a89a98dcd9e208 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Apr 2026 22:09:07 +0000 Subject: [PATCH 1/2] Initial plan From 8eef8b772b199e787400ee6d4af83c77605fae05 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Apr 2026 22:15:11 +0000 Subject: [PATCH 2/2] Fix README, add AddFileLink/AddSubstitutableString extensions, correct package description Agent-Logs-Url: https://github.com/mathieumack/OpenXMLSDK.Engine/sessions/5af0482c-3884-4250-971f-f8e002e6fdc1 Co-authored-by: mathieumack <12582537+mathieumack@users.noreply.github.com> --- README.md | 231 +++++++++++++++--- .../OpenXMLSDK.Engine.csproj | 2 +- .../ContextModelExtensions.cs | 29 +++ 3 files changed, 231 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 56d35a25..a8a741cd 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # OpenXMLSDK.Engine -This library let you to create quickly some docx documents, based on the ooxml sdk of Microsoft. - -By using the WordManager object, you will be able to geneate quickly your documents. +A .NET library that simplifies generating and manipulating Word (.docx) documents using the OpenXML SDK. Use the `WordManager` object and the built-in Report Engine to produce rich documents from templates or from scratch. ## Quality and packaging @@ -12,46 +10,219 @@ By using the WordManager object, you will be able to geneate quickly your docume [![NuGet package](https://buildstats.info/nuget/OpenXMLSDK.Engine?includePreReleases=true)](https://nuget.org/packages/OpenXMLSDK.Engine) -### API +## API + +The API of `WordManager` is designed to be easy to understand and use. + +### Open an existing template + +Call `OpenDocFromTemplate` to open a `.dotx` template, copy it to a new path and work on the copy: + +```csharp +var templatePath = @"C:\temp\template.dotx"; +var outputPath = @"C:\temp\createdDoc.docx"; + +using (var word = new WordManager()) +{ + word.OpenDocFromTemplate(templatePath, outputPath, isEditable: true); + + // ... make changes ... + + word.SaveDoc(); + word.CloseDoc(); +} +``` + +You can also open a template from a `Stream` (useful in web or cloud scenarios): + +```csharp +using (var templateStream = File.OpenRead(@"C:\temp\template.dotx")) +using (var word = new WordManager()) +{ + word.OpenDocFromTemplate(templateStream); + + // ... make changes ... + + word.SaveDoc(); + var result = word.GetMemoryStream(); // returns a copy of the document as a MemoryStream +} +``` + +### Insert text on a bookmark -The API of WordManager is very easy to understand and to use. +Templates can contain named bookmarks. Use the bookmark extension methods to inject content: -#### WordManager Open existing template +```csharp +using (var word = new WordManager()) +{ + word.OpenDocFromTemplate(templatePath, outputPath, isEditable: true); -In order to open a template, call the OpenDocFromTemplate method -```c# - - var resourceName = ""; // ex : C:\temp\template.dotx - var finalFilePath = ""; // ex : C:\temp\createdDoc.docx - - using (var word = new WordManager()) + // Insert a plain string at a bookmark named "CompanyName" + word.SetTextOnBookmark("CompanyName", "Contoso Ltd."); + + // Insert multiple lines at a bookmark named "AddressLines" + word.SetTextsOnBookmark("AddressLines", new List { - word.OpenDocFromTemplate(resourceName, finalFilePath, true); + "1 Microsoft Way", + "Redmond, WA 98052" + }); - word.SaveDoc(); - word.CloseDoc(); - } - + // Replace a bookmark with HTML content + word.SetHtmlOnBookmark("BodyContent", "

Hello World

"); + + word.SaveDoc(); + word.CloseDoc(); +} ``` -#### WordManager Insert text on bookmark +### Report Engine – generate a document from a model + +The Report Engine lets you define an entire document as a C# object tree, bind it to a `ContextModel` and render it to bytes in one call. + +#### Build a simple document -Using the name of the database and the folder on the client device where to store database files: -```c# - - var resourceName = ""; // ex : C:\temp\template.dotx - var finalFilePath = ""; // ex : C:\temp\createdDoc.docx - - using (var word = new WordManager()) +```csharp +using OpenXMLSDK.Engine.Word; +using OpenXMLSDK.Engine.Word.ReportEngine; +using OpenXMLSDK.Engine.Word.ReportEngine.Models; +using OpenXMLSDK.Engine.ReportEngine.DataContext; +using OpenXMLSDK.Engine.ReportEngine.DataContext.FluentExtensions; +using System.Globalization; + +// 1. Build the document model +var document = new Document(); +var page = new Page(); +document.Pages.Add(page); + +var paragraph = new Paragraph(); +page.ChildElements.Add(paragraph); + +paragraph.ChildElements.Add(new Label { Text = "Hello, #CustomerName#!" }); + +// 2. Build the context (data bindings) +var context = new ContextModel() + .AddString("#CustomerName#", "Alice"); + +// 3. Generate bytes +using var word = new WordManager(); +byte[] docBytes = word.GenerateReport(document, context, CultureInfo.InvariantCulture); +File.WriteAllBytes(@"C:\temp\output.docx", docBytes); +``` + +#### Render a table from a collection + +```csharp +var document = new Document(); +var page = new Page(); +document.Pages.Add(page); + +var table = new Table +{ + DataSourceKey = "#Orders#", + ColsWidth = new[] { 3000, 3000, 2000 }, + HeaderRow = new Row + { + Cells = + { + new Cell { ChildElements = { new Label { Text = "Product", Bold = true } } }, + new Cell { ChildElements = { new Label { Text = "Quantity", Bold = true } } }, + new Cell { ChildElements = { new Label { Text = "Price", Bold = true } } }, + } + }, + RowModel = new Row { - word.OpenDocFromTemplate(resourceName, finalFilePath, true); + Cells = + { + new Cell { ChildElements = { new Label { Text = "#Product#" } } }, + new Cell { ChildElements = { new Label { Text = "#Quantity#" } } }, + new Cell { ChildElements = { new Label { Text = "#Price#" } } }, + } + } +}; +page.ChildElements.Add(table); + +// Build a data-source collection +var context = new ContextModel() + .AddCollection("#Orders#", + new ContextModel().AddString("#Product#", "Widget A").AddString("#Quantity#", "10").AddString("#Price#", "$5.00"), + new ContextModel().AddString("#Product#", "Widget B").AddString("#Quantity#", "3").AddString("#Price#", "$15.00") + ); + +using var word = new WordManager(); +byte[] docBytes = word.GenerateReport(document, context, CultureInfo.InvariantCulture); +``` + +#### Multi-report generation (multiple sections) - word.SaveDoc(); - word.CloseDoc(); +```csharp +var reports = new List +{ + new Report + { + Document = BuildCoverPageDocument(), + ContextModel = BuildCoverPageContext(), + AddPageBreak = true + }, + new Report + { + Document = BuildContentDocument(), + ContextModel = BuildContentContext(), + AddPageBreak = false } - +}; + +using var word = new WordManager(); +byte[] docBytes = word.GenerateReport(reports, mergeStyles: true, CultureInfo.CurrentCulture); ``` +### Append an external document + +Append one or more Word documents at the end of the current document: + +```csharp +using (var word = new WordManager()) +{ + word.OpenDocFromTemplate(templatePath, outputPath, isEditable: true); + + using var extra = File.OpenRead(@"C:\temp\annex.docx"); + word.AppendSubDocument(extra, withPageBreak: true); + + word.SaveDoc(); + word.CloseDoc(); +} +``` + +### Create a new blank document + +```csharp +using (var word = new WordManager()) +{ + word.New(); + + // ... add content programmatically ... + + word.SaveDoc(); + var stream = word.GetMemoryStream(); +} +``` + +### Context fluent extensions reference + +`ContextModel` supports a fluent API for adding typed values: + +| Method | Description | +|---|---| +| `AddString(key, value)` | Plain string | +| `AddDouble(key, value, pattern)` | Formatted double (e.g. `"{0:N2}"`) | +| `AddBoolean(key, value)` | Boolean (controls `Show`, `Bold`, etc.) | +| `AddDateTime(key, value, pattern)` | Formatted date/time | +| `AddByteContent(key, bytes)` | Inline image from a byte array | +| `AddBase64Content(key, base64)` | Inline image from a Base64 string | +| `AddFileLink(key, filePath)` | File reference (image path or any file path used by the report engine) | +| `AddSubstitutableString(key, pattern, dataSource)` | Composite string, e.g. `"{0} of {1}"` | +| `AddCollection(key, contexts...)` | Data source for `ForEach` or table row models | + + # Contribute ## How to contribute diff --git a/src/OpenXMLSDK.Engine/OpenXMLSDK.Engine.csproj b/src/OpenXMLSDK.Engine/OpenXMLSDK.Engine.csproj index 055a3776..2ee12d19 100644 --- a/src/OpenXMLSDK.Engine/OpenXMLSDK.Engine.csproj +++ b/src/OpenXMLSDK.Engine/OpenXMLSDK.Engine.csproj @@ -12,7 +12,7 @@ https://github.com/mathieumack/OpenXMLSDK.Engine https://github.com/mathieumack/OpenXMLSDK.Engine git - This package contains the 'Open-XML-SDK' plugin for MvvmCross. + A .NET library that simplifies generating and manipulating Word (.docx) documents using the OpenXML SDK. Provides a high-level report engine, bookmark utilities, chart support, and fluent extension methods for building documents from templates or from scratch. OpenXMLSDK.Engine OpenXMLSDK.Engine engine report and helpers for the OpenXML SDK diff --git a/src/OpenXMLSDK.Engine/ReportEngine/DataContext/FluentExtensions/ContextModelExtensions.cs b/src/OpenXMLSDK.Engine/ReportEngine/DataContext/FluentExtensions/ContextModelExtensions.cs index be5644c2..ca13cfe8 100644 --- a/src/OpenXMLSDK.Engine/ReportEngine/DataContext/FluentExtensions/ContextModelExtensions.cs +++ b/src/OpenXMLSDK.Engine/ReportEngine/DataContext/FluentExtensions/ContextModelExtensions.cs @@ -91,6 +91,35 @@ public static ContextModel AddBase64Content(this ContextModel context, string ke return context; } + /// + /// Add a file link model (image path or any file reference used by the report engine) + /// + /// + /// + /// Absolute or relative path to the file + /// + public static ContextModel AddFileLink(this ContextModel context, string key, string filePath) + { + var element = new FileLinkModel(filePath); + context.AddItem(key, element); + return context; + } + + /// + /// Add a substitutable string model, allowing composite formatted strings built from other context values + /// + /// + /// + /// Format pattern (e.g. "{0} of {1}") + /// Context whose values are injected into the pattern in order + /// + public static ContextModel AddSubstitutableString(this ContextModel context, string key, string renderPattern, ContextModel dataSource) + { + var element = new SubstitutableStringModel(renderPattern, dataSource); + context.AddItem(key, element); + return context; + } + /// /// Add a list of elements as a DataSource ///