Install OneDocument 1.0.0 for .NET 10, read a Word document, convert it to PDF, read and recalculate an Excel workbook, and use the onedocument command-line tool. Every sample on this page was run on the 1.0.0 packages from NuGet and shows its real output. The program makes its own input files, so you need nothing on disk.
OneDocument was called OneOffice before its first release. It is not affiliated with Microsoft; Word, Excel and PowerPoint name the file formats.
Install
The packages target net10.0, so you need the .NET 10 SDK.
dotnet new console -o OneDocumentStart cd OneDocumentStart dotnet add package RecursiveAI.OneDocument.Documents --version 1.0.0 dotnet add package RecursiveAI.OneDocument.Spreadsheets --version 1.0.0 dotnet add package RecursiveAI.OneDocument.Rendering --version 1.0.0
Eight packages are on NuGet at 1.0.0, all on one version. Reference only the kinds you use; each one registers its formats at build time, with no setup call.
RecursiveAI.OneDocument: the core. Containers, the markup tree, the format registry, formulas and number formats, templates, text extraction, RTF and VBA. The others bring it in.RecursiveAI.OneDocument.Documents: Word-processing documents: DOCX, DOC, RTF, ODT, 2003 XML, HTML and Markdown, tracked changes, mail merge, operations.RecursiveAI.OneDocument.Spreadsheets: workbooks: XLSX, XLSB, XLS, ODS, CSV and TSV, 2003 XML, calculation, pivots, streaming, typed rows.RecursiveAI.OneDocument.Presentations: PPTX, PPT and ODP.RecursiveAI.OneDocument.Schema: typed views over the OOXML schemas.RecursiveAI.OneDocument.Rendering: layout, charts, SmartArt and equations; PDF and page images (PNG, JPEG, TIFF, WebP, SVG).RecursiveAI.OneDocument.All: every package above.RecursiveAI.OneDocument.Tool: theonedocumentcommand-line tool.
None of them uses native code. A ninth, RecursiveAI.OneDocument.Cryptography (signatures and encryption on write), isn’t on NuGet: in the changelog’s words, “it follows in a 1.x release.”
Make two files to work with
The samples below are one Program.cs, run top to bottom. This first part writes a short report and a small budget, so the rest has real files to open. A new document starts with one empty paragraph, as Word’s blank document does.
using RecursiveAI.OneDocument; using RecursiveAI.OneDocument.Documents; using RecursiveAI.OneDocument.Spreadsheets; using RecursiveAI.OneDocument.Spreadsheets.Binding; using (Document made = Document.Create()) { made.Styles.AddBuiltIn("Title"); made.Styles.AddBuiltIn("Heading 1"); made.Body.AddParagraph("Site visit report").Style = "Title"; made.Body.AddParagraph("Findings").Style = "Heading 1"; made.Body.AddParagraph("The roof drains are clear. The north stairwell light is out."); made.Body.AddParagraph("Next steps").Style = "Heading 1"; made.Body.AddParagraph("Replace the stairwell light by Friday."); made.SaveAs("report.docx"); } using (Document made = Document.Create()) { Paragraph body = made.Body.AddParagraph("The supplier delivers the goods."); made.Revisions.CreateAuthor("Dana Reviewer", null).InsertText(body, 0, "Within 30 days, "); made.SaveAs("draft.docx"); // a tracked change, for the command-line section } using (Workbook made = Workbook.Create()) { Worksheet sheet = made.Sheets[0]; sheet.Rename("Budget"); sheet.Range("A1:B1").Values = new object?[,] { { "Item", "Cost" } }; sheet.Range("A2:B4").Values = new object?[,] { { "Ladder", 189.5 }, { "Bulbs", 42 }, { "Labor", 360 } }; sheet["D1"].SetString("Total"); sheet["D2"].Formula = "SUM(B2:B4)"; sheet.Range("B2:B4").NumberFormat = "$#,##0.00"; sheet.Range("D2:D2").NumberFormat = "$#,##0.00"; made.Recalculate(); // stores the formula’s result in the file, as Excel does made.SaveAs("budget.xlsx"); }
Read a .docx
Document.Open finds the format from the content, never the extension. Paragraphs carry their text and style name; Office.ExtractText gives the text of any supported file in one call.
using (Document doc = Document.Open("report.docx")) { foreach (Paragraph p in doc.Paragraphs) Console.WriteLine($"[{p.Style}] {p.Text}"); } string text = Office.ExtractText("report.docx"); Console.WriteLine($"{text.Length} characters: {text.Trim().Split('\n')[0]} ...");
[Normal] [Title] Site visit report [Heading 1] Findings [Normal] The roof drains are clear. The north stairwell light is out. [Heading 1] Next steps [Normal] Replace the stairwell light by Friday. 139 characters: Site visit report ...
To change a document in place, edit and call doc.Save(): parts you didn’t touch are written back byte-identical. On this report, setting the text of the title paragraph and saving changed word/document.xml and nothing else. See authoring and tracked changes.
Convert a .docx to PDF
One line, with the Rendering package referenced. The PDF is tagged.
Office.Convert("report.docx", "report.pdf"); byte[] pdf = File.ReadAllBytes("report.pdf"); Console.WriteLine($"report.pdf: {pdf.Length:N0} bytes, starts {System.Text.Encoding.ASCII.GetString(pdf, 0, 8)}");
report.pdf: 14,544 bytes, starts %PDF-1.7The same call converts a workbook or a presentation, and picks the output by extension: .png, .jpg, .tif, .webp and .svg write page images, and .odt, .rtf, .html or .md convert the document. By default the layout uses the open fonts that come with OneRender (Liberation, Carlito and Caladea), with a metric-compatible stand-in where the document names a font it doesn’t have: Times New Roman becomes Liberation Serif, Calibri becomes Carlito. Your own fonts can be added. See layout and PDF and page images and SVG.
Read an .xlsx
Cells give their value, their formula and the text Excel would display. Recalculate runs the calculation engine after an edit.
using (Workbook wb = Workbook.Open("budget.xlsx")) { Worksheet sheet = wb.Sheets["Budget"]; for (int row = 2; row <= 4; row++) Console.WriteLine($"{sheet[$"A{row}"].Text,-7} {sheet[$"B{row}"].FormattedText,9}"); Cell total = sheet["D2"]; Console.WriteLine($"Total {total.FormattedText,9} ={total.Formula}"); sheet["B3"].Value = 58; wb.Recalculate(); Console.WriteLine($"Total {total.FormattedText,9} after B3 = 58"); }
Ladder $189.50 Bulbs $42.00 Labor $360.00 Total $591.50 =SUM(B2:B4) Total $607.50 after B3 = 58
For rows of data, Spreadsheet.Read<T> binds each row to a record by its header names. A source generator writes the binding code, so mark the type partial:
foreach (Expense e in Spreadsheet.Read<Expense>("budget.xlsx")) Console.WriteLine(e); [SpreadsheetRow(Sheet = "Budget")] internal sealed partial record Expense(string Item, decimal Cost);
Expense { Item = Ladder, Cost = 189.5 } Expense { Item = Bulbs, Cost = 42 } Expense { Item = Labor, Cost = 360 }
Spreadsheet.Write(path, rows) is the other direction. Formula text is en-US only; in the README’s words, “localized function names are not supported.” See the workbook model, rows of plain objects and recalculation.
The onedocument tool
dotnet tool install --global RecursiveAI.OneDocument.Tool --version 1.0.0 onedocument --help
Run in the folder the program wrote to:
onedocument detect budget.xlsx onedocument extract report.docx onedocument convert report.docx report.odt onedocument render report.docx -o report.png --dpi 96 onedocument recalc budget.xlsx -o budget-recalc.xlsx
Xlsx Site visit report Findings The roof drains are clear. The north stairwell light is out. Next steps Replace the stairwell light by Friday. wrote report.odt warning font-substitution: Font "Times New Roman" is not available; "Liberation Serif" has the same glyph widths and was used instead. 1 of 1 page(s) exported; wrote report.png 1 of 1 formulas calculated in 1 levels; 0 circular, 0 #SPILL!
The warning goes to standard error. render also writes PDF, with --profile ua or --profile a2a for PDF/UA-1 or PDF/A-2a, and takes --pages for a range.
inspect lists what a file holds that a reader doesn’t see, and sanitize writes a copy without it. Here, the tracked change in draft.docx:
onedocument inspect draft.docx onedocument sanitize draft.docx clean.docx onedocument extract clean.docx
Document (Docx) revisions: 1 /word/document.xml: tracked changes [authors: Dana Reviewer] Docx -> Docx revisions: Removed, 1 removed, 1 before, 0 after removed /word/document.xml: tracked changes accepted Within 30 days, The supplier delivers the goods.
Sanitizing re-inspects what it wrote before it saves. It hasn’t been independently audited; the coverage review in the docs is the project’s own, and some categories are reported but not removed. See inspect and sanitize for every category and what is kept.
Next
- Overview: every format, every check with its source, and the gaps.
- Coming from another library: the Open XML SDK, ClosedXML, EPPlus, NPOI, MiniExcel, ShapeCrawler and Xceed DocX, call by call.
- Templates: one template language for Word, Excel and PowerPoint files.
- Redaction: removing text from the file, not only from view, with the output re-read to verify.