---
title: "WordprocessingML tables, sections, headers and footers"
url: https://recursiveai.org/onedocument/docs/docx-tables-sections/
description: "How the document hub reads and edits them. References are to ECMA-376 Part 1 (5th edition) unless noted. Items marked to check against Word are what the…"
version: "1.0.0"
source: https://github.com/recursive-ai-oss/onedocument-csharp/blob/a85720e075c783fc596641d8586ee126cae088c5/docs/docx-tables-sections.md
---

# WordprocessingML tables, sections, headers and footers

How the document hub reads and edits them. References are to ECMA-376 Part 1 (5th edition) unless noted. Items marked
**to check against Word** are what the hub does today where the text leaves the answer open.

## Tables (§17.4)

- **Rows and cells** are enumerated through content-control and custom-XML wrappers, so nothing is hidden. A cell is a block
  container (`TableCell`); `w:tcPr` is not a block.
- **Logical grid** (`Table.Logical()`): rows × grid columns. A cell starts at the running column (after `w:gridBefore`) and covers
  `w:gridSpan` columns (below 1 or above 16384 reads as 1). The grid width is the larger of `w:tblGrid` and the widest row, so rows
  wider than the grid widen it (`docx.table-grid-mismatch`).
- **Vertical merge** (§17.4.85): `w:vMerge` with `w:val="restart"` starts a region; with `continue` or no value it continues the cell
  directly above in the same starting grid column. A continuation under a cell that is not part of a merge
  still merges upward: the cell above becomes the region's start (observed in Word 365, 16.0.20527: three rows of `vMerge`
  continuations under an unmerged first cell read as one cell). The file is malformed, so the repair is reported as
  `docx.table-merge-orphan`. A continuation in the first row, or with no cell starting in its column above, is a plain cell.
- **Horizontal merge**: current files use `w:gridSpan`. The legacy `w:hMerge` (restart/continue) widens the cell to its left; an
  orphan continuation is a plain cell.
- A region is one `LogicalCell` anchored at its top-left cell; the swallowed physical cells are in `Covered`. Empty grid positions
  (`gridBefore`, `gridAfter`, short rows) read as null.
- **Table styles**: `Table.Look` reads `w:tblLook` (attributes or the hex mask). `StyleSheet.GetTableRegions` names the conditional
  regions of a logical cell in application order (§17.7.6); the header row is not banded and banding counts from the row after it. A
  merged cell counts by its top-left position for banding. `firstCol` and `lastCol` follow the row's first and last *physical* cell, so
  a row that starts after `gridBefore` still has its first cell in the first column (observed in Word 365, 16.0.20527). Band regions
  still count by grid position; Word's behaviour for bands in rows with merged or gapped cells has not been verified.
  `ResolveTableCell` layers style table and cell properties, regions, the table's own properties and the cell's own; borders and
  margins are flattened per side (`tblBorders/top`, `tcBorders/left`, `tblCellMar/start`). `w:tblLook` attributes win over the hex mask when both are
  present (observed in Word 365, 16.0.20527).

### Edits

Edits change only the table's markup; properties, unknown markup and untouched rows stay byte-identical, and only the part that holds
the table is rewritten.

- `InsertRow(index)` copies the row above (the first row at the top), clears the cells to one empty paragraph that keeps the first
  paragraph's properties, removes `w:ins`/`w:del`/`w:trPrChange` from the copy and the paragraph ids. A new cell continues a vertical
  merge exactly when the cells above and below belong to one region.
- `DeleteRow(index)` makes the next row's cell the start of a region that the removed row started. The only row cannot be deleted.
- `InsertColumn(column)` widens cells that span the insertion point, adds a plain empty cell elsewhere (copying the neighbour's
  width) and a `w:gridCol` of the left neighbour's width. Inside `gridBefore`/`gridAfter` the counts change instead.
- `DeleteColumn(column)` narrows spanning cells, removes cells that cover only that column, and removes the `w:gridCol`. The only
  column cannot be deleted.
- `SetCellText` keeps the first paragraph's properties and its first run's formatting and removes every other block of the cell.

## Sections (§17.6)

- A section ends at the paragraph whose `w:pPr` holds a `w:sectPr` (top level or in a block content control); the last section's
  `w:sectPr` closes the body. A body without one has one section of defaults.
- Defaults when attributes are absent: Letter portrait (12240 × 15840), margins 1440, header and footer distance 720, gutter 0, one
  column with 720 space, `w:type` next page. An unusable `w:pgSz` reads as Letter (`docx.section-page-size-invalid`).
- Columns: for unequal layouts the `w:col` list defines the count (`docx.section-columns-mismatch` when `w:num` differs).
- Page numbering, line numbering, document grid, `w:titlePg`, `w:bidi`, `w:vAlign`, `w:textDirection`, `w:noEndnote` are read as data.
  Setters exist for page size, margins, break type, title page, page numbering and equal columns; they respect the schema sequence and
  create the body's `w:sectPr` when missing.

## Headers and footers (§17.10.5)

- A reference is `w:headerReference`/`w:footerReference` with `w:type` `default`, `first` or `even` and an `r:id` of the main part.
  A reference that does not lead to a part is ignored (`docx.header-missing`); a second of the same kind and type is ignored
  (`docx.header-reference-duplicate`).
- **Linked to previous**: a section without its own reference of a kind and type uses the nearest earlier section's. The first
  section without one prints blank. There is no fallback between types: a missing `first` or `even` story is blank (or inherited
  for that type), never the default story.
- **Page choice** (`Section.ForPage`): with `w:titlePg`, the first page of the section uses `first`; else with
  `w:evenAndOddHeaders` (settings part), an even *printed* page number (after any `pgNumType` restart, not the physical page)
  uses `even`; else `default`. `Section.FirstPrintedPageNumber` gives the number a section's first page prints.
- **Blank pages** (`Section.RequiresBlankPageBefore`): observed in Word 365 (16.0.20527), with `w:evenAndOddHeaders` on a new-page
  section whose first printed number has the other parity from the physical page it would start on starts one page later, after an
  empty page (restart at 1 after three pages: starts on physical page 5; restart at 2 after two pages: physical page 4). Layout
  inserts that page; the hub only answers the question.
- **To check against Word:** a section with `w:titlePg` and no `first` reference of its own, after a section that has one, inherits it
  (the hub), while LibreOffice prints the default header on that page.
- `Section.AddOwn` creates `/word/headerN.xml` (or footer) with one empty paragraph, the relationship and the reference.
  `Document.EvenAndOddHeaders` writes `w:evenAndOddHeaders` into settings, creating the part when needed.
