Ank.DocToolkit
0.3.7
See the version list below for details.
dotnet add package Ank.DocToolkit --version 0.3.7
NuGet\Install-Package Ank.DocToolkit -Version 0.3.7
<PackageReference Include="Ank.DocToolkit" Version="0.3.7" />
<PackageVersion Include="Ank.DocToolkit" Version="0.3.7" />
<PackageReference Include="Ank.DocToolkit" />
paket add Ank.DocToolkit --version 0.3.7
#r "nuget: Ank.DocToolkit, 0.3.7"
#:package Ank.DocToolkit@0.3.7
#addin nuget:?package=Ank.DocToolkit&version=0.3.7
#tool nuget:?package=Ank.DocToolkit&version=0.3.7
DocToolkit
Convert HTML to DOCX and PDF, and open/edit DOCX, XLSX and PPTX from .NET.
Pure managed. No native binaries, no browser, no LibreOffice, no Office interop.
Works after dotnet restore alone, and runs on Linux.
Offline by default — safe in air-gapped environments
No method on DocToolkit's public API opens a network connection. Not for images, not for stylesheets, not for fonts, not for linked pictures or external workbook references. Once the package is restored, DocToolkit never needs the network again.
There is exactly one way to change that, and you have to ask for it by name:
// The ONLY API that makes an outbound request. It downloads and embeds the images the markup
// names, so it FAILS in an air-gapped environment - a host that will not answer fails the whole
// conversion, after a connect timeout. Leave it alone unless your machines have internet access.
byte[] docx = await HtmlToDocxConverter.ConvertAsync(html, allowRemoteImageDownload: true, ct);
byte[] pdf = await HtmlToPdfConverter.ConvertAsync(html, allowRemoteImageDownload: true, ct);
Everything else — ConvertAsync(html), ConvertToFileAsync, DocxToPdfConverter, DocxEditor,
WorkbookEditor, PresentationEditor — is offline, unconditionally.
This is enforced, not merely intended. The test suite starts a real TCP listener on loopback,
feeds every public API markup that names it as an <img src>, a <link rel="stylesheet">, a CSS
@import, a background-image, an <a href>, an externally linked DOCX picture, an external
XLSX workbook link and more, and requires the accepted-connection count to be exactly zero. A
companion test points the same APIs at an unroutable address (TEST-NET-3) and requires them to
return promptly rather than stall on a connect timeout.
dotnet restore is the one step that still needs a package feed. THIRD-PARTY-NOTICES.txt lists
the full dependency closure with resolved versions, so it can be mirrored onto an internal feed;
every entry is a plain managed assembly with no native payload and no post-restore download.
Install
dotnet add package Ank.DocToolkit
Targets net8.0 and net10.0.
Usage
using DocToolkit;
// HTML -> DOCX
byte[] docx = await HtmlToDocxConverter.ConvertAsync("<h1>Invoice</h1><p>Total: 18,100.00</p>");
// HTML -> PDF (pivots through DOCX internally)
byte[] pdf = await HtmlToPdfConverter.ConvertAsync("<h1>Invoice</h1><p>Total: 18,100.00</p>");
// DOCX -> PDF
byte[] rendered = DocxToPdfConverter.Convert(docx);
// Fill a DOCX template - body, headers, footers, footnotes, endnotes and text boxes
byte[] filled = DocxEditor.ReplaceText(docx, new Dictionary<string, string>
{
["{{customer}}"] = "Contoso Ltd",
});
string text = DocxEditor.ExtractText(filled); // body only
string all = DocxEditor.ExtractText(filled, includeHeadersAndFooters: true);
// Spreadsheets
byte[] xlsx = WorkbookEditor.Create("Sales", new[]
{
new object?[] { "Region", "Total" },
new object?[] { "North", 1200 },
});
string cell = WorkbookEditor.ReadCell(xlsx, "Sales", "B2");
byte[] updated = WorkbookEditor.SetCell(xlsx, "Sales", "B2", 1500);
// Presentations
byte[] pptx = File.ReadAllBytes("deck.pptx");
int slides = PresentationEditor.SlideCount(pptx);
IReadOnlyList<string> slideText = PresentationEditor.ExtractText(pptx); // in deck order
byte[] editedPptx = PresentationEditor.ReplaceText(pptx, new Dictionary<string, string>
{
["{{title}}"] = "Q3 Results",
});
Placeholder replacement
DocxEditor.ReplaceText and PresentationEditor.ReplaceText substitute against the
concatenated text of each paragraph, because Word and PowerPoint routinely split a single
visible word across several runs — a per-run string.Replace would miss {{name}} whenever
it straddles a boundary.
The result is spliced back into only the runs a match actually overlaps, so:
- runs outside a match keep their text and their formatting;
- hyperlinks and text boxes are left alone unless they contain a placeholder themselves;
- when a placeholder does straddle runs, the value lands in the run holding its first character and inherits that run's formatting.
Keys are matched in one left-to-right pass, longest key first at any given offset, so a substituted value is never rescanned for further placeholders.
How the no-network guarantee is built
HtmlToOpenXml, the HTML parser underneath, defaults to downloading every image it sees, and its
resource loader also speaks file:// — so left alone it would give every caller of a
byte[]-in/byte[]-out API an SSRF reach, a read of the host's disk, and an unbounded hang.
DocToolkit shuts that off in two independent places on the default path:
- Image processing is set to
EmbedDataUriOnly. Onlydata:URI images are embedded;http,httpsandfilesources are skipped. - The resource loader is replaced with one that supports no protocol and fetches nothing.
The component capable of making a request is never constructed, so the guarantee does not rest
on what a future release decides
EmbedDataUriOnlymeans. It also keeps the default path away from HtmlToOpenXml 3.5.0's process-wide staticHttpClient, which is not thread-safe.
Self-contained documents still convert in full: data: URI images are decoded by the parser and
never go through the loader.
The other converters and editors need no such handling — DocumentFormat.OpenXml, ClosedXML
and OfficeIMO do not resolve external relationships, external workbook links or remote fonts.
That is asserted, not assumed; see above.
Errors
Every public method reports failure as DocumentConversionException, with the underlying
library exception as InnerException. Bad arguments (null, empty, blank) still surface as
ArgumentNullException/ArgumentException, and a cancelled CancellationToken as
OperationCanceledException.
Why HTML to PDF goes through DOCX
No permissively-licensed, NuGet-only library renders HTML to PDF on Linux: the only free renderers are browsers, and a browser is a native binary. Pivoting through DOCX keeps the whole chain pure managed.
Licence
MIT. See THIRD-PARTY-NOTICES.txt for dependency attribution — in particular the pinned
SixLabors.Fonts 1.0.0, which is the last Apache-2.0 release of that package.
| 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 is compatible. 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. |
-
net10.0
- ClosedXML (>= 0.105.1)
- DocumentFormat.OpenXml (>= 3.5.1)
- HtmlToOpenXml.dll (>= 3.5.0)
- OfficeIMO.Word.Pdf (>= 3.0.3)
- SixLabors.Fonts (= 1.0.1)
-
net8.0
- ClosedXML (>= 0.105.1)
- DocumentFormat.OpenXml (>= 3.5.1)
- HtmlToOpenXml.dll (>= 3.5.0)
- OfficeIMO.Word.Pdf (>= 3.0.3)
- SixLabors.Fonts (= 1.0.1)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Ank.DocToolkit:
| Package | Downloads |
|---|---|
|
Ank.DocToolkit.Extensions.DependencyInjection
Dependency-injection registration for Ank.DocToolkit. services.AddDocToolkit() registers six injectable interfaces (IHtmlToDocxConverter, IDocxToPdfConverter, IHtmlToPdfConverter, IDocxEditor, IWorkbookEditor, IPresentationEditor) over the same pure-managed HTML/DOCX/ PDF/XLSX/PPTX conversion and editing logic, for ASP.NET Core and worker-service consumers. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.9.0 | 0 | 8/6/2026 |
| 0.8.0 | 0 | 8/6/2026 |
| 0.7.0 | 128 | 8/5/2026 |
| 0.6.0 | 88 | 8/4/2026 |
| 0.5.0 | 126 | 8/3/2026 |
| 0.4.0 | 112 | 8/3/2026 |
| 0.3.12 | 97 | 8/3/2026 |
| 0.3.11 | 84 | 8/3/2026 |
| 0.3.10 | 63 | 8/3/2026 |
| 0.3.9 | 89 | 8/3/2026 |
| 0.3.8 | 72 | 8/3/2026 |
| 0.3.7 | 83 | 8/3/2026 |
| 0.3.6 | 92 | 8/3/2026 |
| 0.3.5 | 82 | 8/3/2026 |
| 0.3.4 | 62 | 8/3/2026 |
| 0.3.3 | 55 | 8/3/2026 |
| 0.3.2 | 179 | 8/3/2026 |
| 0.3.1 | 57 | 8/3/2026 |
| 0.3.0 | 59 | 8/3/2026 |
| 0.2.2 | 59 | 8/3/2026 |