---
title: "Authoring with the hub"
url: https://recursiveai.org/onedocument/docs/authoring/
description: "The hub types add content through small calls that write through the markup tree and the generated views. Everything else in the part stays as it was…"
version: "1.0.0"
source: https://github.com/recursive-ai-oss/onedocument-csharp/blob/a85720e075c783fc596641d8586ee126cae088c5/docs/authoring.md
---

# Authoring with the hub

The hub types add content through small calls that write through the markup tree and the generated views. Everything else in the part stays as it was: unknown elements, attributes, prefixes, `mc:AlternateContent` and `extLst` keep their place, and the parts an authoring call does not touch are written byte-identical. `samples/Authoring` builds a document, a workbook and a deck with these calls; `testdata/authoring` holds the three files it writes.

Errors for bad arguments are `ArgumentException` (a value that cannot be stored) or `ArgumentOutOfRangeException` (a count or index); a name that does not exist is a typed `OfficeException` (below). A call that throws leaves the document as it was.

## Documents

**Styles by name.** `Paragraph.Style`, `Run.Style` and `Table.Style` read and set the style by the name Word shows. Built-in names are stored in lower case in files (`heading 1`) and read back capitalised (`Heading 1`); matching ignores case. A paragraph without a style reads as the default paragraph style (`Normal`). Setting a name the document does not define throws `StyleNotFoundException` (`docx.style-not-found`) and creates nothing; a name that belongs to another type of style is the same error. `StyleId` reads and writes the id as before.

A document made by `Document.Create()` has only `Normal`. `doc.Styles.AddBuiltIn("Heading 1")` adds a built-in style under its well-known name when the document lacks it (Title, Heading 1 to 3, Caption, List Paragraph, Hyperlink, Table Grid) and returns the existing one otherwise. These definitions are plain: fonts are not tied to the theme.

**Runs and formatting.** `paragraph.AddRun(text)` appends a run (tabs and line feeds become `w:tab` and `w:br`) and returns it. `Run.Bold`, `Italic` and `Underline` are `bool?`: true turns the property on, false writes an explicit off (so it overrides the style), null removes the direct setting. `FontSize` is in points (written in half-points, also for complex scripts), `Color` is six hex digits or `auto`, `FontName` names the font for Latin, East Asian and complex-script text. The properties report direct formatting only; resolved formatting (the style's included) comes from the style sheet (`docs/docx-styles-numbering.md`).

**Links and pictures.** `paragraph.AddHyperlink(text, address)` adds a hyperlink in the `Hyperlink` character style (added when missing). `#name` is a bookmark in the document; anything else becomes an external relationship of the main part. It is stored and never fetched. `paragraph.AddPicture(bytes, widthPoints, heightPoints, altText)` and `body.AddPicture(...)` add an inline picture (PNG, JPEG, GIF or BMP, recognised by header; the pixels are not decoded). With one side given the other keeps the picture's proportions; with none the picture shows at its pixel size on a 96 dpi screen, scaled down to fit 6 inches. Drawing ids (`wp:docPr/@id`) continue after the largest in the main part.

**Tables.** `body.AddTable(rows)` takes rows of strings (a `string[][]`, lists, `string?[,]`), `body.AddTable(rowCount, columnCount)` an empty one. The table goes at the end of the body, before the last section's properties, in the Table Grid style with columns of equal width spanning the page's text width (page width less the margins of the last section; 6.5 inches when there are none). Short rows get empty cells. At most 63 columns, as in Word. `table[row, column]` reaches a cell (a merged region answers to any of its grid positions), `table.SetCellText` sets its text.

## Workbooks

**Ranges.** `sheet.Range("A1:C10")` (or `Range(CellRange)`) returns a `SheetRange`, a handle over the sheet. `Values` reads and writes a two-dimensional array, rows first. Reading gives `null` (empty), `double`, `DateTime` (numbers whose format shows a date), `string`, `bool` or `CellError`. Writing accepts those plus the integer and floating types, `decimal`, `DateOnly` and `CellValue`; `null` empties the cell and keeps its style. Strings are text and are never parsed as numbers or formulas (set `Cell.Formula` for those). The array must have exactly the range's size, every element is checked first, and a failing write changes nothing. `SetStyle`, `UpdateStyle` (one change function call per distinct current style) and `NumberFormat` apply to every cell; `Clear()` removes the cells completely and unmerges merged ranges inside; `Merge()` keeps the top-left value and empties the others, as Excel's Merge Cells does, and refuses a single cell or an overlap with an existing merge (`InvalidOperationException`). Reads and writes over more than 4,194,304 cells throw `InvalidOperationException`, so `A1:XFD1048576` cannot allocate by accident.

**Pictures.** `sheet.AddPicture(bytes, "E1", widthPixels, heightPixels, altText)` stores the picture as a media part, creates the sheet's drawing when it has none and anchors the picture at the cell with the size given (pixels at 96 dpi; the other side keeps proportions). It returns the shape id.

## Presentations

`deck.AddSlide("Title and Content")` adds a slide from the layout of that name (case ignored, every master searched); an unknown name throws `PresentationAuthoringException` (`pptx.layout-not-found`) listing the names there are. `slide.SetPlaceholderText("title", text)` sets a placeholder by type (`title` also finds `ctrTitle`; `subTitle`, `body`, `obj`, ...) and `SetPlaceholderText(1, text)` by index; lines become paragraphs. No such placeholder throws `PresentationAuthoringException` (`pptx.placeholder-not-found`) listing the placeholders the slide has.

`slide.AddPicture(bytes, left, top, width, height, altText)` and `slide.AddTable(rows, left, top, width, rowHeight)` (or `AddTable(rowCount, columnCount, ...)`) put a picture or a table on top of the slide. Positions and sizes are in points; the table's columns share the width equally and it uses PowerPoint's default table style with a header row. Shape ids continue after the largest on the slide. New shapes go at the end of the shape tree, before a trailing `extLst`.

## Sanitising in memory

`doc.Sanitize()`, `wb.Sanitize()` and `deck.Sanitize()` run the sanitiser of `Office.Sanitize` on the open file and return its `SanitizeReport`. The default categories apply unless a `SanitizeOptions` is passed. Pending edits are committed first. The sanitiser works on a copy, **verifies on the call** (the result is inspected again, and `SanitizeVerificationException` is thrown if a category reported as removed still holds something), and only then is the file's package brought to the result: parts the sanitiser did not change keep their stored bytes, so a later save writes them raw. Verification runs on the call and not on save so that a failure surfaces where the caller can handle it, and the document is left as it was.

After the call, objects obtained from the file before it (paragraphs, sheets, slides, the style sheet) no longer belong to it; read them again. The file's `Format` does not change: when the report's `OutputFormat` differs (removing a macro project makes a macro-enabled file macro-free), name the output file after it. A binary workbook (XLSB) cannot be sanitised in memory (`OfficeUnsupportedException`); `Office.Sanitize(input, output)` writes a new file for it. For a copy of a file on disk, `Office.Sanitize(input, output)` stays the way: it never changes the input.
