Pacem.Extensions.OfficeWord
0.8.11
See the version list below for details.
dotnet add package Pacem.Extensions.OfficeWord --version 0.8.11
NuGet\Install-Package Pacem.Extensions.OfficeWord -Version 0.8.11
<PackageReference Include="Pacem.Extensions.OfficeWord" Version="0.8.11" />
<PackageVersion Include="Pacem.Extensions.OfficeWord" Version="0.8.11" />
<PackageReference Include="Pacem.Extensions.OfficeWord" />
paket add Pacem.Extensions.OfficeWord --version 0.8.11
#r "nuget: Pacem.Extensions.OfficeWord, 0.8.11"
#:package Pacem.Extensions.OfficeWord@0.8.11
#addin nuget:?package=Pacem.Extensions.OfficeWord&version=0.8.11
#tool nuget:?package=Pacem.Extensions.OfficeWord&version=0.8.11
Office Word Extensions
About
Pacem.Extensions.OfficeWord
provides a lightweight set
of utilities that ease the handling of .docx (OpenXml) documents.
The library is mainly about retrieving and replacing content inside a Word document.
The convenient part is that content gets found despite being messily positioned inside the (Open)Xml nodes of the document itself.
The following paragraphs exemplify the library utilization given common use cases.
Getting started
Inject the IOfficeWordProcessor
singleton service into the DI container.
Currently, there's no other [than the dependency injection] way to reach the instance of the processor service.
Like this:
// Program.cs
services.AddPacemOfficeWordProcessor();
Then use it in your services, like this:
namespace Pacem.OpenXml.OfficeWord;
public class MyController : ControllerBase
{
private readonly IOfficeWordProcessor _word;
public MyController(IOfficeWordProcessor word)
{
_word = word;
}
// [...]
}
Placeholder replacement
Straight match and replace
Goal: find the occurrences of a given string and replace them with a precise value.
Write a placeholder string (e.g. [_my_placeholder_]
) in your document file
(e.g. file1.docx) and replace it programmatically with a new string (e.g. "Hello, World!"
).
string root = AppContext.BaseDirectory;
string source = Path.Combine(root, "file1.docx");
string target = Path.Combine(root, "file1-replaced.docx");
_word.Open(source)
.Find("[_my_placeholder_]")
.ReplaceWith("Hello, World!")
.Flush(target);
Conditional replacement
Goal: find consistently formatted placeholders and replace them 1-by-1.
You can gather all the occurrences of your document that obey to a
regular expression and then apply a replacement given the actual match value, like
in the following example.
string root = AppContext.BaseDirectory;
string source = Path.Combine(root, "file2.docx");
string target = Path.Combine(root, "file2-replaced.docx");
_word.Open(source)
// placeholders are strings like "{_content_}", "{_stuff_}", "{_something_}", etc.
.Find(new Regex((@"\{_[\w]+_\}"))
.ReplaceWith(match => match switch
{
"{_Heading1_}" => "Replaced Heading",
"{_Body1_}" => "Replaced Body",
"{_Footer1_}" => "Replaced Footer",
_ => match // ...else return the input match itself.
})
.Flush(target);
Table databinding
Goal: fill a table with data.
Prepare a table in your Word document with 2 rows:
- first row is the heading row,
- last row is the templated row.
Remarks: You can include none or multiple heading rows, as well as multiple foot rows.
Example:
Prop 1 | Prop 2 | Prop 3 |
---|---|---|
[_prop1_] | [_prop2_] | Property3 |
Datasource for the Word table can be shaped using a set of objects whose type has proper metadata set. Like this:
class Poco {
[Placeholder("[_prop1_]", /* format */"Price {0:C4}")]
public double Property1 { get; set; }
[Placeholder("[_prop2_]")]
public string Property2 { get; set; }
[IgnorePlaceholder()]
public string Property3 { get; set; }
}
When
PlaceholderAttribute
is not provided in association with a property, then the property name itself will be used as a placeholder.
string root = AppContext.BaseDirectory;
string source = Path.Combine(root, "file2.docx");
string target = Path.Combine(root, "file2-databound.docx");
_word.Open(source)
.Tables()
// the datasource keys define the tables to be databound,
// based on placeholders' matching!
.DataBind(
new CultureInfo("it-IT"),
new Poco2 { Property1 = 1D, Property2 = "1st item's property 2", /* ignored */Property3 = "1st item's property 3" },
new Poco2 { Property1 = 2D, Property2 = "2nd item's property 2", /* ignored */Property3 = "2nd item's property 3" },
new Poco2 { Property1 = 3D, Property2 = "3rd item's property 2", /* ignored */Property3 = "3rd item's property 3" }
)
.Flush(target);
Result:
Prop 1 | Prop 2 | Prop 3 |
---|---|---|
Price 1,000 € | 1st item's property 2 | Property3 |
Price 2,000 € | 2nd item's property 2 | Property3 |
Price 3,000 € | 3rd item's property 2 | Property3 |
Picture replacement
Goal: replace an existing image with another.
Prepare a document having named pictures.
To name a picture use Office Word Selection Pane and type a label (e.g.
MyPic
).
string root = AppContext.BaseDirectory;
string pic = Path.Combine(root, "pic3.jpg");
string source = Path.Combine(root, "file3.docx");
string target = Path.Combine(root, "file3-replaced.docx");
// load the picture file
using var reader = File.OpenRead(pic);
byte[] data = new byte[reader.Length];
reader.Read(data, 0, data.Length);
// define the desired picture proportion, usually by providing its dimensions
// (new picture will be adapted and 'contained' into the original box)
var proportion = Extent.FromSize(1368,642);
_word.Open(source)
.Pictures("MyPic")
.ReplaceWith(data, KnownMimeTypes.ImageJpeg, proportion)
.Flush(target);
Picture enumeration
Goal: enumerate (and extract data from) the pictures embedded into the document.
string root = AppContext.BaseDirectory;
string source = Path.Combine(root, "file4.docx");
using (var docx = _word.Open(source))
{
docx.Pictures(/* either include or not a selection filter */)
.Read(picture =>
{
// `picture` is an instance of
// PictureMatch which gathers
// useful - readonly - properties
// about the whole picture object
Console.WriteLine($"Picture {picture.Id} found at page {picture.PageIndex+1}.");
});
// remember to explicitly close
// the document when it is not
// meant to be flushed, eventually.
}
Picture removal
Goal: remove all the pictures that match provided criteria.
string root = AppContext.BaseDirectory;
string source = Path.Combine(root, "file5.docx");
string target = Path.Combine(root, "file5-replaced.docx");
string selectionFilter = "MyPics";
_word.Open(source)
.Pictures(selectionFilter)
.Remove(picture =>
{
var pos = picture.Position;
if (pos is null)
{
return false;
}
// remove only floating images
// whose anchor is the `page`
return pos.Value.Y.RelativeFrom == RelativeFrom.Page
&& pos.Value.X.RelativeFrom == RelativeFrom.Page;
})
.Flush(target);
Page cloning
🆕 v0.8.10
Goal: use a single page or a range of pages as a template and clone it n-times while manipulating content at every step.
string root = AppContext.BaseDirectory;
string source = Path.Combine(root, "file6.docx");
string target = Path.Combine(root, "file6-replaced.docx");
string selectionFilter = "MyPics";
// number of copies of the selected page range to create
int copies = 2;
_word.Open(source)
// select pages (last one only in this specific case)
.Pages(pageCount =>
// picks last two pages
(pageCount - 2)..(pageCount - 1)
)
// clone 'em/it (twice in this case)
.Clone(copies, page =>
{
// ranges from 0 to copies-1
int iteration = page.CloneIndex;
// source/template page index
int originalPageIndex = page.PageIndex;
// external convenient calls
var (picturePayload, extent) = BuildPicture(originalPageIndex, iteration);
var datasource = BuildDatasource(originalPageIndex, iteration);
// page actions use the same
// syntax of the above document actions
page.
// text replacement
.Find("[_Placeholder_]")
.ReplaceWith("Iteration #"+ iteration.ToString())
// picture replacement
.Pictures("MyPic")
.ReplaceWith(picturePayload, "image/jpeg", extent)
// table databinding
.Tables()
.DataBind(datasource);
})
.Flush(target);
Please notice that the range of pages used as a template will be removed, eventually.
Section changes
This current version does not handle section (cfr.
<w:sectPr>
) changes.
Section changes handling are planned in future releases.
Breaking Changes from previous versions
- (
0.7.2
) RefactoredPictureMatch
class:ReferenceId
corresponds to the previousId
(tracks the "embed id");Id
gets the unique id of the object inside the document.
- (
0.7.1
) Deprecating table databinding using arrays of string dictionaries. - (
0.7.0-cumae
) Replacing pictures needs to provide relevant mime-type.
Bonus tip: Managed/supported mime-types are exposed by
KnownMimeTypes
.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Pacem.Extensions.OfficeWord:
Package | Downloads |
---|---|
Pacem.Extensions.OfficeWord.Pdf
Utilities to convert .docx files into PDFs. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
0.9.0-dirac | 67 | 8/2/2025 |
0.9.0-cayley | 117 | 7/31/2025 |
0.9.0-boyle | 114 | 7/31/2025 |
0.9.0-abel | 138 | 7/17/2025 |
0.8.11 | 165 | 5/27/2025 |
0.8.10 | 166 | 5/7/2025 |
0.8.10-fourier | 103 | 5/2/2025 |
0.8.10-fibonacci | 112 | 2/7/2025 |
0.8.10-fermat | 102 | 2/3/2025 |
0.8.10-euler | 109 | 2/3/2025 |
0.8.10-euclid | 113 | 2/1/2025 |
0.8.10-dalembert | 114 | 1/31/2025 |
0.8.10-cauchy | 102 | 1/26/2025 |
0.8.10-cantor | 121 | 12/18/2024 |
0.8.10-caccioppoli | 108 | 12/6/2024 |
0.8.10-bourbaki | 105 | 12/6/2024 |
0.8.10-abel | 195 | 4/22/2024 |
0.8.6 | 194 | 3/17/2024 |
0.8.6-abel | 116 | 3/14/2024 |
0.8.5 | 165 | 3/13/2024 |
0.8.4 | 147 | 3/12/2024 |
0.8.3 | 145 | 3/12/2024 |
0.8.2 | 164 | 3/11/2024 |
0.8.1 | 152 | 3/11/2024 |
0.8.0 | 155 | 2/4/2024 |
0.8.0-akkad | 204 | 12/8/2023 |
0.7.4 | 497 | 11/20/2023 |
0.7.3 | 159 | 11/17/2023 |
0.7.2 | 172 | 11/16/2023 |
0.7.2-atlantis | 114 | 11/16/2023 |
0.7.1 | 153 | 11/15/2023 |
0.7.0-cumae | 187 | 8/26/2023 |
0.7.0-corcyra | 166 | 8/17/2023 |
0.7.0-byzantium | 172 | 8/16/2023 |
0.7.0-bithynia | 172 | 8/4/2023 |