# OpenXML SDK 3.x Complete Reference Encyclopedia — Part 2
**Target:** DocumentFormat.OpenXml 3.x / .NET 8+ / C# 12
**Last Updated:** 2026-03-22
This document covers page setup, tables, headers/footers, section breaks, document properties, and printing/compatibility settings. Every code block is ready to copy-paste.
---
## Namespace Aliases Used Throughout
```csharp
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using A = DocumentFormat.OpenXml.Drawing;
using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing;
using PIC = DocumentFormat.OpenXml.Drawing.Pictures;
```
---
## Table of Contents
1. [Page Setup](#1-page-setup)
2. [Tables — Comprehensive](#2-tables--comprehensive)
3. [Headers, Footers & Page Numbers](#3-headers-footers--page-numbers)
4. [Section Breaks & Multi-Section](#4-section-breaks--multi-section)
5. [Document Properties](#5-document-properties)
6. [Printing & Compatibility Settings](#6-printing--compatibility-settings)
---
## 1. Page Setup
Page setup is controlled via `SectionProperties`, which is placed as the **last child element** of `Body` for the final (or only) section, or inside `ParagraphProperties` for mid-document section breaks.
### 1.1 PageSize — Standard Paper Sizes
```csharp
// =============================================================================
// PAGE SIZE — STANDARD PAPER SIZES
// =============================================================================
// PageSize Width and Height are specified in DXA (twentieths of a point).
// 1 inch = 1440 DXA. 1 cm ≈ 567 DXA. 1 mm ≈ 56.7 DXA.
//
// Common paper sizes (portrait orientation):
// Letter: 12240 × 15840 (8.5" × 11")
// A4: 11906 × 16838 (210mm × 297mm)
// Legal: 12240 × 20160 (8.5" × 14")
// A3: 16838 × 23811 (297mm × 420mm)
// B5: 10318 × 14570 (182mm × 257mm)
// 16K: 10318 × 14570 (approximate, varies by region)
var sectionProps = new SectionProperties();
// --- Letter (US default) ---
sectionProps.AppendChild(new PageSize
{
Width = 12240U, // 8.5 inches
Height = 15840U // 11 inches
});
// Produces XML:
// --- A4 (ISO default) ---
var a4Size = new PageSize
{
Width = 11906U, // 210mm
Height = 16838U // 297mm
};
// --- Legal ---
var legalSize = new PageSize
{
Width = 12240U, // 8.5 inches
Height = 20160U // 14 inches
};
// --- A3 ---
var a3Size = new PageSize
{
Width = 16838U, // 297mm
Height = 23811U // 420mm
};
body.AppendChild(sectionProps);
```
### 1.2 PageOrientation — CRITICAL Landscape Handling
```csharp
// =============================================================================
// PAGE ORIENTATION — LANDSCAPE
// =============================================================================
// CRITICAL GOTCHA: Setting Orient = Landscape is NOT enough!
// You MUST ALSO swap Width and Height values. Word uses the numeric dimensions
// to determine actual page size; Orient only controls the print driver rotation.
//
// If you set Orient = Landscape but don't swap dimensions, Word will
// display portrait but print rotated — a confusing bug.
// --- Portrait (default, explicit) ---
var portraitSize = new PageSize
{
Width = 12240U,
Height = 15840U
// Orient is omitted for portrait (it's the default)
};
// Produces XML:
// --- Landscape — CORRECT way ---
var landscapeSize = new PageSize
{
Width = 15840U, // SWAPPED: was Height
Height = 12240U, // SWAPPED: was Width
Orient = PageOrientationValues.Landscape // AND set Orient
};
// Produces XML:
// --- Helper method for safe orientation switching ---
static PageSize CreatePageSize(uint shortEdge, uint longEdge, bool landscape)
{
return landscape
? new PageSize
{
Width = longEdge, // Long edge becomes width
Height = shortEdge, // Short edge becomes height
Orient = PageOrientationValues.Landscape
}
: new PageSize
{
Width = shortEdge,
Height = longEdge
};
}
// Usage:
var letterLandscape = CreatePageSize(12240, 15840, landscape: true);
var a4Portrait = CreatePageSize(11906, 16838, landscape: false);
```
### 1.3 PageMargin — Common Presets
```csharp
// =============================================================================
// PAGE MARGIN — ALL PROPERTIES
// =============================================================================
// All margin values are in DXA (twentieths of a point). 1 inch = 1440 DXA.
//
// Properties:
// Top, Bottom — signed Int32 (can be negative for overlap)
// Left, Right — unsigned UInt32
// Header, Footer — unsigned UInt32 (distance from page edge to header/footer)
// Gutter — unsigned UInt32 (extra margin for binding; added to Left
// unless GutterAtTop is set in Settings)
// --- Normal / Standard (1" all around) ---
var normalMargins = new PageMargin
{
Top = 1440, // 1 inch
Bottom = 1440, // 1 inch
Left = 1440U, // 1 inch
Right = 1440U, // 1 inch
Header = 720U, // 0.5 inch (distance from page edge)
Footer = 720U, // 0.5 inch
Gutter = 0U // No binding gutter
};
// Produces XML:
//
// --- Narrow (0.5" all around) ---
var narrowMargins = new PageMargin
{
Top = 720,
Bottom = 720,
Left = 720U,
Right = 720U,
Header = 720U,
Footer = 720U,
Gutter = 0U
};
// --- Moderate (1" top/bottom, 0.75" left/right) ---
var moderateMargins = new PageMargin
{
Top = 1440,
Bottom = 1440,
Left = 1080U, // 0.75 inch
Right = 1080U, // 0.75 inch
Header = 720U,
Footer = 720U,
Gutter = 0U
};
// --- Wide (1" top/bottom, 2" left/right) ---
var wideMargins = new PageMargin
{
Top = 1440,
Bottom = 1440,
Left = 2880U, // 2 inches
Right = 2880U, // 2 inches
Header = 720U,
Footer = 720U,
Gutter = 0U
};
// --- Chinese Government Document 公文 standard (GB/T 9704-2012) ---
// Top: 37mm (2098 DXA), Bottom: 35mm (1984 DXA)
// Left: 28mm (1588 DXA), Right: 26mm (1474 DXA)
var chineseGovMargins = new PageMargin
{
Top = 2098, // 37mm
Bottom = 1984, // 35mm
Left = 1588U, // 28mm
Right = 1474U, // 26mm
Header = 851U, // 15mm
Footer = 567U, // 10mm (approx)
Gutter = 0U
};
// --- With binding gutter (e.g., for book printing) ---
var gutterMargins = new PageMargin
{
Top = 1440,
Bottom = 1440,
Left = 1440U,
Right = 1440U,
Header = 720U,
Footer = 720U,
Gutter = 720U // 0.5 inch added to left margin for binding
};
```
### 1.4 PageBorders
```csharp
// =============================================================================
// PAGE BORDERS
// =============================================================================
// PageBorders defines borders around the entire page.
// OffsetFrom controls whether border distance is from page edge or text margin.
// PageBorderOffsetValues.Page = from page edge
// PageBorderOffsetValues.Text = from text margin
//
// Each border: Val (style), Size (eighth-points: 4=0.5pt, 8=1pt, 12=1.5pt),
// Color (hex RGB), Space (distance in points from text/page edge)
var pageBorders = new PageBorders
{
OffsetFrom = PageBorderOffsetValues.Page
};
// Top border: single line, 1pt, black
pageBorders.AppendChild(new TopBorder
{
Val = BorderValues.Single,
Size = 8U, // 8 eighth-points = 1pt
Color = "000000",
Space = 24U // 24 points from page edge
});
// Bottom border
pageBorders.AppendChild(new BottomBorder
{
Val = BorderValues.Single,
Size = 8U,
Color = "000000",
Space = 24U
});
// Left border
pageBorders.AppendChild(new LeftBorder
{
Val = BorderValues.Single,
Size = 8U,
Color = "000000",
Space = 24U
});
// Right border
pageBorders.AppendChild(new RightBorder
{
Val = BorderValues.Single,
Size = 8U,
Color = "000000",
Space = 24U
});
// --- Double border example ---
var doubleBorder = new TopBorder
{
Val = BorderValues.Double, // Double line
Size = 4U, // 0.5pt per line
Color = "0000FF", // Blue
Space = 24U
};
// --- Common BorderValues ---
// Single, Double, Triple, DotDash, Dashed, Dotted, Thick, ThinThickSmallGap,
// ThickThinSmallGap, Wave, DoubleWave, BasicBlackDots, None
sectionProps.AppendChild(pageBorders);
```
### 1.5 Columns — Multi-Column Layout
```csharp
// =============================================================================
// COLUMNS — MULTI-COLUMN LAYOUTS
// =============================================================================
// Columns element defines the column layout within a section.
// Space is measured in DXA. EqualWidth=true makes all columns equal.
// --- 2 equal columns ---
var twoCols = new Columns
{
EqualWidth = true,
ColumnCount = 2,
Space = "720" // 0.5 inch gap between columns
};
// Produces XML:
// --- 3 equal columns with separator ---
var threeColsSep = new Columns
{
EqualWidth = true,
ColumnCount = 3,
Space = "360", // 0.25 inch gap
Separator = true // Vertical line between columns
};
// Produces XML:
// --- Custom unequal columns (e.g., 2/3 + 1/3 of content width) ---
// When using unequal widths, EqualWidth must be false and you define
// each column explicitly with Column elements.
// For Letter page with 1" margins: content width = 12240 - 1440 - 1440 = 9360 DXA
// Column 1: 6000 DXA wide, 360 DXA space after
// Column 2: 3000 DXA wide (6000 + 360 + 3000 = 9360)
var unequalCols = new Columns { EqualWidth = false };
unequalCols.AppendChild(new Column
{
Width = "6000",
Space = "360"
});
unequalCols.AppendChild(new Column
{
Width = "3000"
// No Space on last column
});
// Produces XML:
//
//
//
//
sectionProps.AppendChild(unequalCols);
```
### 1.6 LineNumbering
```csharp
// =============================================================================
// LINE NUMBERING
// =============================================================================
// LineNumbering adds line numbers in the margin. Useful for legal/academic docs.
// CountBy = show every Nth number (1 = every line, 5 = every 5th)
// Start = starting number
// Distance = distance from text in DXA
// Restart: NewPage, NewSection, Continuous
var lineNums = new LineNumbering
{
CountBy = 5, // Show every 5th line number
Start = 1, // Start at line 1
Distance = 360U, // 0.25 inch from text
Restart = LineNumberRestartValues.NewPage // Restart each page
};
// Produces XML:
//
sectionProps.AppendChild(lineNums);
```
### 1.7 DocGrid — CJK Document Grid
```csharp
// =============================================================================
// DOCUMENT GRID — CJK LAYOUT
// =============================================================================
// DocGrid specifies a document grid (character grid) primarily for CJK documents.
// Type: Default (no grid), Lines (line grid), LinesAndChars (line+char grid),
// SnapToChars (snap to character grid)
// LinePitch = distance between lines in DXA (e.g., 312 for standard Chinese docs)
// CharacterSpace = extra spacing between characters in DXA
// --- Chinese document grid: 28 lines × 28 chars per line (common for 公文) ---
var cjkGrid = new DocGrid
{
Type = DocGridValues.LinesAndChars,
LinePitch = 579, // DXA between lines (≈ 28 lines on A4 with std margins)
CharacterSpace = 0 // Default character spacing
};
// Produces XML:
//
// --- Simple line-only grid ---
var lineGrid = new DocGrid
{
Type = DocGridValues.Lines,
LinePitch = 360 // Standard line pitch
};
sectionProps.AppendChild(cjkGrid);
```
### 1.8 VerticalTextAlignmentOnPage
```csharp
// =============================================================================
// VERTICAL TEXT ALIGNMENT ON PAGE
// =============================================================================
// Controls vertical alignment of text within the page (above bottom margin).
// Values: Top (default), Center, Both (justified), Bottom
var vertAlign = new VerticalTextAlignmentOnPage
{
Val = VerticalJustificationValues.Center
};
// Produces XML:
// Use case: Title pages, certificate pages
sectionProps.AppendChild(vertAlign);
```
### 1.9 Complete Page Setup Example
```csharp
// =============================================================================
// COMPLETE PAGE SETUP — PUTTING IT ALL TOGETHER
// =============================================================================
// A4 portrait, moderate margins, 2-column layout with separator
using var doc = WordprocessingDocument.Create(
"PageSetupDemo.docx", WordprocessingDocumentType.Document);
var mainPart = doc.MainDocumentPart!;
var body = mainPart.Document.Body!;
// Add content paragraphs ...
body.AppendChild(new Paragraph(
new Run(new Text("This is a two-column document on A4 paper."))));
// Section properties — MUST be last child of Body
var sectPr = new SectionProperties();
// Page size: A4 portrait
sectPr.AppendChild(new PageSize
{
Width = 11906U,
Height = 16838U
});
// Margins: moderate
sectPr.AppendChild(new PageMargin
{
Top = 1440, Bottom = 1440,
Left = 1080U, Right = 1080U,
Header = 720U, Footer = 720U, Gutter = 0U
});
// 2-column with separator
sectPr.AppendChild(new Columns
{
EqualWidth = true,
ColumnCount = 2,
Space = "720",
Separator = true
});
// Line grid for CJK
sectPr.AppendChild(new DocGrid
{
Type = DocGridValues.Lines,
LinePitch = 312
});
body.AppendChild(sectPr);
mainPart.Document.Save();
```
---
## 2. Tables — Comprehensive
### 2.1 TableProperties — Width, Layout, Alignment, Indent
```csharp
// =============================================================================
// TABLE PROPERTIES — WIDTH, LAYOUT, ALIGNMENT
// =============================================================================
// Table width can be specified in three ways:
// Pct: percentage of page width. 5000 = 100%. (multiply % by 50)
// Dxa: absolute width in DXA (twentieths of a point). 1 inch = 1440 DXA.
// Auto: table auto-sizes to content.
//
// TableLayout:
// Fixed: columns maintain exact widths. Required for complex cell sizing.
// Autofit: columns adjust to content (default).
//
// TableJustification: Left (default), Center, Right
var table = new Table();
var tblProps = new TableProperties();
// --- Width: 100% of page ---
tblProps.AppendChild(new TableWidth
{
Width = "5000", // 5000 = 100%
Type = TableWidthUnitValues.Pct
});
// Produces XML:
// --- Width: fixed DXA ---
// var tblWidth = new TableWidth { Width = "9360", Type = TableWidthUnitValues.Dxa };
// --- Width: auto ---
// var tblWidth = new TableWidth { Width = "0", Type = TableWidthUnitValues.Auto };
// Layout: fixed column widths
tblProps.AppendChild(new TableLayout
{
Type = TableLayoutValues.Fixed
});
// Center the table on the page
tblProps.AppendChild(new TableJustification
{
Val = TableRowAlignmentValues.Center
});
// Table indent (left offset when not centered) — in DXA
// tblProps.AppendChild(new TableIndentation
// {
// Width = 720, // 0.5 inch indent
// Type = TableWidthUnitValues.Dxa
// });
table.AppendChild(tblProps);
```
### 2.2 TableBorders — All Border Properties
```csharp
// =============================================================================
// TABLE BORDERS
// =============================================================================
// TableBorders defines default borders for the entire table.
// Each border has:
// Val — BorderValues enum (Single, Double, Dotted, Dashed, None, etc.)
// Size — in eighth-points: 4 = 0.5pt, 8 = 1pt, 12 = 1.5pt, 16 = 2pt, 24 = 3pt
// Color — hex RGB string (e.g., "000000" for black, "FF0000" for red)
// Space — space between border and content in points
//
// Border types: TopBorder, BottomBorder, LeftBorder, RightBorder,
// InsideHorizontalBorder, InsideVerticalBorder
var tblBorders = new TableBorders();
// Top border: single, 1pt, black
tblBorders.AppendChild(new TopBorder
{
Val = BorderValues.Single,
Size = 8U, // 1pt (8 eighth-points)
Color = "000000",
Space = 0U
});
// Bottom border
tblBorders.AppendChild(new BottomBorder
{
Val = BorderValues.Single,
Size = 8U,
Color = "000000",
Space = 0U
});
// Left border
tblBorders.AppendChild(new LeftBorder
{
Val = BorderValues.Single,
Size = 8U,
Color = "000000",
Space = 0U
});
// Right border
tblBorders.AppendChild(new RightBorder
{
Val = BorderValues.Single,
Size = 8U,
Color = "000000",
Space = 0U
});
// Inside horizontal borders (between rows)
tblBorders.AppendChild(new InsideHorizontalBorder
{
Val = BorderValues.Single,
Size = 4U, // 0.5pt — thinner than outer borders
Color = "000000",
Space = 0U
});
// Inside vertical borders (between columns)
tblBorders.AppendChild(new InsideVerticalBorder
{
Val = BorderValues.Single,
Size = 4U,
Color = "000000",
Space = 0U
});
// Produces XML:
//
//
//
//
//
//
//
//
tblProps.AppendChild(tblBorders);
```
### 2.3 TableGrid — Column Definitions
```csharp
// =============================================================================
// TABLE GRID — COLUMN WIDTH DEFINITIONS
// =============================================================================
// TableGrid defines the column structure of the table. Each GridColumn
// specifies the width in DXA. The number of GridColumn elements determines
// the number of columns.
//
// IMPORTANT: GridColumn widths should sum to the table width.
// For a 100% table on Letter with 1" margins: 12240 - 1440 - 1440 = 9360 DXA
var tableGrid = new TableGrid();
// 3 columns: 3000 + 3000 + 3360 = 9360 DXA
tableGrid.AppendChild(new GridColumn { Width = "3000" });
tableGrid.AppendChild(new GridColumn { Width = "3000" });
tableGrid.AppendChild(new GridColumn { Width = "3360" });
// Produces XML:
//
//
//
//
//
table.AppendChild(tableGrid);
```
### 2.4 TableCellProperties — Width, Alignment, Direction, Shading
```csharp
// =============================================================================
// TABLE CELL PROPERTIES
// =============================================================================
// TableCellProperties controls individual cell appearance.
var cellProps = new TableCellProperties();
// Cell width — should match the GridColumn width
cellProps.AppendChild(new TableCellWidth
{
Width = "3000",
Type = TableWidthUnitValues.Dxa
});
// Vertical alignment within cell: Top (default), Center, Bottom
cellProps.AppendChild(new TableCellVerticalAlignment
{
Val = TableVerticalAlignmentValues.Center
});
// Text direction (for vertical text in CJK, etc.)
// Values: LefToRight (default), TopToBottom (text rotated 90° CW),
// TopToBottomV (vertical CJK), BottomToTopV
cellProps.AppendChild(new TextDirection
{
Val = TextDirectionValues.TopToBottom
});
// NoWrap — prevents text from wrapping within the cell
cellProps.AppendChild(new NoWrap());
// Cell shading (background color)
cellProps.AppendChild(new Shading
{
Val = ShadingPatternValues.Clear,
Color = "auto",
Fill = "D9E2F3" // Light blue background
});
// Produces XML:
```
### 2.5 TableCellMargin — Table-Level Default vs Per-Cell Override
```csharp
// =============================================================================
// TABLE CELL MARGINS
// =============================================================================
// There are TWO levels of cell margin control:
// 1. Table-level default: TableCellMarginDefault (inside TableProperties)
// 2. Per-cell override: TableCellMargin (inside TableCellProperties)
//
// All values are in DXA. Default Word cell margins are approximately:
// Top: 0, Bottom: 0, Left: 108 (0.075"), Right: 108
// --- Table-level default margins ---
var defaultMargins = new TableCellMarginDefault();
defaultMargins.AppendChild(new TopMargin
{
Width = "72", // ~0.05 inch
Type = TableWidthUnitValues.Dxa
});
defaultMargins.AppendChild(new BottomMargin
{
Width = "72",
Type = TableWidthUnitValues.Dxa
});
defaultMargins.AppendChild(new StartMargin
{
Width = "108", // ~0.075 inch (default)
Type = TableWidthUnitValues.Dxa
});
defaultMargins.AppendChild(new EndMargin
{
Width = "108",
Type = TableWidthUnitValues.Dxa
});
tblProps.AppendChild(defaultMargins);
// Produces XML:
//
//
//
//
//
//
// --- Per-cell override (larger padding for a specific cell) ---
var cellMarginOverride = new TableCellMargin();
cellMarginOverride.AppendChild(new TopMargin
{
Width = "144", // 0.1 inch
Type = TableWidthUnitValues.Dxa
});
cellMarginOverride.AppendChild(new BottomMargin
{
Width = "144",
Type = TableWidthUnitValues.Dxa
});
cellMarginOverride.AppendChild(new StartMargin
{
Width = "216", // 0.15 inch
Type = TableWidthUnitValues.Dxa
});
cellMarginOverride.AppendChild(new EndMargin
{
Width = "216",
Type = TableWidthUnitValues.Dxa
});
cellProps.AppendChild(cellMarginOverride);
```
### 2.6 Row Height
```csharp
// =============================================================================
// TABLE ROW HEIGHT
// =============================================================================
// TableRowHeight is set inside TableRowProperties.
// Val = height in DXA
// HeightRuleType:
// Exact: row is exactly this height (content may be clipped)
// AtLeast: row is at least this height, grows for content (default behavior)
// Auto: height determined by content
var rowProps = new TableRowProperties();
// Row height: at least 0.5 inch
rowProps.AppendChild(new TableRowHeight
{
Val = 720U, // 0.5 inch in DXA
HeightRule = HeightRuleValues.AtLeast
});
// Produces XML:
// Exact height (content may clip):
// rowProps.AppendChild(new TableRowHeight
// {
// Val = 720U,
// HeightRule = HeightRuleValues.Exact
// });
var row = new TableRow();
row.AppendChild(rowProps);
```
### 2.7 Header Row Repeat (Repeat on Every Page)
```csharp
// =============================================================================
// HEADER ROW REPEAT
// =============================================================================
// TableHeader on TableRowProperties marks a row to repeat at the top
// of each page when the table spans multiple pages.
// IMPORTANT: Only works for rows at the TOP of the table (contiguous from first row).
// You cannot repeat a row in the middle of a table.
var headerRowProps = new TableRowProperties();
headerRowProps.AppendChild(new TableHeader()); // No value needed — presence = true
// Produces XML:
//
//
//
var headerRow = new TableRow();
headerRow.AppendChild(headerRowProps);
// ... add cells to headerRow ...
```
### 2.8 Per-Cell Border Override
```csharp
// =============================================================================
// PER-CELL BORDER OVERRIDE
// =============================================================================
// TableCellBorders inside TableCellProperties overrides table-level borders
// for a specific cell. Useful for special formatting, merging visual areas, etc.
var cellBorders = new TableCellBorders();
// Remove bottom border on this cell
cellBorders.AppendChild(new BottomBorder
{
Val = BorderValues.None,
Size = 0U,
Color = "auto",
Space = 0U
});
// Add thick right border
cellBorders.AppendChild(new RightBorder
{
Val = BorderValues.Single,
Size = 24U, // 3pt thick
Color = "FF0000", // Red
Space = 0U
});
// Produces XML:
//
//
//
//
cellProps.AppendChild(cellBorders);
```
### 2.9 Horizontal Merge (GridSpan)
```csharp
// =============================================================================
// HORIZONTAL MERGE — GRIDSPAN
// =============================================================================
// To merge cells horizontally, use GridSpan on the first cell's properties.
// The cell spans across multiple grid columns. You do NOT add extra cells
// for the spanned columns — only one cell covers the span.
//
// Example: 3-column table, first row merges columns 1+2
var mergedRow = new TableRow();
// Cell spanning columns 1 and 2
var spanCell = new TableCell();
var spanCellProps = new TableCellProperties();
spanCellProps.AppendChild(new GridSpan { Val = 2 }); // Span 2 columns
spanCellProps.AppendChild(new TableCellWidth
{
Width = "6000", // Combined width: 3000 + 3000
Type = TableWidthUnitValues.Dxa
});
spanCell.AppendChild(spanCellProps);
spanCell.AppendChild(new Paragraph(new Run(new Text("Spans columns 1-2"))));
mergedRow.AppendChild(spanCell);
// Cell in column 3 (normal)
var normalCell = new TableCell();
normalCell.AppendChild(new TableCellProperties(
new TableCellWidth { Width = "3360", Type = TableWidthUnitValues.Dxa }));
normalCell.AppendChild(new Paragraph(new Run(new Text("Column 3"))));
mergedRow.AppendChild(normalCell);
// Produces XML for the merged cell:
//
//
//
//
//
// Spans columns 1-2
//
```
### 2.10 Vertical Merge
```csharp
// =============================================================================
// VERTICAL MERGE
// =============================================================================
// To merge cells vertically across rows, use VerticalMerge:
// First row: VerticalMerge with Val = Restart (starts the merge)
// Subsequent rows: VerticalMerge with Val = Continue (or omit Val — Continue is default)
//
// Each row still needs the cell placeholder, but continue cells should contain
// an empty paragraph only.
// Row 1: start of vertical merge
var vRow1 = new TableRow();
var vCell1 = new TableCell();
var vCellProps1 = new TableCellProperties();
vCellProps1.AppendChild(new VerticalMerge { Val = MergedCellValues.Restart });
vCellProps1.AppendChild(new TableCellWidth { Width = "3000", Type = TableWidthUnitValues.Dxa });
vCell1.AppendChild(vCellProps1);
vCell1.AppendChild(new Paragraph(new Run(new Text("Merged vertically"))));
vRow1.AppendChild(vCell1);
// ... add remaining cells in row 1
// Row 2: continue the vertical merge
var vRow2 = new TableRow();
var vCell2 = new TableCell();
var vCellProps2 = new TableCellProperties();
vCellProps2.AppendChild(new VerticalMerge()); // Val omitted = Continue
vCellProps2.AppendChild(new TableCellWidth { Width = "3000", Type = TableWidthUnitValues.Dxa });
vCell2.AppendChild(vCellProps2);
vCell2.AppendChild(new Paragraph()); // Empty paragraph required — cell must have content
vRow2.AppendChild(vCell2);
// ... add remaining cells in row 2
// Row 3: if no VerticalMerge, the merge stops and this cell is independent.
// Produces XML:
// Row 1 cell:
// Row 2 cell: (continue is the default)
```
### 2.11 Nested Tables
```csharp
// =============================================================================
// NESTED TABLES
// =============================================================================
// A table can be nested inside a table cell. Simply add a Table element
// as a child of a TableCell (alongside the required Paragraph).
// IMPORTANT: Every TableCell MUST contain at least one Paragraph, even if
// it also contains a nested table. The Paragraph should come AFTER the table.
var outerTable = new Table();
// ... outer table properties and grid ...
var containerCell = new TableCell();
containerCell.AppendChild(new TableCellProperties(
new TableCellWidth { Width = "6000", Type = TableWidthUnitValues.Dxa }));
// Build inner (nested) table
var innerTable = new Table();
var innerProps = new TableProperties();
innerProps.AppendChild(new TableWidth { Width = "5000", Type = TableWidthUnitValues.Pct });
innerProps.AppendChild(new TableBorders(
new TopBorder { Val = BorderValues.Single, Size = 4U, Color = "999999" },
new BottomBorder { Val = BorderValues.Single, Size = 4U, Color = "999999" },
new LeftBorder { Val = BorderValues.Single, Size = 4U, Color = "999999" },
new RightBorder { Val = BorderValues.Single, Size = 4U, Color = "999999" },
new InsideHorizontalBorder { Val = BorderValues.Single, Size = 4U, Color = "999999" },
new InsideVerticalBorder { Val = BorderValues.Single, Size = 4U, Color = "999999" }
));
innerTable.AppendChild(innerProps);
var innerGrid = new TableGrid(
new GridColumn { Width = "2500" },
new GridColumn { Width = "2500" });
innerTable.AppendChild(innerGrid);
var innerRow = new TableRow(
new TableCell(
new TableCellProperties(new TableCellWidth { Width = "2500", Type = TableWidthUnitValues.Dxa }),
new Paragraph(new Run(new Text("Inner A")))),
new TableCell(
new TableCellProperties(new TableCellWidth { Width = "2500", Type = TableWidthUnitValues.Dxa }),
new Paragraph(new Run(new Text("Inner B"))))
);
innerTable.AppendChild(innerRow);
// Add inner table to container cell
containerCell.AppendChild(innerTable);
// MUST have a paragraph after nested table
containerCell.AppendChild(new Paragraph());
```
### 2.12 Table Positioning (Floating Table)
```csharp
// =============================================================================
// TABLE POSITIONING — FLOATING TABLE
// =============================================================================
// TablePositionProperties makes a table "float" at a specific position
// on the page, allowing text to wrap around it.
// All position values are in DXA.
var floatProps = new TablePositionProperties
{
VerticalAnchor = VerticalAnchorValues.Page,
HorizontalAnchor = HorizontalAnchorValues.Page,
TablePositionX = 2880, // 2 inches from left page edge
TablePositionY = 4320, // 3 inches from top page edge
LeftFromText = 180, // 0.125 inch gap from text on left
RightFromText = 180,
TopFromText = 0,
BottomFromText = 0
};
tblProps.AppendChild(floatProps);
// Produces XML:
//
```
### 2.13 TableLook — Conditional Formatting Flags
```csharp
// =============================================================================
// TABLE LOOK — CONDITIONAL FORMATTING FLAGS
// =============================================================================
// TableLook controls which conditional formatting bands are applied from
// the table style. These flags tell Word which "special" formatting to use.
//
// Val is a hex bitmask. Common values:
// 0x04A0 = FirstRow + LastRow + NoHBand (typical for styled tables)
// 0x0000 = no conditional formatting
//
// Individual boolean flags can also be set:
var tableLook = new TableLook
{
Val = "04A0",
FirstRow = true, // Apply first-row (header) formatting
LastRow = true, // Apply last-row (totals) formatting
FirstColumn = false,
LastColumn = false,
NoHorizontalBand = true, // Don't apply horizontal banding
NoVerticalBand = true // Don't apply vertical banding
};
tblProps.AppendChild(tableLook);
// Produces XML:
//
```
### 2.14 Complete Styled Table — Header + Data + Totals + Zebra Striping
```csharp
// =============================================================================
// COMPLETE STYLED TABLE
// =============================================================================
// Builds a professional table with:
// - Header row (dark background, white text, repeats on page break)
// - Data rows with alternating zebra-stripe shading
// - Totals row (bold, top border)
// - Full 100% width on Letter page
static Table CreateStyledTable(string[][] data, bool hasHeaderRow = true, bool hasTotalsRow = true)
{
int cols = data[0].Length;
// For Letter page with 1" margins: 12240 - 2 * 1440 = 9360
int totalWidth = 9360;
int colWidth = totalWidth / cols;
var table = new Table();
// --- Table Properties ---
var tblPr = new TableProperties(
new TableWidth { Width = "5000", Type = TableWidthUnitValues.Pct },
new TableLayout { Type = TableLayoutValues.Fixed },
new TableBorders(
new TopBorder { Val = BorderValues.Single, Size = 8U, Color = "000000" },
new BottomBorder { Val = BorderValues.Single, Size = 8U, Color = "000000" },
new LeftBorder { Val = BorderValues.Single, Size = 4U, Color = "BFBFBF" },
new RightBorder { Val = BorderValues.Single, Size = 4U, Color = "BFBFBF" },
new InsideHorizontalBorder { Val = BorderValues.Single, Size = 4U, Color = "BFBFBF" },
new InsideVerticalBorder { Val = BorderValues.Single, Size = 4U, Color = "BFBFBF" }
),
new TableLook
{
Val = "04A0", FirstRow = true, LastRow = true,
FirstColumn = false, LastColumn = false,
NoHorizontalBand = true, NoVerticalBand = true
}
);
table.AppendChild(tblPr);
// --- Table Grid ---
var grid = new TableGrid();
for (int c = 0; c < cols; c++)
{
int w = (c == cols - 1) ? totalWidth - colWidth * (cols - 1) : colWidth;
grid.AppendChild(new GridColumn { Width = w.ToString() });
}
table.AppendChild(grid);
// --- Rows ---
for (int r = 0; r < data.Length; r++)
{
bool isHeader = hasHeaderRow && r == 0;
bool isTotals = hasTotalsRow && r == data.Length - 1;
bool isOddDataRow = !isHeader && !isTotals && (r % 2 == 1);
var row = new TableRow();
// Row properties
var trPr = new TableRowProperties();
trPr.AppendChild(new TableRowHeight
{
Val = isHeader ? 480U : 360U,
HeightRule = HeightRuleValues.AtLeast
});
if (isHeader)
{
trPr.AppendChild(new TableHeader()); // Repeat header on each page
}
row.AppendChild(trPr);
// Cells
for (int c = 0; c < cols; c++)
{
int w = (c == cols - 1) ? totalWidth - colWidth * (cols - 1) : colWidth;
var cell = new TableCell();
var tcPr = new TableCellProperties();
tcPr.AppendChild(new TableCellWidth
{
Width = w.ToString(),
Type = TableWidthUnitValues.Dxa
});
tcPr.AppendChild(new TableCellVerticalAlignment
{
Val = TableVerticalAlignmentValues.Center
});
// Header: dark blue background
if (isHeader)
{
tcPr.AppendChild(new Shading
{
Val = ShadingPatternValues.Clear,
Color = "auto",
Fill = "2F5496" // Dark blue
});
}
// Zebra stripe: light gray on odd data rows
else if (isOddDataRow)
{
tcPr.AppendChild(new Shading
{
Val = ShadingPatternValues.Clear,
Color = "auto",
Fill = "F2F2F2" // Light gray
});
}
// Totals row: top border emphasis
if (isTotals)
{
tcPr.AppendChild(new TableCellBorders(
new TopBorder
{
Val = BorderValues.Single,
Size = 12U, // 1.5pt
Color = "000000"
}
));
}
cell.AppendChild(tcPr);
// Paragraph with text
var runProps = new RunProperties();
if (isHeader)
{
runProps.AppendChild(new Bold());
runProps.AppendChild(new Color { Val = "FFFFFF" }); // White text
runProps.AppendChild(new FontSize { Val = "22" }); // 11pt
}
else if (isTotals)
{
runProps.AppendChild(new Bold());
}
var para = new Paragraph(
new ParagraphProperties(
new Justification
{
Val = (c > 0) ? JustificationValues.Right : JustificationValues.Left
}
),
new Run(runProps, new Text(data[r][c]))
);
cell.AppendChild(para);
row.AppendChild(cell);
}
table.AppendChild(row);
}
return table;
}
// --- Usage ---
string[][] salesData =
[
["Product", "Q1", "Q2", "Q3", "Q4" ],
["Widget A", "1,200", "1,350", "1,100", "1,500"],
["Widget B", "800", "920", "870", "1,010"],
["Widget C", "2,100", "2,300", "2,150", "2,400"],
["Total", "4,100", "4,570", "4,120", "4,910"]
];
var styledTable = CreateStyledTable(salesData, hasHeaderRow: true, hasTotalsRow: true);
body.AppendChild(styledTable);
body.AppendChild(new Paragraph()); // Empty paragraph after table
```
### 2.15 Three-Line Table (学术三线表)
```csharp
// =============================================================================
// THREE-LINE TABLE (学术三线表)
// =============================================================================
// Academic/scientific table style common in Chinese academic publishing:
// - Top border: THICK (1.5pt)
// - Border below header: THIN (0.75pt)
// - Bottom border: THICK (1.5pt)
// - NO vertical borders, NO other horizontal borders
//
// This is achieved by:
// 1. Setting table borders to None (removes all defaults)
// 2. Using per-cell borders on the header row (bottom) and first/last rows (top/bottom)
static Table CreateThreeLineTable(string[] headers, string[][] rows)
{
int cols = headers.Length;
int totalWidth = 9360;
int colWidth = totalWidth / cols;
var table = new Table();
// Table properties: NO borders by default
var tblPr = new TableProperties(
new TableWidth { Width = "5000", Type = TableWidthUnitValues.Pct },
new TableJustification { Val = TableRowAlignmentValues.Center },
new TableLayout { Type = TableLayoutValues.Fixed },
new TableBorders(
new TopBorder { Val = BorderValues.None, Size = 0U },
new BottomBorder { Val = BorderValues.None, Size = 0U },
new LeftBorder { Val = BorderValues.None, Size = 0U },
new RightBorder { Val = BorderValues.None, Size = 0U },
new InsideHorizontalBorder { Val = BorderValues.None, Size = 0U },
new InsideVerticalBorder { Val = BorderValues.None, Size = 0U }
)
);
table.AppendChild(tblPr);
// Table grid
var grid = new TableGrid();
for (int c = 0; c < cols; c++)
{
int w = (c == cols - 1) ? totalWidth - colWidth * (cols - 1) : colWidth;
grid.AppendChild(new GridColumn { Width = w.ToString() });
}
table.AppendChild(grid);
// --- Header row ---
var headerRow = new TableRow();
headerRow.AppendChild(new TableRowProperties(new TableHeader()));
for (int c = 0; c < cols; c++)
{
int w = (c == cols - 1) ? totalWidth - colWidth * (cols - 1) : colWidth;
var cell = new TableCell();
var tcPr = new TableCellProperties(
new TableCellWidth { Width = w.ToString(), Type = TableWidthUnitValues.Dxa },
new TableCellBorders(
// Top: THICK line (1.5pt = 12 eighth-points)
new TopBorder { Val = BorderValues.Single, Size = 12U, Color = "000000", Space = 0U },
// Bottom: THIN line (0.75pt = 6 eighth-points)
new BottomBorder { Val = BorderValues.Single, Size = 6U, Color = "000000", Space = 0U }
),
new TableCellVerticalAlignment { Val = TableVerticalAlignmentValues.Center }
);
cell.AppendChild(tcPr);
cell.AppendChild(new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center }),
new Run(
new RunProperties(new Bold()),
new Text(headers[c]))));
cell.AppendChild(cell); // Fix: should be row.AppendChild
headerRow.AppendChild(cell);
}
table.AppendChild(headerRow);
// --- Data rows ---
for (int r = 0; r < rows.Length; r++)
{
var dataRow = new TableRow();
bool isLastRow = (r == rows.Length - 1);
for (int c = 0; c < cols; c++)
{
int w = (c == cols - 1) ? totalWidth - colWidth * (cols - 1) : colWidth;
var cell = new TableCell();
var tcPr = new TableCellProperties(
new TableCellWidth { Width = w.ToString(), Type = TableWidthUnitValues.Dxa }
);
// Last data row: add THICK bottom border
if (isLastRow)
{
tcPr.AppendChild(new TableCellBorders(
new BottomBorder
{
Val = BorderValues.Single,
Size = 12U, // 1.5pt thick
Color = "000000",
Space = 0U
}
));
}
tcPr.AppendChild(new TableCellVerticalAlignment
{
Val = TableVerticalAlignmentValues.Center
});
cell.AppendChild(tcPr);
cell.AppendChild(new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center }),
new Run(new Text(rows[r][c]))));
dataRow.AppendChild(cell);
}
table.AppendChild(dataRow);
}
return table;
}
// --- Usage ---
string[] columnHeaders = ["Variable", "Mean", "SD", "p-value"];
string[][] dataRows =
[
["Age", "45.2", "12.3", "0.032"],
["BMI", "26.8", "4.1", "0.001"],
["SBP", "132", "18.5", "< 0.001"],
["HR", "72.1", "11.2", "0.145"]
];
var threeLineTable = CreateThreeLineTable(columnHeaders, dataRows);
body.AppendChild(threeLineTable);
```
---
## 3. Headers, Footers & Page Numbers
Headers and footers are stored in separate XML parts (HeaderPart, FooterPart) linked to SectionProperties via HeaderReference and FooterReference.
### 3.1 Creating HeaderPart and FooterPart
```csharp
// =============================================================================
// CREATING HEADER AND FOOTER PARTS
// =============================================================================
// Headers and footers are separate parts within the package, each containing
// their own XML document tree rooted at or .
// They are linked to sections via HeaderReference/FooterReference.
//
// Steps:
// 1. Add a HeaderPart/FooterPart to the MainDocumentPart
// 2. Set the part's root element (Header/Footer)
// 3. Add content (paragraphs, tables, images) to the root element
// 4. Create a HeaderReference/FooterReference in SectionProperties
var mainPart = doc.MainDocumentPart!;
// --- Create a header part ---
var headerPart = mainPart.AddNewPart();
string headerPartId = mainPart.GetIdOfPart(headerPart);
// Build header content
headerPart.Header = new Header(
new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center }),
new Run(
new RunProperties(
new FontSize { Val = "18" }, // 9pt — typical header size
new Color { Val = "808080" }), // Gray
new Text("Company Confidential"))
)
);
headerPart.Header.Save();
// --- Create a footer part ---
var footerPart = mainPart.AddNewPart();
string footerPartId = mainPart.GetIdOfPart(footerPart);
footerPart.Footer = new Footer(
new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center }),
new Run(
new RunProperties(new FontSize { Val = "18" }),
new Text("Footer text here"))
)
);
footerPart.Footer.Save();
```
### 3.2 HeaderReference and FooterReference — Types
```csharp
// =============================================================================
// HEADER/FOOTER REFERENCE — LINKING TO SECTION
// =============================================================================
// HeaderReference/FooterReference types:
// Default — used on all pages (unless First/Even overrides)
// First — used on the first page of the section (requires TitlePage)
// Even — used on even-numbered pages (requires EvenAndOddHeaders setting)
//
// A section can have up to 3 headers and 3 footers (Default + First + Even).
var sectPr = body.Elements().FirstOrDefault()
?? body.AppendChild(new SectionProperties());
// Default header (all pages)
sectPr.AppendChild(new HeaderReference
{
Type = HeaderFooterValues.Default,
Id = headerPartId
});
// Default footer (all pages)
sectPr.AppendChild(new FooterReference
{
Type = HeaderFooterValues.Default,
Id = footerPartId
});
// Produces XML:
//
//
//
//
```
### 3.3 Different First Page (TitlePage)
```csharp
// =============================================================================
// DIFFERENT FIRST PAGE — TITLEPAGE
// =============================================================================
// To have a different header/footer on the first page, you must:
// 1. Add a TitlePage element to SectionProperties
// 2. Create separate HeaderPart/FooterPart for the first page
// 3. Add HeaderReference/FooterReference with Type = First
// Enable different first page
sectPr.AppendChild(new TitlePage());
// Produces XML:
// Create first-page header (e.g., empty / no header on cover page)
var firstHeaderPart = mainPart.AddNewPart();
string firstHeaderId = mainPart.GetIdOfPart(firstHeaderPart);
firstHeaderPart.Header = new Header(new Paragraph()); // Empty header
firstHeaderPart.Header.Save();
// Create first-page footer
var firstFooterPart = mainPart.AddNewPart();
string firstFooterId = mainPart.GetIdOfPart(firstFooterPart);
firstFooterPart.Footer = new Footer(new Paragraph()); // Empty footer
firstFooterPart.Footer.Save();
// Link to section
sectPr.AppendChild(new HeaderReference
{
Type = HeaderFooterValues.First,
Id = firstHeaderId
});
sectPr.AppendChild(new FooterReference
{
Type = HeaderFooterValues.First,
Id = firstFooterId
});
```
### 3.4 Even and Odd Page Headers
```csharp
// =============================================================================
// EVEN AND ODD PAGE HEADERS/FOOTERS
// =============================================================================
// For different headers on even vs. odd pages (e.g., book-style layout):
// 1. Set EvenAndOddHeaders in DocumentSettingsPart
// 2. Create separate parts for Even pages
// 3. "Default" type becomes the ODD page header/footer
// Enable even/odd in Settings
var settingsPart = mainPart.DocumentSettingsPart
?? mainPart.AddNewPart();
if (settingsPart.Settings == null)
settingsPart.Settings = new Settings();
settingsPart.Settings.AppendChild(new EvenAndOddHeaders());
settingsPart.Settings.Save();
// Produces XML in settings.xml:
// Create even-page header
var evenHeaderPart = mainPart.AddNewPart();
string evenHeaderId = mainPart.GetIdOfPart(evenHeaderPart);
evenHeaderPart.Header = new Header(
new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Left }),
new Run(new Text("Chapter Title")) // Left-aligned on even pages
)
);
evenHeaderPart.Header.Save();
// Link: "Default" = odd pages, "Even" = even pages
sectPr.AppendChild(new HeaderReference
{
Type = HeaderFooterValues.Even,
Id = evenHeaderId
});
// The existing Default header is now used only on odd pages.
```
### 3.5 SimpleField — PAGE, NUMPAGES, SECTIONPAGES
```csharp
// =============================================================================
// SIMPLE FIELDS — PAGE NUMBERS AND TOTALS
// =============================================================================
// SimpleField inserts a field code that Word evaluates at render time.
// Common field codes:
// PAGE — current page number
// NUMPAGES — total pages in document
// SECTIONPAGES — total pages in current section
// DATE — current date
// TIME — current time
//
// The Instruction property contains the field code string.
// A child Run with text is used for the "cached" display value (optional but
// recommended for non-Word renderers).
// --- Simple page number field ---
var pageField = new SimpleField { Instruction = " PAGE " };
pageField.AppendChild(new Run(new Text("1"))); // Cached display value
// Produces XML: 1
// --- Total page count ---
var totalField = new SimpleField { Instruction = " NUMPAGES " };
totalField.AppendChild(new Run(new Text("1")));
// --- Section page count ---
var sectionPagesField = new SimpleField { Instruction = " SECTIONPAGES " };
sectionPagesField.AppendChild(new Run(new Text("1")));
```
### 3.6 "Page X of Y" Footer
```csharp
// =============================================================================
// "PAGE X OF Y" FOOTER
// =============================================================================
// Combines text runs with SimpleField elements in a single paragraph.
var pageXofYFooter = mainPart.AddNewPart();
string pageXofYId = mainPart.GetIdOfPart(pageXofYFooter);
var footerPara = new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center })
);
// "Page "
footerPara.AppendChild(new Run(
new RunProperties(new FontSize { Val = "18" }), // 9pt
new Text("Page ") { Space = SpaceProcessingModeValues.Preserve }
));
// PAGE field
var pgField = new SimpleField { Instruction = " PAGE " };
pgField.AppendChild(new Run(
new RunProperties(new FontSize { Val = "18" }),
new Text("1")));
footerPara.AppendChild(pgField);
// " of "
footerPara.AppendChild(new Run(
new RunProperties(new FontSize { Val = "18" }),
new Text(" of ") { Space = SpaceProcessingModeValues.Preserve }
));
// NUMPAGES field
var npField = new SimpleField { Instruction = " NUMPAGES " };
npField.AppendChild(new Run(
new RunProperties(new FontSize { Val = "18" }),
new Text("1")));
footerPara.AppendChild(npField);
pageXofYFooter.Footer = new Footer(footerPara);
pageXofYFooter.Footer.Save();
// Link to section
sectPr.AppendChild(new FooterReference
{
Type = HeaderFooterValues.Default,
Id = pageXofYId
});
```
### 3.7 Header with Logo Image
```csharp
// =============================================================================
// HEADER WITH LOGO IMAGE
// =============================================================================
// To add an image to a header:
// 1. Add an ImagePart to the HeaderPart (not MainDocumentPart)
// 2. Build the Drawing/Inline/Graphic elements in the header paragraph
// 3. Image sizing uses EMUs (English Metric Units): 1 inch = 914400 EMU
var logoHeaderPart = mainPart.AddNewPart();
string logoHeaderId = mainPart.GetIdOfPart(logoHeaderPart);
// Add image to header part
var imagePart = logoHeaderPart.AddImagePart(ImagePartType.Png);
using (var stream = File.OpenRead("logo.png"))
{
imagePart.FeedData(stream);
}
string imageRelId = logoHeaderPart.GetIdOfPart(imagePart);
// Image dimensions in EMU (e.g., 1.5 inch wide × 0.5 inch tall)
long widthEmu = 1371600; // 1.5 * 914400
long heightEmu = 457200; // 0.5 * 914400
// Build the inline drawing element
var drawing = new Drawing(
new DW.Inline(
new DW.Extent { Cx = widthEmu, Cy = heightEmu },
new DW.EffectExtent { LeftEdge = 0L, TopEdge = 0L, RightEdge = 0L, BottomEdge = 0L },
new DW.DocProperties { Id = 1U, Name = "Logo" },
new DW.NonVisualGraphicFrameDrawingProperties(
new A.GraphicFrameLocks { NoChangeAspect = true }),
new A.Graphic(
new A.GraphicData(
new PIC.Picture(
new PIC.NonVisualPictureProperties(
new PIC.NonVisualDrawingProperties { Id = 1U, Name = "logo.png" },
new PIC.NonVisualPictureDrawingProperties()),
new PIC.BlipFill(
new A.Blip { Embed = imageRelId },
new A.Stretch(new A.FillRectangle())),
new PIC.ShapeProperties(
new A.Transform2D(
new A.Offset { X = 0L, Y = 0L },
new A.Extents { Cx = widthEmu, Cy = heightEmu }),
new A.PresetGeometry(new A.AdjustValueList())
{ Preset = A.ShapeTypeValues.Rectangle })
)
) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" }
)
)
{
DistanceFromTop = 0U,
DistanceFromBottom = 0U,
DistanceFromLeft = 0U,
DistanceFromRight = 0U
}
);
logoHeaderPart.Header = new Header(
new Paragraph(new Run(drawing))
);
logoHeaderPart.Header.Save();
```
### 3.8 Table-Layout Header (Logo Left, Text Center, Page Right)
```csharp
// =============================================================================
// TABLE-LAYOUT HEADER
// =============================================================================
// A common professional header pattern: 3-column invisible table
// Left cell: Company logo
// Center cell: Document title
// Right cell: Page number
// The table has no borders, giving a clean three-zone layout.
var tblHeaderPart = mainPart.AddNewPart();
string tblHeaderId = mainPart.GetIdOfPart(tblHeaderPart);
// Content width for Letter with 1" margins = 9360 DXA
var headerTable = new Table(
new TableProperties(
new TableWidth { Width = "5000", Type = TableWidthUnitValues.Pct },
new TableLayout { Type = TableLayoutValues.Fixed },
new TableBorders(
new TopBorder { Val = BorderValues.None },
new BottomBorder { Val = BorderValues.Single, Size = 4U, Color = "000000" },
new LeftBorder { Val = BorderValues.None },
new RightBorder { Val = BorderValues.None },
new InsideHorizontalBorder { Val = BorderValues.None },
new InsideVerticalBorder { Val = BorderValues.None }
)
),
new TableGrid(
new GridColumn { Width = "3120" }, // ~1/3
new GridColumn { Width = "3120" }, // ~1/3
new GridColumn { Width = "3120" } // ~1/3
)
);
// Left cell: logo placeholder (or image Drawing)
var leftCell = new TableCell(
new TableCellProperties(
new TableCellWidth { Width = "3120", Type = TableWidthUnitValues.Dxa },
new TableCellVerticalAlignment { Val = TableVerticalAlignmentValues.Center }),
new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Left }),
new Run(
new RunProperties(new Bold(), new FontSize { Val = "18" }),
new Text("ACME Corp")))
);
// Center cell: document title
var centerCell = new TableCell(
new TableCellProperties(
new TableCellWidth { Width = "3120", Type = TableWidthUnitValues.Dxa },
new TableCellVerticalAlignment { Val = TableVerticalAlignmentValues.Center }),
new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center }),
new Run(
new RunProperties(new FontSize { Val = "18" }),
new Text("Technical Specification v2.0")))
);
// Right cell: page number
var pageFieldRight = new SimpleField { Instruction = " PAGE " };
pageFieldRight.AppendChild(new Run(
new RunProperties(new FontSize { Val = "18" }),
new Text("1")));
var rightCell = new TableCell(
new TableCellProperties(
new TableCellWidth { Width = "3120", Type = TableWidthUnitValues.Dxa },
new TableCellVerticalAlignment { Val = TableVerticalAlignmentValues.Center }),
new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Right }),
new Run(
new RunProperties(new FontSize { Val = "18" }),
new Text("Page ") { Space = SpaceProcessingModeValues.Preserve }),
pageFieldRight)
);
headerTable.AppendChild(new TableRow(leftCell, centerCell, rightCell));
tblHeaderPart.Header = new Header(headerTable, new Paragraph());
tblHeaderPart.Header.Save();
```
### 3.9 Chinese Government Document Page Numbers (公文页码)
```csharp
// =============================================================================
// CHINESE GOVERNMENT DOCUMENT PAGE NUMBERS (公文页码)
// =============================================================================
// Standard: bottom center, format "-X-" (em-dash surrounding page number)
// Font: 宋体 (SimSun) 四号 (14pt = "28" half-points)
// Per GB/T 9704-2012
var govFooterPart = mainPart.AddNewPart();
string govFooterId = mainPart.GetIdOfPart(govFooterPart);
var govFooterPara = new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center })
);
// Run properties for 宋体四号
RunProperties GovPageRunProps() => new RunProperties(
new RunFonts
{
Ascii = "SimSun",
HighAnsi = "SimSun",
EastAsia = "SimSun"
},
new FontSize { Val = "28" }, // 14pt = 四号
new FontSizeComplexScript { Val = "28" }
);
// "—" (em-dash before page number)
govFooterPara.AppendChild(new Run(
GovPageRunProps(),
new Text("\u2014") { Space = SpaceProcessingModeValues.Preserve }
));
// PAGE field
var govPageField = new SimpleField { Instruction = " PAGE " };
govPageField.AppendChild(new Run(GovPageRunProps(), new Text("1")));
govFooterPara.AppendChild(govPageField);
// "—" (em-dash after page number)
govFooterPara.AppendChild(new Run(
GovPageRunProps(),
new Text("\u2014") { Space = SpaceProcessingModeValues.Preserve }
));
govFooterPart.Footer = new Footer(govFooterPara);
govFooterPart.Footer.Save();
// Link to section
sectPr.AppendChild(new FooterReference
{
Type = HeaderFooterValues.Default,
Id = govFooterId
});
```
### 3.10 Multi-Section with Different Headers
```csharp
// =============================================================================
// MULTI-SECTION WITH DIFFERENT HEADERS
// =============================================================================
// Each section can have its own set of header/footer parts.
// To change headers mid-document, create a section break and attach
// different HeaderReference/FooterReference to each SectionProperties.
// See Section 4 for full section break mechanics.
// Section 1 header
var sec1Header = mainPart.AddNewPart();
string sec1HeaderId = mainPart.GetIdOfPart(sec1Header);
sec1Header.Header = new Header(
new Paragraph(new Run(new Text("Chapter 1: Introduction"))));
sec1Header.Header.Save();
// Section 2 header
var sec2Header = mainPart.AddNewPart();
string sec2HeaderId = mainPart.GetIdOfPart(sec2Header);
sec2Header.Header = new Header(
new Paragraph(new Run(new Text("Chapter 2: Methods"))));
sec2Header.Header.Save();
// Section 1 content + section break
body.AppendChild(new Paragraph(new Run(new Text("Section 1 content..."))));
// Mid-document section properties (in last paragraph of section 1)
var sec1Break = new Paragraph(
new ParagraphProperties(
new SectionProperties(
new PageSize { Width = 12240U, Height = 15840U },
new PageMargin
{
Top = 1440, Bottom = 1440,
Left = 1440U, Right = 1440U,
Header = 720U, Footer = 720U, Gutter = 0U
},
new SectionType { Val = SectionMarkValues.NextPage },
new HeaderReference { Type = HeaderFooterValues.Default, Id = sec1HeaderId }
)
)
);
body.AppendChild(sec1Break);
// Section 2 content
body.AppendChild(new Paragraph(new Run(new Text("Section 2 content..."))));
// Final section properties (last child of Body)
var sec2Props = new SectionProperties(
new PageSize { Width = 12240U, Height = 15840U },
new PageMargin
{
Top = 1440, Bottom = 1440,
Left = 1440U, Right = 1440U,
Header = 720U, Footer = 720U, Gutter = 0U
},
new HeaderReference { Type = HeaderFooterValues.Default, Id = sec2HeaderId }
);
body.AppendChild(sec2Props);
```
---
## 4. Section Breaks & Multi-Section
### 4.1 Section Properties Placement Rules
```csharp
// =============================================================================
// SECTION PROPERTIES PLACEMENT
// =============================================================================
// There are exactly TWO places SectionProperties can appear:
//
// 1. FINAL SECTION: As the last child element of .
// This controls the last (or only) section of the document.
// body.AppendChild(new SectionProperties(...));
//
// 2. MID-DOCUMENT SECTION BREAK: Inside ParagraphProperties of the
// LAST paragraph of a section. This paragraph acts as the section divider.
// The paragraph's text content belongs to the PREVIOUS section.
//
// IMPORTANT: Mid-document SectionProperties goes inside pPr, NOT as a child
// of Body. This is the #1 mistake when creating multi-section documents.
// --- Final section (last child of Body) ---
var finalSectPr = new SectionProperties(
new PageSize { Width = 12240U, Height = 15840U },
new PageMargin
{
Top = 1440, Bottom = 1440,
Left = 1440U, Right = 1440U,
Header = 720U, Footer = 720U, Gutter = 0U
}
);
body.AppendChild(finalSectPr); // MUST be last child
// --- Mid-document section break ---
// This paragraph ends section 1 and starts section 2
var sectionBreakPara = new Paragraph(
new ParagraphProperties(
new SectionProperties(
new PageSize { Width = 12240U, Height = 15840U },
new PageMargin
{
Top = 1440, Bottom = 1440,
Left = 1440U, Right = 1440U,
Header = 720U, Footer = 720U, Gutter = 0U
},
new SectionType { Val = SectionMarkValues.NextPage }
)
)
);
// Produces XML:
//
//
//
//
//
//
//
//
//
```
### 4.2 SectionType Values
```csharp
// =============================================================================
// SECTION TYPE VALUES
// =============================================================================
// SectionType controls how the section break behaves:
//
// NextPage — new section starts on next page (most common)
// Continuous — new section starts on same page (used for column changes)
// EvenPage — new section starts on next even page (inserts blank if needed)
// OddPage — new section starts on next odd page (inserts blank if needed)
//
// If SectionType is omitted, the default is NextPage.
// Next page break (explicit)
var nextPageBreak = new SectionType { Val = SectionMarkValues.NextPage };
// Continuous — same page (e.g., switch from 1-column to 2-column)
var continuousBreak = new SectionType { Val = SectionMarkValues.Continuous };
// Even page — for chapters that must start on left page (in book layout)
var evenPageBreak = new SectionType { Val = SectionMarkValues.EvenPage };
// Odd page — for chapters that must start on right page
var oddPageBreak = new SectionType { Val = SectionMarkValues.OddPage };
```
### 4.3 Per-Section Page Setup
```csharp
// =============================================================================
// PER-SECTION PAGE SETUP
// =============================================================================
// Each section independently controls its own:
// - PageSize (portrait vs landscape, paper size)
// - PageMargin
// - Columns
// - Headers/Footers
// - PageNumberType (restart numbering)
// - LineNumbering
// - DocGrid
// Example: Section with landscape A4 and narrow margins
var landscapeSection = new SectionProperties(
new PageSize
{
Width = 16838U, // A4 long edge (landscape: swap W/H)
Height = 11906U, // A4 short edge
Orient = PageOrientationValues.Landscape
},
new PageMargin
{
Top = 720, Bottom = 720,
Left = 720U, Right = 720U,
Header = 720U, Footer = 720U, Gutter = 0U
},
new SectionType { Val = SectionMarkValues.NextPage }
);
```
### 4.4 PageNumberType — Restart Numbering
```csharp
// =============================================================================
// PAGE NUMBER TYPE — RESTART AND FORMAT
// =============================================================================
// PageNumberType controls page numbering within a section.
// Start — starting page number (restarts counting)
// Format — numbering format (Decimal, UpperRoman, LowerRoman,
// UpperLetter, LowerLetter)
//
// Common pattern: front matter uses i, ii, iii; body restarts at 1.
// Restart at page 1 (e.g., after front matter)
var restartNumbering = new PageNumberType
{
Start = 1,
Format = NumberFormatValues.Decimal
};
// Produces XML:
// Roman numeral numbering for front matter
var romanNumbering = new PageNumberType
{
Start = 1,
Format = NumberFormatValues.LowerRoman
};
// Produces XML:
// Add to section properties
landscapeSection.AppendChild(restartNumbering);
```
### 4.5 Complete Example: Portrait, Landscape, Portrait
```csharp
// =============================================================================
// COMPLETE MULTI-SECTION EXAMPLE
// =============================================================================
// Document with three sections:
// Section 1: Letter portrait (cover + intro)
// Section 2: Letter landscape (wide table)
// Section 3: Letter portrait (conclusion), page numbers restart at 1
//
// Each section has its own header.
using var doc = WordprocessingDocument.Create(
"MultiSection.docx", WordprocessingDocumentType.Document);
var mainPart = doc.MainDocumentPart!;
var body = mainPart.Document.Body!;
// --- Create headers for each section ---
HeaderPart CreateSimpleHeader(string text)
{
var hp = mainPart.AddNewPart();
hp.Header = new Header(
new Paragraph(
new ParagraphProperties(
new ParagraphBorders(
new BottomBorder
{
Val = BorderValues.Single, Size = 4U,
Color = "999999", Space = 1U
}),
new Justification { Val = JustificationValues.Right }),
new Run(
new RunProperties(
new FontSize { Val = "18" },
new Color { Val = "999999" }),
new Text(text)))
);
hp.Header.Save();
return hp;
}
var header1 = CreateSimpleHeader("Introduction");
var header2 = CreateSimpleHeader("Data Tables");
var header3 = CreateSimpleHeader("Conclusion");
// --- Create "Page X of Y" footer (shared) ---
var sharedFooter = mainPart.AddNewPart();
var fPara = new Paragraph(
new ParagraphProperties(new Justification { Val = JustificationValues.Center }));
fPara.AppendChild(new Run(
new RunProperties(new FontSize { Val = "18" }),
new Text("Page ") { Space = SpaceProcessingModeValues.Preserve }));
var pf = new SimpleField { Instruction = " PAGE " };
pf.AppendChild(new Run(new RunProperties(new FontSize { Val = "18" }), new Text("1")));
fPara.AppendChild(pf);
fPara.AppendChild(new Run(
new RunProperties(new FontSize { Val = "18" }),
new Text(" of ") { Space = SpaceProcessingModeValues.Preserve }));
var npf = new SimpleField { Instruction = " NUMPAGES " };
npf.AppendChild(new Run(new RunProperties(new FontSize { Val = "18" }), new Text("1")));
fPara.AppendChild(npf);
sharedFooter.Footer = new Footer(fPara);
sharedFooter.Footer.Save();
string sharedFooterId = mainPart.GetIdOfPart(sharedFooter);
// =================== SECTION 1: Portrait ===================
body.AppendChild(new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center }),
new Run(
new RunProperties(new Bold(), new FontSize { Val = "48" }),
new Text("Annual Report 2025"))));
body.AppendChild(new Paragraph(new Run(new Text("Introduction content goes here..."))));
// Section 1 break (inside pPr of last paragraph)
body.AppendChild(new Paragraph(
new ParagraphProperties(
new SectionProperties(
new PageSize { Width = 12240U, Height = 15840U },
new PageMargin
{
Top = 1440, Bottom = 1440,
Left = 1440U, Right = 1440U,
Header = 720U, Footer = 720U, Gutter = 0U
},
new SectionType { Val = SectionMarkValues.NextPage },
new HeaderReference
{
Type = HeaderFooterValues.Default,
Id = mainPart.GetIdOfPart(header1)
},
new FooterReference
{
Type = HeaderFooterValues.Default,
Id = sharedFooterId
}
)
)
));
// =================== SECTION 2: Landscape ===================
body.AppendChild(new Paragraph(new Run(
new RunProperties(new Bold(), new FontSize { Val = "28" }),
new Text("Wide Data Table"))));
body.AppendChild(new Paragraph(new Run(
new Text("This section uses landscape orientation for a wide table."))));
// Section 2 break
body.AppendChild(new Paragraph(
new ParagraphProperties(
new SectionProperties(
new PageSize
{
Width = 15840U, // SWAPPED for landscape
Height = 12240U, // SWAPPED for landscape
Orient = PageOrientationValues.Landscape
},
new PageMargin
{
Top = 1440, Bottom = 1440,
Left = 1440U, Right = 1440U,
Header = 720U, Footer = 720U, Gutter = 0U
},
new SectionType { Val = SectionMarkValues.NextPage },
new HeaderReference
{
Type = HeaderFooterValues.Default,
Id = mainPart.GetIdOfPart(header2)
},
new FooterReference
{
Type = HeaderFooterValues.Default,
Id = sharedFooterId
}
)
)
));
// =================== SECTION 3: Portrait (restart page numbers) ===================
body.AppendChild(new Paragraph(new Run(
new RunProperties(new Bold(), new FontSize { Val = "28" }),
new Text("Conclusion"))));
body.AppendChild(new Paragraph(new Run(
new Text("Final section content with restarted page numbering."))));
// Final section properties — last child of Body
body.AppendChild(new SectionProperties(
new PageSize { Width = 12240U, Height = 15840U },
new PageMargin
{
Top = 1440, Bottom = 1440,
Left = 1440U, Right = 1440U,
Header = 720U, Footer = 720U, Gutter = 0U
},
new PageNumberType
{
Start = 1, // Restart at page 1
Format = NumberFormatValues.Decimal
},
new HeaderReference
{
Type = HeaderFooterValues.Default,
Id = mainPart.GetIdOfPart(header3)
},
new FooterReference
{
Type = HeaderFooterValues.Default,
Id = sharedFooterId
}
));
mainPart.Document.Save();
```
---
## 5. Document Properties
### 5.1 CoreFilePropertiesPart (Dublin Core Metadata)
```csharp
// =============================================================================
// CORE FILE PROPERTIES (DUBLIN CORE)
// =============================================================================
// Core properties map to the Dublin Core metadata standard.
// They appear in File > Properties > Summary in Word.
// The underlying XML is stored in docProps/core.xml.
//
// Available properties: Title, Subject, Creator (Author), Keywords,
// Description, LastModifiedBy, Revision, Created, Modified, Category,
// ContentStatus, ContentType, Identifier, Language, Version
using var doc = WordprocessingDocument.Create(
"PropertiesDemo.docx", WordprocessingDocumentType.Document);
// Core properties are accessed via the package-level properties
// Use the OpenXml Packaging API:
var corePart = doc.CoreFilePropertiesPart
?? doc.AddCoreFilePropertiesPart();
// The core properties use the OpenXmlPart's XML directly.
// You can set properties via the PackageProperties interface:
doc.PackageProperties.Title = "Quarterly Financial Report";
doc.PackageProperties.Subject = "Q4 2025 Financial Summary";
doc.PackageProperties.Creator = "Jane Smith";
doc.PackageProperties.Keywords = "finance, quarterly, report, 2025";
doc.PackageProperties.Description = "Comprehensive financial report for Q4 2025";
doc.PackageProperties.LastModifiedBy = "John Doe";
doc.PackageProperties.Revision = "3";
doc.PackageProperties.Created = DateTime.UtcNow;
doc.PackageProperties.Modified = DateTime.UtcNow;
doc.PackageProperties.Category = "Financial Reports";
doc.PackageProperties.ContentStatus = "Final";
doc.PackageProperties.Language = "en-US";
doc.PackageProperties.Version = "2.0";
// Produces docProps/core.xml:
//
// Quarterly Financial Report
// Q4 2025 Financial Summary
// Jane Smith
// finance, quarterly, report, 2025
// Comprehensive financial report...
// John Doe
// 3
// 2025-12-01T00:00:00Z
// 2025-12-01T00:00:00Z
// Financial Reports
// Final
//
```
### 5.2 ExtendedFilePropertiesPart (Application Properties)
```csharp
// =============================================================================
// EXTENDED FILE PROPERTIES (APPLICATION PROPERTIES)
// =============================================================================
// Extended properties are stored in docProps/app.xml.
// They include application-specific metadata like Company, Manager, etc.
using DocumentFormat.OpenXml.ExtendedProperties;
var extPart = doc.ExtendedFilePropertiesPart
?? doc.AddExtendedFilePropertiesPart();
extPart.Properties = new DocumentFormat.OpenXml.ExtendedProperties.Properties();
// Company name
extPart.Properties.AppendChild(
new DocumentFormat.OpenXml.ExtendedProperties.Company("ACME Corporation"));
// Manager
extPart.Properties.AppendChild(
new DocumentFormat.OpenXml.ExtendedProperties.Manager("Alice Johnson"));
// Application name
extPart.Properties.AppendChild(
new DocumentFormat.OpenXml.ExtendedProperties.Application("Custom Document Generator"));
// Application version
extPart.Properties.AppendChild(
new DocumentFormat.OpenXml.ExtendedProperties.ApplicationVersion("1.0.0"));
// Total editing time in minutes
extPart.Properties.AppendChild(
new DocumentFormat.OpenXml.ExtendedProperties.TotalTime("0"));
// Pages, Words, Characters (these are hints; Word recalculates them)
extPart.Properties.AppendChild(
new DocumentFormat.OpenXml.ExtendedProperties.Pages("1"));
extPart.Properties.AppendChild(
new DocumentFormat.OpenXml.ExtendedProperties.Words("0"));
extPart.Properties.AppendChild(
new DocumentFormat.OpenXml.ExtendedProperties.Characters("0"));
extPart.Properties.Save();
// Produces docProps/app.xml:
//
// ACME Corporation
// Alice Johnson
// Custom Document Generator
// 1.0.0
// 0
// 1
//
```
### 5.3 CustomFilePropertiesPart (Custom Key-Value Properties)
```csharp
// =============================================================================
// CUSTOM FILE PROPERTIES (KEY-VALUE PAIRS)
// =============================================================================
// Custom properties allow arbitrary key-value metadata.
// Stored in docProps/custom.xml. Useful for document management systems,
// workflow tracking, template variables, etc.
//
// Supported value types: Text (string), Number (int), Date (DateTime),
// Boolean (bool), Filetime
using DocumentFormat.OpenXml.CustomProperties;
using DocumentFormat.OpenXml.VariantTypes;
var customPart = doc.CustomFilePropertiesPart
?? doc.AddCustomFilePropertiesPart();
customPart.Properties = new DocumentFormat.OpenXml.CustomProperties.Properties();
// Property IDs must start at 2 and increment
int propertyId = 2;
// --- String property ---
customPart.Properties.AppendChild(new CustomDocumentProperty
{
FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}",
PropertyId = propertyId++,
Name = "Department",
VTLPWSTR = new VTLPWSTR("Engineering")
});
// --- Integer property ---
customPart.Properties.AppendChild(new CustomDocumentProperty
{
FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}",
PropertyId = propertyId++,
Name = "DocumentVersion",
VTInt32 = new VTInt32("5")
});
// --- Boolean property ---
customPart.Properties.AppendChild(new CustomDocumentProperty
{
FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}",
PropertyId = propertyId++,
Name = "Approved",
VTBool = new VTBool("true")
});
// --- DateTime property ---
customPart.Properties.AppendChild(new CustomDocumentProperty
{
FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}",
PropertyId = propertyId++,
Name = "ApprovalDate",
VTFileTime = new VTFileTime(DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"))
});
// --- Double/Float property ---
customPart.Properties.AppendChild(new CustomDocumentProperty
{
FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}",
PropertyId = propertyId++,
Name = "ConfidenceScore",
VTDouble = new VTDouble("0.95")
});
customPart.Properties.Save();
// Produces docProps/custom.xml:
//
//
// Engineering
//
//
// 5
//
//
// true
//
// ...
//
```
### 5.4 Reading and Modifying Existing Properties
```csharp
// =============================================================================
// READING AND MODIFYING EXISTING PROPERTIES
// =============================================================================
using var existingDoc = WordprocessingDocument.Open("existing.docx", isEditable: true);
// --- Read core properties ---
string? title = existingDoc.PackageProperties.Title;
string? author = existingDoc.PackageProperties.Creator;
DateTime? modified = existingDoc.PackageProperties.Modified;
// --- Modify core properties ---
existingDoc.PackageProperties.Title = "Updated Title";
existingDoc.PackageProperties.Modified = DateTime.UtcNow;
// --- Read custom properties ---
var customProps = existingDoc.CustomFilePropertiesPart?.Properties;
if (customProps != null)
{
foreach (var prop in customProps.Elements())
{
string name = prop.Name!;
// Value is in one of the VT* child elements
string? textValue = prop.VTLPWSTR?.Text;
string? intValue = prop.VTInt32?.Text;
string? boolValue = prop.VTBool?.Text;
// Use whichever is non-null
}
}
// --- Update a specific custom property ---
var deptProp = customProps?.Elements()
.FirstOrDefault(p => p.Name == "Department");
if (deptProp != null)
{
deptProp.VTLPWSTR = new VTLPWSTR("Marketing");
customProps!.Save();
}
```
---
## 6. Printing & Compatibility Settings
### 6.1 DocumentSettingsPart — Zoom, TabStop, ProofState
```csharp
// =============================================================================
// DOCUMENT SETTINGS — ZOOM, TAB STOPS, PROOF STATE
// =============================================================================
// DocumentSettingsPart (settings.xml) contains document-wide settings.
// These control behavior in Word's UI and rendering.
var mainPart2 = doc.MainDocumentPart!;
var settingsPart2 = mainPart2.DocumentSettingsPart
?? mainPart2.AddNewPart();
settingsPart2.Settings ??= new Settings();
var settings = settingsPart2.Settings;
// --- Zoom level ---
// Val = percentage (100 = 100%). Percent is a string.
settings.AppendChild(new Zoom
{
Percent = "120", // 120% zoom
Val = PresetZoomValues.BestFit // Or: None, FullPage, BestFit, TextFit
});
// Produces XML:
// --- Default Tab Stop ---
// Distance in DXA for default tab stops. Standard is 720 (0.5 inch).
settings.AppendChild(new DefaultTabStop
{
Val = 720 // 0.5 inch
});
// Produces XML:
// For Chinese documents, common default tab stop is 420 (about 2 characters wide)
// settings.AppendChild(new DefaultTabStop { Val = 420 });
// --- Proof State ---
// Tells Word the spell/grammar check status. Setting both to "clean"
// prevents Word from showing the "Proofing" notification on open.
settings.AppendChild(new ProofState
{
Spelling = ProofingStateValues.Clean,
Grammar = ProofingStateValues.Clean
});
// Produces XML:
// --- Character Spacing Control ---
// CompressWhitespace: whether to compress whitespace at line edges (CJK)
settings.AppendChild(new CharacterSpacingControl
{
Val = CharacterSpacingValues.DoNotCompress
});
// --- Remove personal information on save ---
settings.AppendChild(new RemovePersonalInformation());
// Produces XML:
settings.Save();
```
### 6.2 Compatibility Settings
```csharp
// =============================================================================
// COMPATIBILITY SETTINGS
// =============================================================================
// Compatibility settings control how Word renders the document,
// providing backward compatibility with older Word versions.
// CompatibilityMode is the most important setting.
var compat = new Compatibility();
// --- Compatibility Mode ---
// 15 = Word 2013+ mode (current standard)
// 14 = Word 2010 mode
// 12 = Word 2007 mode
// Omitted or 0 = oldest compatibility
compat.AppendChild(new CompatibilitySetting
{
Name = CompatSettingNameValues.CompatibilityMode,
Uri = "http://schemas.microsoft.com/office/word",
Val = "15" // Word 2013+ mode
});
// --- Override page break rules ---
// Useful compatibility flags for consistent rendering:
// UseWord2002TableStyleRules: use newer table style rules
compat.AppendChild(new CompatibilitySetting
{
Name = CompatSettingNameValues.OverrideTableStyleFontSizeAndJustification,
Uri = "http://schemas.microsoft.com/office/word",
Val = "1"
});
// EnableOpenTypeFeatures: enable advanced typography
compat.AppendChild(new CompatibilitySetting
{
Name = CompatSettingNameValues.EnableOpenTypeFeatures,
Uri = "http://schemas.microsoft.com/office/word",
Val = "1"
});
// DifferentiateMultirowTableHeaders
compat.AppendChild(new CompatibilitySetting
{
Name = CompatSettingNameValues.DifferentiateMultirowTableHeaders,
Uri = "http://schemas.microsoft.com/office/word",
Val = "1"
});
// UseWord2013TrackBottomHyphenation
compat.AppendChild(new CompatibilitySetting
{
Name = CompatSettingNameValues.UseWord2013TrackBottomHyphenation,
Uri = "http://schemas.microsoft.com/office/word",
Val = "0"
});
settings.AppendChild(compat);
settings.Save();
// Produces XML:
//
//
//
// ...
//
```
### 6.3 MirrorMargins, GutterAtTop, BookFoldPrinting
```csharp
// =============================================================================
// MIRROR MARGINS, GUTTER, BOOK FOLD
// =============================================================================
// These settings are for documents intended for double-sided printing or
// book binding.
// --- Mirror Margins ---
// When enabled, Left/Right margins become Inside/Outside.
// On even pages, margins are swapped so the binding edge stays consistent.
settings.AppendChild(new MirrorMargins());
// Produces XML:
// After this, PageMargin.Left = inside margin, PageMargin.Right = outside margin.
// Even pages automatically flip them.
// --- Gutter at Top ---
// By default, gutter is added to the left (or inside with MirrorMargins).
// GutterAtTop moves the gutter to the top margin instead.
// Used for calendar-style or top-bound documents.
settings.AppendChild(new GutterAtTop());
// Produces XML:
// --- Book Fold Printing ---
// Enables booklet printing (2 pages per sheet, folded in half).
// Word reorders pages for saddle-stitch binding.
settings.AppendChild(new BookFoldPrinting());
// Produces XML:
// Optional: sheets per booklet (for thick documents split into signatures)
// Default 0 = all pages in one booklet
settings.AppendChild(new BookFoldPrintingSheets { Val = 16 });
// Produces XML:
// 16 sheets = 64 pages per booklet signature
// --- Book Fold Reversed ---
// For right-to-left booklets (Hebrew, Arabic, Japanese right-bound)
// settings.AppendChild(new BookFoldRevPrinting());
settings.Save();
```
### 6.4 Additional Document Settings
```csharp
// =============================================================================
// ADDITIONAL DOCUMENT SETTINGS
// =============================================================================
// --- Update Fields on Open ---
// Forces Word to recalculate all fields (TOC, page numbers, etc.) on open.
settings.AppendChild(new UpdateFieldsOnOpen { Val = true });
// Produces XML:
// WARNING: This causes a dialog popup in some Word versions.
// --- Do Not Track Moves ---
// Prevents move tracking in track changes (shows as delete + insert instead).
// settings.AppendChild(new DoNotTrackMoves());
// --- Do Not Track Formatting ---
// Prevents formatting changes from being tracked.
// settings.AppendChild(new DoNotTrackFormatting());
// --- Default Zoom for Print Preview ---
// PrintTwoOnOne: print 2 pages per sheet
// settings.AppendChild(new PrintTwoOnOne());
// --- Document protection (read-only, forms-only, etc.) ---
// DocumentProtection can enforce read-only, allow only comments,
// restrict to form fields, etc.
// settings.AppendChild(new DocumentProtection
// {
// Edit = DocumentProtectionValues.ReadOnly,
// Enforcement = true
// });
// --- Even and Odd Headers (setting-level flag) ---
// Must be set here for the EvenPage header/footer references to work.
// settings.AppendChild(new EvenAndOddHeaders());
settings.Save();
```
### 6.5 Complete Settings Setup
```csharp
// =============================================================================
// COMPLETE SETTINGS SETUP — PRODUCTION-READY
// =============================================================================
// A comprehensive settings configuration suitable for most documents.
static void ConfigureDocumentSettings(WordprocessingDocument doc)
{
var mainPart = doc.MainDocumentPart!;
var settingsPart = mainPart.DocumentSettingsPart
?? mainPart.AddNewPart();
settingsPart.Settings = new Settings();
var s = settingsPart.Settings;
// Zoom to 100%
s.AppendChild(new Zoom
{
Percent = "100",
Val = PresetZoomValues.None
});
// Default tab stop: 0.5 inch
s.AppendChild(new DefaultTabStop { Val = 720 });
// Mark spell/grammar as clean
s.AppendChild(new ProofState
{
Spelling = ProofingStateValues.Clean,
Grammar = ProofingStateValues.Clean
});
// Character spacing control
s.AppendChild(new CharacterSpacingControl
{
Val = CharacterSpacingValues.DoNotCompress
});
// Compatibility: Word 2013+ mode with useful features
var compat = new Compatibility();
foreach (var (name, val) in new[]
{
(CompatSettingNameValues.CompatibilityMode, "15"),
(CompatSettingNameValues.OverrideTableStyleFontSizeAndJustification, "1"),
(CompatSettingNameValues.EnableOpenTypeFeatures, "1"),
(CompatSettingNameValues.DifferentiateMultirowTableHeaders, "1"),
(CompatSettingNameValues.UseWord2013TrackBottomHyphenation, "0"),
})
{
compat.AppendChild(new CompatibilitySetting
{
Name = name,
Uri = "http://schemas.microsoft.com/office/word",
Val = val
});
}
s.AppendChild(compat);
s.Save();
}
// Usage:
ConfigureDocumentSettings(doc);
```
---
## Quick Reference: Unit Conversions
| Unit | Full Name | Relation |
|------|-----------|----------|
| DXA | Twentieths of a point | 1 inch = 1440 DXA; 1 cm = 567 DXA; 1 pt = 20 DXA |
| Half-point | Half a typographic point | Font size: 24 = 12pt. 1 pt = 2 half-points |
| Eighth-point | Eighth of a point | Border size: 8 = 1pt. 1 pt = 8 eighth-points |
| EMU | English Metric Unit | 1 inch = 914400 EMU; 1 cm = 360000 EMU; 1 pt = 12700 EMU |
| Pct (table width) | Fiftieths of a percent | 5000 = 100%. Multiply percentage by 50 |
## Quick Reference: Common PageSize Values
| Paper | Width (DXA) | Height (DXA) | Inches | mm |
|-------|-------------|--------------|--------|-----|
| Letter | 12240 | 15840 | 8.5 x 11 | 216 x 279 |
| A4 | 11906 | 16838 | -- | 210 x 297 |
| Legal | 12240 | 20160 | 8.5 x 14 | 216 x 356 |
| A3 | 16838 | 23811 | -- | 297 x 420 |
| B5 | 10318 | 14570 | -- | 182 x 257 |
## Quick Reference: Common Margin Presets (DXA)
| Preset | Top | Bottom | Left | Right |
|--------|-----|--------|------|-------|
| Normal | 1440 | 1440 | 1440 | 1440 |
| Narrow | 720 | 720 | 720 | 720 |
| Moderate | 1440 | 1440 | 1080 | 1080 |
| Wide | 1440 | 1440 | 2880 | 2880 |
| Chinese 公文 | 2098 | 1984 | 1588 | 1474 |