Files
skills/minimax-docx/references/openxml_encyclopedia_part1.md
shihao 6487becf60 Initial commit: add all skills files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 16:52:49 +08:00

4062 lines
137 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OpenXML SDK 3.x Complete Reference Encyclopedia
**Target:** DocumentFormat.OpenXml 3.x / .NET 8+ / C# 12
**Last Updated:** 2026-03-22
This document serves as an exhaustive reference for building DOCX files with the OpenXML SDK. Every code block is ready to copy-paste.
---
## Namespace Aliases Used Throughout
```csharp
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
```
---
## Table of Contents
1. [Document Creation Skeleton](#1-document-creation-skeleton)
2. [Style System Deep Dive](#2-style-system-deep-dive)
3. [Character Formatting (RunProperties)](#3-character-formatting-runproperties--exhaustive)
4. [Paragraph Formatting (ParagraphProperties)](#4-paragraph-formatting-paragraphproperties--exhaustive)
---
## 1. Document Creation Skeleton
### 1.1 Complete Flow: Create to Save
```csharp
// =============================================================================
// DOCUMENT CREATION SKELETON
// =============================================================================
// This is the minimal complete flow for creating a valid DOCX from scratch.
// Follow these steps in order: Create -> AddParts -> AddContent -> Save.
//
// Key insight: WordprocessingDocument.Create() adds MainDocumentPart automatically,
// but all other parts (Styles, Settings, Numbering, Theme) must be added manually.
// --- STEP 1: CREATE THE PACKAGE ---
// The file path can be absolute or relative. WordprocessingDocumentType.Document
// is the standard choice for .docx files (vs. Template, MacroEnabled, etc.)
string outputPath = "C:\\Docs\\MyDocument.docx";
using var doc = WordprocessingDocument.Create(
outputPath, // File path
WordprocessingDocumentType.Document, // Document type enum
new DocumentOptions // Optional: AutoSave, etc.
{
AutoSave = false // true = flush changes automatically
});
// --- STEP 2: GET OR CREATE THE MAIN DOCUMENT PART ---
// When you call Create(), MainDocumentPart is automatically created and linked.
// You access it via .MainDocumentPart (not .AddMainDocumentPart, which would add
// a SECOND main part — illegal). For a fresh document, just use .MainDocumentPart.
var mainPart = doc.MainDocumentPart!;
var body = mainPart.Document.Body!; // Body is created automatically with the part
// --- STEP 3: ADD ADDITIONAL PARTS ---
// These are OPTIONAL but recommended for a complete document:
// - StyleDefinitionsPart: required for styles
// - NumberingDefinitionsPart: required for bullets/numbers
// - DocumentSettingsPart: zoom, proof state, tab stops, compatibility
// - ThemePart: color/theme information
// Parts are created fresh and linked via relationships.
// Example: Add styles part (covered in Section 2)
var stylesPart = mainPart.AddNewPart<StyleDefinitionsPart>();
stylesPart.Styles = new Styles();
stylesPart.Styles.Save();
// Example: Add settings part (covered in 1.4)
var settingsPart = mainPart.AddNewPart<DocumentSettingsPart>();
settingsPart.Settings = new Settings();
settingsPart.Settings.Save();
// --- STEP 4: ADD CONTENT TO BODY ---
// Body accepts: Paragraph (w:p), Table (w:tbl), Structured Document Tag (w:sdt)
// Content is added in document order (no need for explicit index).
// IMPORTANT: SectionProperties (w:sectPr) MUST be the last child of body.
body.Append(new Paragraph(
new Run(new Text("Hello, World!"))));
// --- STEP 5: SET SECTION PROPERTIES (PAGE LAYOUT) ---
// sectPr defines page size, margins, headers/footers, columns, etc.
// It must be the last child of body. If missing, Word uses defaults (Letter/A4, 1" margins).
var sectPr = new SectionProperties();
// Page Size: Width/Height in DXA (1 inch = 1440 DXA)
// Letter: 12240 x 15840 DXA (8.5" x 11")
// A4: 11906 x 16838 DXA (210mm x 297mm)
sectPr.Append(new PageSize
{
Width = 12240u, // 8.5 inches
Height = 15840u // 11 inches
});
// Page Margins: all four margins in DXA
// Note: Top+Bottom margins + HeaderDistance = distance from page edge to text
sectPr.Append(new PageMargin
{
Top = 1440, // 1 inch
Bottom = 1440, // 1 inch
Left = 1440u, // 1 inch (uint required)
Right = 1440u, // 1 inch
Header = 720u, // 0.5 inch from page edge to header
Footer = 720u // 0.5 inch from page edge to footer
});
// Attach sectPr to body (must be last)
body.Append(sectPr);
// --- STEP 6: SAVE ---
// Because we use `using`, Dispose() is called automatically when the block exits.
// Dispose() saves the file. If you forget `using`, call doc.Save() explicitly.
```
### 1.2 Opening an Existing Document
```csharp
// =============================================================================
// OPENING EXISTING DOCUMENTS
// =============================================================================
// Open() has multiple overloads:
// 1. Open(string path, bool isEditable, AutoSave)
// 2. Open(Stream, bool isEditable, AutoSave)
// 3. Open(string path, bool isEditable, OpenSettings)
//
// isEditable=true means open for read/write. false = read-only.
// isEditable=false is faster (shared locks avoided) but throws if file is read-only.
// --- OPEN FOR EDITING (READ/WRITE) ---
string inputPath = "C:\\Docs\\Existing.docx";
using var editDoc = WordprocessingDocument.Open(
inputPath,
isEditable: true, // Required for modification
new OpenSettings
{
AutoSave = true // Automatically save on Dispose
});
var body = editDoc.MainDocumentPart!.Document.Body!;
// ... make changes ...
// No explicit Save() needed if AutoSave = true
// --- OPEN AS READ-ONLY (FASTER) ---
using var readOnlyDoc = WordprocessingDocument.Open(
inputPath,
isEditable: false, // Read-only mode
new OpenSettings
{
// MarkupDeclarationProcess options
});
// --- OPEN FROM STREAM ---
byte[] fileBytes = File.ReadAllBytes(inputPath);
using var streamDoc = WordprocessingDocument.Open(
new MemoryStream(fileBytes),
isEditable: true,
new OpenSettings { AutoSave = false });
// After editing, you MUST copy the stream back to file if AutoSave=false:
// streamDoc.MainDocumentPart.Document.Save();
// File.WriteAllBytes(outputPath, streamStream.ToArray());
// --- OPEN FROM HTTP RESPONSE (WEB SCENARIO) ---
using var httpClient = new HttpClient();
var response = await httpClient.GetAsync("https://example.com/document.docx");
using var webStream = await response.Content.ReadAsStreamAsync();
using var webDoc = WordprocessingDocument.Open(webStream, isEditable: true);
```
### 1.3 Stream-Based Creation (MemoryStream for Web)
```csharp
// =============================================================================
// STREAM-BASED DOCUMENT CREATION
// =============================================================================
// Use MemoryStream when you want to:
// 1. Generate a document in memory before sending to a client
// 2. Avoid touching the filesystem (ASP.NET Core scenarios)
// 3. Return a document from an API endpoint
//
// CRITICAL: The stream MUST be seekable when you call .Open().
// After WordprocessingDocument.Create(), the stream position is at the beginning.
// If you write to the stream BEFORE creating the document, seek to 0 first.
// --- CREATE IN MEMORY ---
MemoryStream memStream = new MemoryStream();
// Create directly on a stream (no file path involved)
using (var doc = WordprocessingDocument.Create(
memStream,
WordprocessingDocumentType.Document,
new DocumentOptions { AutoSave = false }))
{
var mainPart = doc.MainDocumentPart!;
mainPart.Document = new Document(new Body());
mainPart.Document.Body!.Append(new Paragraph(
new Run(new Text("Generated in memory"))));
mainPart.Document.Save(); // Save to the underlying stream
}
// At this point, memStream contains the complete DOCX
// --- SEND TO HTTP RESPONSE (ASP.NET Core) ---
// In an API controller:
[HttpGet("download")]
public async Task<IActionResult> DownloadDocument()
{
var memStream = new MemoryStream();
using (var doc = WordprocessingDocument.Create(
memStream,
WordprocessingDocumentType.Document))
{
var mainPart = doc.MainDocumentPart!;
mainPart.Document = new Document(new Body());
mainPart.Document.Body!.Append(new Paragraph(
new Run(new Text("Download me!"))));
mainPart.Document.Save();
}
memStream.Position = 0; // IMPORTANT: Reset position for reading
return File(memStream,
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"GeneratedDocument.docx");
}
// --- CREATE FROM TEMPLATE IN MEMORY ---
// Useful for mail-merge style operations
MemoryStream templateStream = new MemoryStream();
File.WriteAllBytes("template.docx", templateStream.ToArray()); // Save a template first
using var templateSource = new MemoryStream(File.ReadAllBytes("template.docx"));
using var mergedDoc = (WordprocessingDocument)templateSource.Clone();
// Clone() creates an editable copy. Don't forget to set position:
mergedDoc.MainDocumentPart!.Document.Body!.Append(new Paragraph(
new Run(new Text("Added content"))));
```
### 1.4 Adding All Standard Parts
```csharp
// =============================================================================
// ADDING ALL STANDARD DOCUMENT PARTS
// =============================================================================
// A complete document should have:
// 1. MainDocumentPart (auto-created)
// 2. StyleDefinitionsPart
// 3. NumberingDefinitionsPart
// 4. DocumentSettingsPart
// 5. ThemePart (optional)
// 6. Custom parts (headers, footers, comments, etc.)
// --- COMPLETE SETUP METHOD ---
public static void CreateCompleteDocument(string path)
{
using var doc = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document);
var mainPart = doc.MainDocumentPart!;
// Initialize document
mainPart.Document = new Document(new Body());
var body = mainPart.Document.Body!;
// Add all parts
AddStylesPart(mainPart);
AddNumberingPart(mainPart);
AddSettingsPart(mainPart);
AddThemePart(mainPart);
AddHeadersAndFooters(mainPart);
// Add sample content
AddSampleContent(body);
// Section properties MUST be last
body.Append(CreateSectionProperties());
mainPart.Document.Save();
}
// --- STYLES PART ---
// See Section 2 for detailed style creation
private static void AddStylesPart(MainDocumentPart mainPart)
{
var stylesPart = mainPart.AddNewPart<StyleDefinitionsPart>();
var styles = new Styles();
// DocDefaults: document-wide defaults for run and paragraph properties
// These apply when no explicit style or direct formatting overrides them
styles.Append(new DocDefaults(
new RunPropertiesDefault(
new RunPropertiesBaseStyle(
new RunFonts { Ascii = "Calibri", HighAnsi = "Calibri" },
new FontSize { Val = "22" }, // 22 half-points = 11pt
new FontSizeComplexScript { Val = "22" }
)
),
new ParagraphPropertiesDefault(
new ParagraphPropertiesBaseStyle(
new SpacingBetweenLines { After = "200", Line = "276", LineRule = LineSpacingRuleValues.Auto }
)
)
));
// Default Normal style
styles.Append(new Style(
new StyleName { Val = "Normal" },
new PrimaryStyle()
)
{ Type = StyleValues.Paragraph, StyleId = "Normal", Default = true });
stylesPart.Styles = styles;
stylesPart.Styles.Save();
}
// --- NUMBERING PART ---
// Required for bulleted and numbered lists
private static void AddNumberingPart(MainDocumentPart mainPart)
{
var numberingPart = mainPart.AddNewPart<NumberingDefinitionsPart>();
var numbering = new Numbering();
// AbstractNum defines the list format (bullet, number, multilevel)
// Creates a bullet list definition with 3 levels
var abstractNum = new AbstractNum { AbstractNumberId = 1 };
// Level 0: Bullet (dot)
abstractNum.Append(new Level(
new StartNumberingValue { Val = 1 },
new NumberingFormat { Val = NumberFormatValues.Bullet },
new LevelText { Val = "•" },
new LevelJustification { Val = LevelJustificationValues.Left },
new PreviousParagraphProperties(
new Indentation { Left = "720", Hanging = "360" }) // 720 DXA indent, 360 DXA hanging
)
{ LevelIndex = 0 });
// Level 1: Dash
abstractNum.Append(new Level(
new StartNumberingValue { Val = 1 },
new NumberingFormat { Val = NumberFormatValues.Bullet },
new LevelText { Val = "" },
new LevelJustification { Val = LevelJustificationValues.Left },
new PreviousParagraphProperties(
new Indentation { Left = "1440", Hanging = "360" })
)
{ LevelIndex = 1 });
// Level 2: Circle
abstractNum.Append(new Level(
new StartNumberingValue { Val = 1 },
new NumberingFormat { Val = NumberFormatValues.Bullet },
new LevelText { Val = "◦" },
new LevelJustification { Val = LevelJustificationValues.Left },
new PreviousParagraphProperties(
new Indentation { Left = "2160", Hanging = "360" })
)
{ LevelIndex = 2 });
numbering.Append(abstractNum);
// NumberingInstance links to AbstractNum and assigns a numId
numbering.Append(new NumberingInstance(
new AbstractNumId { Val = 1 }
)
{ NumberID = 1 });
numberingPart.Numbering = numbering;
numberingPart.Numbering.Save();
}
// --- SETTINGS PART ---
// Contains document-level settings: zoom, proof state, default tab stop, etc.
private static void AddSettingsPart(MainDocumentPart mainPart)
{
var settingsPart = mainPart.AddNewPart<DocumentSettingsPart>();
var settings = new Settings();
// Zoom: document zoom percentage (default 100%)
// Val is a percentage value (e.g., "100" = 100%)
settings.Append(new Zoom { Val = "100", Percent = true, SnapToGrid = true });
// ProofState: spelling/grammar check state
// Val combines bits: 1=grammar, 2=spelling, 3=both
settings.Append(new ProofState { Val = ProofingStateValues.Clean });
// Default tab stop interval in DXA
// Word inserts tab stops every 720 DXA (0.5 inch) by default
settings.Append(new DefaultTabStop { Val = 720 });
// Character spacing control: automatically adjust character spacing
// to maintain consistent line spacing (similar to InDesign)
settings.Append(new CharacterSpacingControl { Val = CharacterSpacingValues.CompressPunctuation });
// Compatibility settings: controls how Word handles certain formatting
// to ensure compatibility with different Word versions
settings.Append(new Compatibility(
new UseFELayout(), // Use formatted East Asian layout
new UseAsianDigraphicLineBreakRules(), // CJK line breaking rules
new AllowSpaceOfSameStyleInTable(), // Table cell spacing
new DoNotUseIndentAsPercentageForTabStops(), // Legacy tab behavior
new ProportionalOtherIndents(), // Proportional indents
new LayoutTableRawTextInTable() // Raw text in layout tables
));
// Revision tracking view settings
settings.Append(new RevisionView { DocPart = false, Formatting = true, Ink = true, Markup = true });
settingsPart.Settings = settings;
settingsPart.Settings.Save();
}
// --- THEME PART ---
// Defines color scheme, font scheme, and format scheme for the document theme
private static void AddThemePart(MainDocumentPart mainPart)
{
var themePart = mainPart.AddNewPart<ThemePart>();
var theme = new Theme(
new ThemeElements(
// Color scheme: 10 predefined theme colors
new ColorScheme(
new Dark1Color(new Color { Val = "000000" }),
new Light1Color(new Color { Val = "FFFFFF" }),
new Dark2Color(new Color { Val = "1F497D" }),
new Light2Color(new Color { Val = "EEECE1" }),
new Accent1Color(new Color { Val = "4F81BD" }),
new Accent2Color(new Color { Val = "C0504D" }),
new Accent3Color(new Color { Val = "9BBB59" }),
new Accent4Color(new Color { Val = "8064A2" }),
new Accent5Color(new Color { Val = "4BACC6" }),
new Accent6Color(new Color { Val = "F79646" }),
new Hyperlink(new Color { Val = "0000FF" }),
new FollowedHyperlinkColor(new Color { Val = "800080" })
),
// Font scheme: major (headings) and minor (body) fonts
new FontScheme(
new MajorFont { Val = "Calibri Light" },
new MinorFont { Val = "Calibri" }
),
// Format scheme: default fill and effect styles
new FormatScheme(
new FillStyleList(
new FillStyle { Fill = new PatternFill { PatternType = PatternValues.Solid } }
),
new LineStyleList(
new LineStyle { Val = LineValues.Single }
)
)
),
new ThemeName { Val = "Office Theme" },
new ThemeNames(
new LanguageBasedString { Val = "en-US", LanguageId = "x-none" }
)
);
themePart.Theme = theme;
themePart.Theme.Save();
}
// --- HEADERS AND FOOTERS ---
private static void AddHeadersAndFooters(MainDocumentPart mainPart)
{
// Header
var headerPart = mainPart.AddNewPart<HeaderPart>();
headerPart.Header = new Header(
new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Right }),
new Run(
new RunProperties(
new RunFonts { Ascii = "Calibri Light", HighAnsi = "Calibri Light" },
new Italic(),
new FontSize { Val = "20" } // 10pt
),
new Text("Document Header"))
));
var headerId = mainPart.GetIdOfPart(headerPart);
// Footer
var footerPart = mainPart.AddNewPart<FooterPart>();
footerPart.Footer = new Footer(
new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center }),
new Run(new Text("Page ") { Space = SpaceProcessingModeValues.Preserve }),
new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
new Run(new FieldCode(" PAGE ") { Space = SpaceProcessingModeValues.Preserve }),
new Run(new FieldChar { FieldCharType = FieldCharValues.End }),
new Run(new Text(" of ") { Space = SpaceProcessingModeValues.Preserve }),
new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
new Run(new FieldCode(" NUMPAGES ") { Space = SpaceProcessingModeValues.Preserve }),
new Run(new FieldChar { FieldCharType = FieldCharValues.End })
));
var footerId = mainPart.GetIdOfPart(footerPart);
// Reference IDs in section properties
// (added in CreateSectionProperties below)
}
// --- SECTION PROPERTIES (COMPLETE) ---
private static SectionProperties CreateSectionProperties()
{
var sectPr = new SectionProperties();
// Header/Footer references (must come before page size/margins)
var mainPart = doc.MainDocumentPart; // Note: in real code, pass as parameter
sectPr.Append(new HeaderReference
{
Type = HeaderFooterValues.Default,
Id = mainPart!.GetIdOfPart(mainPart.HeaderParts.First())
});
sectPr.Append(new FooterReference
{
Type = HeaderFooterValues.Default,
Id = mainPart.GetIdOfPart(mainPart.FooterParts.First())
});
// Page size
sectPr.Append(new PageSize { Width = 12240u, Height = 15840u });
// Page margins
sectPr.Append(new PageMargin
{
Top = 1440,
Bottom = 1440,
Left = 1440u,
Right = 1440u,
Header = 720u,
Footer = 720u
});
// Page numbering format
sectPr.Append(new PageNumberType { Start = 1, Format = NumberFormatValues.Decimal });
// Column settings (default: 1 column)
sectPr.Append(new Columns { ColumnCount = 1, EqualWidth = true });
// Paper source (printer tray)
// sectPr.Append(new PaperSource { Tray = 1, Paper = 7 });
return sectPr;
}
```
### 1.5 Unit Systems Reference
```csharp
// =============================================================================
// UNIT SYSTEMS IN OPENXML
// =============================================================================
// Understanding units is critical. Wrong unit = wrong formatting.
//
// DXA (Twentieths of a DXA) - "Standard Document Unit"
// 1 DXA = 1/20th of a point
// 1 inch = 1440 DXA
// 1 cm = 567 DXA (approx)
// Used for: margins, indents, spacing, tab stops, column widths
//
// Half-Points (sz) - Font Size
// Value is in half-points (1/2 point increments)
// 24 = 12pt, 28 = 14pt, 36 = 18pt, 48 = 24pt
// Used for: FontSize.Val, FontSizeComplexScript.Val
//
// Points (pt) - Direct Measurements
// Standard typographic point (72 per inch)
// Used for: some line spacing values, border widths
//
// EMU (English Metric Units) - Drawing Objects
// 1 inch = 914400 EMU
// Used for: drawing object sizes, shapes, images
//
// STARS (Special Twips Advanced Right-Left) - CJK Indentation
// Used for: FirstLineChars, HangingChars (special FirstLine/Hanging for CJK)
// Converts character counts to DXA based on font metrics
//
// LINE SPACING SPECIAL VALUES:
// Line = "240" with LineRule = Auto = single spacing (default)
// Line = "480" with LineRule = Auto = double spacing
// Line = "360" with LineRule = Auto = 1.5 spacing
// Line = "240" with LineRule = Exact = exactly 12pt
// Line = "288" with LineRule = AtLeast = at least 14.4pt (grows with content)
// --- CONVERSION HELPER METHODS ---
public static class OpenXmlUnits
{
// DXA conversions
public static int InchesToDxa(double inches) => (int)(inches * 1440);
public static int CmToDxa(double cm) => (int)(cm * 567.0);
public static int PtToDxa(double pt) => (int)(pt * 20);
public static double DxaToInches(int dxa) => dxa / 1440.0;
public static double DxaToCm(int dxa) => dxa / 567.0;
public static double DxaToPt(int dxa) => dxa / 20.0;
// EMU conversions (for drawings)
public static long InchesToEmu(double inches) => (long)(inches * 914400);
public static long CmToEmu(double cm) => (long)(cm * 360000);
public static double EmuToInches(long emu) => emu / 914400.0;
// Half-point conversions (font sizes)
public static int PtToHalfPt(double pt) => (int)(pt * 2);
public static int FontSizeToSz(double ptSize) => (int)(ptSize * 2);
public static double SzToPt(int sz) => sz / 2.0;
// Line spacing
public static int SingleSpacing => 240;
public static int DoubleSpacing => 480;
public static int OneAndHalfSpacing => 360;
public static int LineSpacingPt(double pt) => (int)(pt * 20); // Convert to DXA
}
// Example usage:
var marginInInches = OpenXmlUnits.DxaToInches(1440); // 1.0
var fontSizeInSz = OpenXmlUnits.FontSizeToSz(12.0); // 24
var indentInDxa = OpenXmlUnits.InchesToDxa(0.5); // 720
```
---
## 2. Style System Deep Dive
### 2.1 Style Types and Structure
```csharp
// =============================================================================
// STYLE TYPES OVERVIEW
// =============================================================================
// OpenXML defines 4 style types (StyleValues enum):
// 1. Paragraph (w:p) - controls paragraph-level formatting
// 2. Character (w:r) - controls inline/run-level formatting
// 3. Table (w:tbl) - controls table-level formatting
// 4. Numbering (w:num) - NOT a style type, but a separate numbering system
//
// Key insight: A style can be BOTH paragraph and character style (linked style).
// The "linkedStyle" element links a paragraph style to a character style.
// --- MINIMAL PARAGRAPH STYLE ---
// A paragraph style controls: pPr (paragraph properties) and optionally rPr
Style minimalParaStyle = new Style(
new StyleName { Val = "MyParagraphStyle" },
new PrimaryStyle() // Primary styles appear in Style gallery
)
{
Type = StyleValues.Paragraph,
StyleId = "MyParagraphStyle"
};
// --- MINIMAL CHARACTER STYLE ---
// A character style controls: rPr only (no pPr)
Style minimalCharStyle = new Style(
new StyleName { Val = "MyCharacterStyle" },
new PrimaryStyle()
)
{
Type = StyleValues.Character,
StyleId = "MyCharacterStyle"
};
// Character style with run properties (fonts, size, bold, etc.)
Style charStyleWithFormatting = new Style(
new StyleName { Val = "Emphasis" },
new PrimaryStyle(),
new StyleRunProperties(
new Italic(),
new Color { Val = "C00000" } // Dark red
)
)
{
Type = StyleValues.Character,
StyleId = "Emphasis"
};
// --- LINKED STYLE (Paragraph + Character) ---
// A linked style combines both: it can be applied to a paragraph OR a run.
// This is how Word's "Heading 1" works — applies to paragraphs, but you can
// also select text within a heading and apply the same style as character formatting.
Style linkedStyle = new Style(
new StyleName { Val = "LinkedStyle" },
new PrimaryStyle(),
new LinkedStyle { Val = "LinkedStyleChar" }, // Links to character style
new StyleParagraphProperties(
new SpacingBetweenLines { After = "120" }
),
new StyleRunProperties(
new Bold(),
new FontSize { Val = "24" }
)
)
{
Type = StyleValues.Paragraph,
StyleId = "LinkedStyle"
};
// Corresponding character style (normally same name + "Char" suffix by convention)
Style linkedStyleChar = new Style(
new StyleName { Val = "LinkedStyle Char" }, // Word convention: adds " Char"
new PrimaryStyle(),
new StyleRunProperties(
new Bold(),
new FontSize { Val = "24" }
)
)
{
Type = StyleValues.Character,
StyleId = "LinkedStyleChar"
};
// --- TABLE STYLE ---
Style tableStyle = new Style(
new StyleName { Val = "MyTableStyle" },
new PrimaryStyle(),
new StyleTableProperties(
new TableWidth { Width = "5000", Type = TableWidthUnitValues.Pct }, // 50% width
new TableBorders(
new TopBorder { Val = BorderValues.Single, Size = 4, Color = "000000" },
new BottomBorder { Val = BorderValues.Single, Size = 4, Color = "000000" },
new LeftBorder { Val = BorderValues.Single, Size = 4, Color = "000000" },
new RightBorder { Val = BorderValues.Single, Size = 4, Color = "000000" },
new InsideHorizontalBorder { Val = BorderValues.Single, Size = 2, Color = "CCCCCC" },
new InsideVerticalBorder { Val = BorderValues.Single, Size = 2, Color = "CCCCCC" }
),
new TableCellMarginDefault(
new TopMargin { Width = "0", Type = TableWidthUnitValues.DXA },
new StartMargin { Width = "108", Type = TableWidthUnitValues.DXA },
new BottomMargin { Width = "0", Type = TableWidthUnitValues.DXA },
new EndMargin { Width = "108", Type = TableWidthUnitValues.DXA }
)
)
)
{
Type = StyleValues.Table,
StyleId = "MyTableStyle"
};
```
### 2.2 DocDefaults and Document-Wide Defaults
```csharp
// =============================================================================
// DOCDEFAULTS: DOCUMENT-WIDE DEFAULTS
// =============================================================================
// DocDefaults lives inside Styles and provides fallback values when:
// 1. No explicit style is applied
// 2. No direct formatting is applied
// It contains RunPropertiesDefault and/or ParagraphPropertiesDefault.
//
// CRITICAL: DocDefaults applies to the entire document. Any explicit style
// or direct formatting will override it.
// --- COMPLETE DOCDEFAULTS SETUP ---
var docDefaults = new DocDefaults(
// Run properties defaults: default font, size, language for all runs
new RunPropertiesDefault(
new RunPropertiesBaseStyle(
// RunFonts: which font to use for each script
// Word will fall back through these: ASCII -> HighAnsi -> EastAsia -> ComplexScript
// Always specify at minimum Ascii and HighAnsi
new RunFonts
{
Ascii = "Calibri", // Western/Latin font (primary)
HighAnsi = "Calibri", // Latin characters (often same as Ascii)
EastAsia = "SimSun", // East Asian font (CJK)
ComplexScript = "Arial", // Complex scripts (Arabic, Hebrew, Thai)
ASCIITheme = ThemeFontValues.Minor,
HighAnsiTheme = ThemeFontValues.Minor,
EastAsiaTheme = ThemeFontValues.Minor,
ComplexScriptTheme = ThemeFontValues.Minor
},
// FontSize: in HALF-POINTS (24 = 12pt, 22 = 11pt, 20 = 10pt)
new FontSize { Val = "22" }, // 11pt for body
new FontSizeComplexScript { Val = "22" },
// Languages: required for proper hyphenation and spell checking
new Languages { Val = "en-US" }, // Default language
new Languages { EastAsia = "zh-CN", Val = "en-US" } // Can set multiple
)
),
// Paragraph properties defaults: default spacing, etc.
new ParagraphPropertiesDefault(
new ParagraphPropertiesBaseStyle(
// SpacingBetweenLines: default paragraph spacing
// After = "200" = 200 DXA = 10pt after each paragraph
new SpacingBetweenLines
{
After = "200",
Line = "276",
LineRule = LineSpacingRuleValues.Auto // Auto = 1.15x line height
}
)
)
);
// --- LAYOUT LUNCTIONS (LATENT STYLES) ---
// Latent styles are hidden styles that exist in Word but aren't in styles.xml.
// They provide fast-access defaults for formatting (e.g., Normal, Heading 1-6, etc.)
// when the user hasn't explicitly customized them.
//
// DocDefaults can define LatentStyleCountOverride to adjust count,
// but true latent styles are controlled by Normal.dotm (Word's global template).
Styles CreateStylesWithDocDefaults()
{
var styles = new Styles();
// DocDefaults with run and paragraph properties defaults
styles.Append(new DocDefaults(
new RunPropertiesDefault(
new RunPropertiesBaseStyle(
new RunFonts { Ascii = "Calibri", HighAnsi = "Calibri" },
new FontSize { Val = "22" },
new Languages { Val = "en-US" }
)
),
new ParagraphPropertiesDefault(
new ParagraphPropertiesBaseStyle(
new SpacingBetweenLines { After = "160", Line = "276", LineRule = LineSpacingRuleValues.Auto }
)
)
));
// LatentStyles: override defaults for built-in latent styles
// These control Word's "fast-styles" like Heading 1-6 before they're customized
styles.Append(new LatentStyles(
new Count { Val = 159 }, // Total latent style count
new FirstLineChars { Val = 352 }, // Default first line char count
new HorizontalOverflow { Val = HorizontalOverflowValues.Overflow },
new VerticalOverflow { Val = VerticalOverflowValues.Overflow },
new KoreanSpaceAdjust { Val = true },
// Each LatentStyleException overrides ONE attribute of ONE latent style
// StyleID = the built-in style name (e.g., "Normal", "heading 1")
// Attribute: what to change (bold, italic, font, color, etc.)
// The defaults for built-in headings: font=Calibri, size=24, bold
new LatentStyleException(
new Primary烙,
new StyleName { Val = "Normal" },
new UIPriority { Val = 1 },
new PrimaryZone(),
new QuickStyle()
),
new LatentStyleException(
new Primary烙,
new StyleName { Val = "heading 1" },
new UIPriority { Val = 9 },
new PrimaryZone(),
new QuickStyle(),
new Bold(),
new BoldComplexScript(),
new FontSize { Val = "48" }, // 24pt = 48 half-pts
new FontSizeComplexScript { Val = "48" }
)
));
return styles;
}
```
### 2.3 Complete Heading Styles Hierarchy
```csharp
// =============================================================================
// HEADING STYLES WITH PROPER INHERITANCE CHAIN
// =============================================================================
// Word's built-in heading system uses style inheritance:
// Normal (base) -> Heading1 -> Heading2 -> Heading3 -> Heading4 -> Heading5 -> Heading6
//
// Why this matters:
// - Each heading INHERITS from its parent (basedOn)
// - Define common properties in Normal, override in each heading
// - Change body font once in Normal, all headings inherit it
// - Heading-specific properties override as needed
// --- HEADING STYLE FACTORY ---
public static Style CreateHeadingStyle(int level, FontConfig fonts)
{
// Validate level (1-9 are valid, 1-6 are standard)
if (level < 1 || level > 9)
throw new ArgumentOutOfRangeException(nameof(level));
double[] headingSizes = [26.0, 20.0, 16.0, 14.0, 12.0, 11.0, 11.0, 11.0, 11.0];
string[] outlineLevels = ["0", "1", "2", "3", "4", "5", "6", "7", "8"};
var style = new Style(
new StyleName { Val = $"heading {level}" }, // Display name
new BasedOn { Val = level == 1 ? "Normal" : $"Heading{level - 1}" }, // Parent style
new NextParagraphStyle { Val = "Normal" }, // After heading -> Normal
new PrimaryStyle(), // Show in Styles gallery
new UIPriority { Val = 9 - level }, // Priority in gallery (H1 = 8, H2 = 7, etc.)
new QuickStyle(), // Appears in Quick Styles gallery
// Paragraph properties: spacing, keep options, outline level
new StyleParagraphProperties(
new KeepNext(), // Keep heading with next paragraph
new KeepLines(), // Keep all lines of heading together
new SpacingBetweenLines // Spacing before/after
{
Before = level == 1 ? "480" : "240", // H1 = 240pt before, others = 120pt
After = "120"
},
new OutlineLevel { Val = level - 1 } // 0-indexed for H1=0, H2=1, etc.
),
// Run properties: font, size, bold
new StyleRunProperties(
new RunFonts
{
Ascii = fonts.HeadingFont,
HighAnsi = fonts.HeadingFont,
EastAsia = "SimHei" // Bold heading font for CJK
},
new FontSize { Val = UnitConverter.FontSizeToSz(headingSizes[level - 1]) },
new FontSizeComplexScript { Val = UnitConverter.FontSizeToSz(headingSizes[level - 1]) },
new Bold(),
new BoldComplexScript()
)
)
{
Type = StyleValues.Paragraph,
StyleId = $"Heading{level}"
};
return style;
}
// --- ADD ALL HEADING STYLES TO STYLES COLLECTION ---
public static void AddHeadingStyles(Styles styles, FontConfig fonts)
{
for (int i = 1; i <= 6; i++)
{
styles.Append(CreateHeadingStyle(i, fonts));
}
// Also add Heading 7-9 (valid in Word, less commonly used)
for (int i = 7; i <= 9; i++)
{
styles.Append(CreateHeadingStyle(i, fonts));
}
}
// --- HEADING STYLES INHERITANCE VISUALIZATION ---
// When you apply "Heading2" (basedOn="Heading1"):
//
// Normal style:
// - Font: Calibri 11pt
// - Spacing: 0 before, 200 after
// - No bold
//
// Heading1 (basedOn="Normal"):
// - Inherits: Calibri 11pt
// - Overrides: Calibri Light 26pt, Bold, Spacing 480 before/120 after
// - Adds: KeepNext, KeepLines, OutlineLevel=0
//
// Heading2 (basedOn="Heading1"):
// - Inherits: Calibri Light 26pt, Bold, KeepNext, KeepLines
// - Overrides: 20pt
// - Inherits: OutlineLevel=1
//
// Effective result: Heading2 = Calibri Light 20pt Bold, KeepNext+KeepLines, 480/120 spacing, OL=1
```
### 2.4 Style Inheritance Chain Resolution
```csharp
// =============================================================================
// STYLE INHERITANCE RESOLUTION
// =============================================================================
// OpenXML styles resolve properties through the basedOn chain at RENDER TIME.
// The document.xml stores only the styleId, not the resolved properties.
// Word (or this library) walks the chain at load/display time.
//
// Example: Applying "Heading2" to a paragraph
//
// 1. Start with Heading2 style definition
// 2. Walk basedOn chain: Heading2 -> Heading1 -> Normal -> (null)
// 3. Collect properties in reverse order (most generic first):
// a. Normal: Ascii=Calibri, sz=22, no bold
// b. Heading1: Ascii=Calibri Light, sz=48, bold (override Calibri, sz, bold)
// c. Heading2: sz=40 (override sz only)
// 4. Final resolved style: Ascii=Calibri Light, sz=40, bold (bold from H1)
//
// IMPORTANT: Style override is COMPLETE for each element type:
// - If Normal has rPr with Fonts, and Heading1 has pPr only,
// Heading1 still inherits Normal's rPr fully.
// - StyleRunProperties (rPr) and StyleParagraphProperties (pPr) are separate.
// --- RESOLVING STYLE PROPERTIES MANUALLY ---
// For debugging or custom rendering, you may need to resolve style chains
public static class StyleResolver
{
public record ResolvedStyle(
StyleName? Name,
RunProperties? RunProps,
ParagraphProperties? ParaProps,
string? BasedOn,
string Type);
public static ResolvedStyle Resolve(Styles styles, string styleId)
{
var styleMap = styles.Elements<Style>().ToDictionary(s => s.StyleId?.Value ?? "");
var resolvedRpr = new List<RunProperties>();
var resolvedPpr = new List<ParagraphProperties>();
string? currentId = styleId;
string? name = null;
string type = "paragraph";
// Walk the chain
while (currentId != null && styleMap.TryGetValue(currentId, out var style))
{
name ??= style.Name?.Val?.Value;
type = style.Type?.Value?.ToString() ?? "paragraph";
// Collect rPr (style-level run properties)
var rpr = style.StyleRunProperties;
if (rpr != null) resolvedRpr.Add(rpr);
// Collect pPr (style-level paragraph properties)
var ppr = style.StyleParagraphProperties;
if (ppr != null) resolvedPpr.Add(ppr);
// Move to parent
currentId = style.BasedOn?.Val?.Value;
}
// Merge in reverse order (base styles first, derived last)
// This is a simplified merge — real Word merging is more complex
var mergedRpr = MergeRunProperties(resolvedRpr);
var mergedPpr = MergeParagraphProperties(resolvedPpr);
return new ResolvedStyle(
name != null ? new StyleName { Val = name } : null,
mergedRpr,
mergedPpr,
styleId,
type);
}
private static RunProperties MergeRunProperties(List<RunProperties> chain)
{
var merged = new RunProperties();
// In real implementation, copy each child element from chain[0] first,
// then chain[1], etc., overriding as you go
foreach (var rpr in chain)
{
foreach (var child in rpr.ChildElements)
{
// Skip duplicates, keep derived class's version
merged.RemoveAll(child.GetType());
merged.Append(child.CloneNode(true));
}
}
return merged;
}
private static ParagraphProperties MergeParagraphProperties(List<ParagraphProperties> chain)
{
var merged = new ParagraphProperties();
foreach (var ppr in chain)
{
foreach (var child in ppr.ChildElements)
{
merged.RemoveAll(child.GetType());
merged.Append(child.CloneNode(true));
}
}
return merged;
}
}
// --- STYLE ID VS STYLE NAME ---
// StyleId: the machine-readable identifier (used in w:pStyle val="Heading1")
// StyleName.Val: the display name shown in Word UI ("Heading 1")
//
// Word allows StyleId="Heading1" with StyleName.Val="Custom Heading One"
// The Id must be unique within the document; the Name can duplicate others.
//
// Built-in styles use specific Ids:
// "Normal", "Heading1"-"Heading9", "Title", "Subtitle", "Quote", "Quote1",
// "IntenseQuote", "SubtleReference", "Bibliography", "TOC1"-"TOC9", etc.
```
### 2.5 Complete Style Definitions Example
```csharp
// =============================================================================
// COMPLETE STYLE DEFINITIONS FOR A BUSINESS DOCUMENT
// =============================================================================
// This creates a complete styles.xml with all recommended styles for a
// professional document: Normal, Title, Subtitle, Headings 1-6, Quote,
// IntenseQuote, and linked character styles.
public static Styles CreateBusinessDocumentStyles()
{
var styles = new Styles();
// --- DOCDEFAULTS ---
styles.Append(new DocDefaults(
new RunPropertiesDefault(
new RunPropertiesBaseStyle(
new RunFonts { Ascii = "Calibri", HighAnsi = "Calibri" },
new FontSize { Val = "22" },
new FontSizeComplexScript { Val = "22" },
new Languages { Val = "en-US" }
)
),
new ParagraphPropertiesDefault(
new ParagraphPropertiesBaseStyle(
new SpacingBetweenLines { After = "200", Line = "276", LineRule = LineSpacingRuleValues.Auto }
)
)
));
// --- NORMAL STYLE (BASE FOR ALL) ---
styles.Append(new Style(
new StyleName { Val = "Normal" },
new PrimaryStyle(),
new UIPriority { Val = 10 },
new Primary烙,
new StyleRunProperties(
new RunFonts { Ascii = "Calibri", HighAnsi = "Calibri" },
new FontSize { Val = "22" },
new FontSizeComplexScript { Val = "22" }
)
)
{ Type = StyleValues.Paragraph, StyleId = "Normal", Default = true });
// --- TITLE STYLE ---
styles.Append(new Style(
new StyleName { Val = "Title" },
new BasedOn { Val = "Normal" },
new NextParagraphStyle { Val = "Normal" },
new PrimaryStyle(),
new UIPriority { Val = 1 },
new QuickStyle(),
new StyleParagraphProperties(
new Justification { Val = JustificationValues.Center },
new SpacingBetweenLines { After = "300", Line = "240", LineRule = LineSpacingRuleValues.Auto },
new KeepNext(),
new KeepLines()
),
new StyleRunProperties(
new RunFonts { Ascii = "Calibri Light", HighAnsi = "Calibri Light" },
new FontSize { Val = "56" }, // 28pt
new FontSizeComplexScript { Val = "56" },
new Bold(),
new BoldComplexScript(),
new Color { Val = "1F497D" } // Dark blue
)
)
{ Type = StyleValues.Paragraph, StyleId = "Title" });
// --- SUBTITLE STYLE ---
styles.Append(new Style(
new StyleName { Val = "Subtitle" },
new BasedOn { Val = "Normal" },
new NextParagraphStyle { Val = "Normal" },
new PrimaryStyle(),
new UIPriority { Val = 2 },
new QuickStyle(),
new StyleParagraphProperties(
new Justification { Val = JustificationValues.Center },
new SpacingBetweenLines { After = "200" },
new KeepNext(),
new KeepLines()
),
new StyleRunProperties(
new RunFonts { Ascii = "Calibri", HighAnsi = "Calibri" },
new FontSize { Val = "26" }, // 13pt
new Color { Val = "5A5A5A" } // Gray
)
)
{ Type = StyleValues.Paragraph, StyleId = "Subtitle" });
// --- HEADING 1-6 STYLES ---
AddHeadingStyles(styles);
// --- QUOTE STYLES ---
// Quote (indented, italic)
styles.Append(new Style(
new StyleName { Val = "Quote" },
new BasedOn { Val = "Normal" },
new NextParagraphStyle { Val = "Normal" },
new PrimaryStyle(),
new UIPriority { Val = 29 },
new QuickStyle(),
new StyleParagraphProperties(
new Justification { Val = JustificationValues.Both },
new Indentation { Left = "720", Right = "720" },
new SpacingBetweenLines { After = "160" },
new KeepNext(),
new KeepLines()
),
new StyleRunProperties(
new Italic(),
new ItalicComplexScript()
)
)
{ Type = StyleValues.Paragraph, StyleId = "Quote" });
// Intense Quote (bold, larger indent)
styles.Append(new Style(
new StyleName { Val = "Intense Quote" },
new BasedOn { Val = "Normal" },
new NextParagraphStyle { Val = "Normal" },
new PrimaryStyle(),
new UIPriority { Val = 30 },
new QuickStyle(),
new StyleParagraphProperties(
new Justification { Val = JustificationValues.Center },
new Indentation { Left = "1440", Right = "1440" },
new SpacingBetweenLines { After = "160" },
new KeepNext(),
new KeepLines(),
new ParagraphBorders(
new LeftBorder { Val = BorderValues.Single, Size = 24, Color = "4472C4", Space = 4 }
)
),
new StyleRunProperties(
new Bold(),
new Color { Val = "2F5496" }
)
)
{ Type = StyleValues.Paragraph, StyleId = "IntenseQuote" });
// --- LINKED CHARACTER STYLES ---
// "Emphasis" linked character style (used for <Ctrl+E> in Word)
styles.Append(new Style(
new StyleName { Val = "Emphasis" },
new PrimaryStyle(),
new StyleRunProperties(
new Italic()
)
)
{ Type = StyleValues.Character, StyleId = "Emphasis", Default = true });
// "Strong" linked character style
styles.Append(new Style(
new StyleName { Val = "Strong" },
new PrimaryStyle(),
new StyleRunProperties(
new Bold()
)
)
{ Type = StyleValues.Character, StyleId = "Strong", Default = true });
// --- TOC STYLES (for Table of Contents) ---
// TOC1-TOC9 are used by Word's TOC field for different heading levels
styles.Append(new Style(
new StyleName { Val = "TOC 1" },
new BasedOn { Val = "Normal" },
new Primary烙,
new StyleParagraphProperties(
new SpacingBetweenLines { After = "0" }
)
)
{ Type = StyleValues.Paragraph, StyleId = "TOC1" });
styles.Append(new Style(
new StyleName { Val = "TOC 2" },
new BasedOn { Val = "Normal" },
new Primary烙,
new StyleParagraphProperties(
new Indentation { Left = "220" },
new SpacingBetweenLines { After = "0" }
)
)
{ Type = StyleValues.Paragraph, StyleId = "TOC2" });
styles.Append(new Style(
new StyleName { Val = "TOC 3" },
new BasedOn { Val = "Normal" },
new Primary烙,
new StyleParagraphProperties(
new Indentation { Left = "440" },
new SpacingBetweenLines { After = "0" }
)
)
{ Type = StyleValues.Paragraph, StyleId = "TOC3" });
return styles;
}
// --- ADDING STYLES TO A DOCUMENT ---
public static void AddStylesToDocument(WordprocessingDocument doc, Styles styles)
{
var mainPart = doc.MainDocumentPart!;
// Get existing or create new styles part
var stylesPart = mainPart.StyleDefinitionsPart;
if (stylesPart == null)
{
stylesPart = mainPart.AddNewPart<StyleDefinitionsPart>();
stylesPart.Styles = styles;
}
else
{
// Clear and replace existing styles
stylesPart.Styles?.RemoveAllChildren();
stylesPart.Styles = styles;
}
stylesPart.Styles.Save();
}
```
### 2.6 Importing Styles from Another Document
```csharp
// =============================================================================
// IMPORTING STYLES FROM ANOTHER DOCUMENT
// =============================================================================
// Word's Organizer functionality allows copying styles between documents.
// This is useful for templates, branding, or style normalization.
public static class StyleImporter
{
/// <summary>
/// Imports styles from a source document into a target document.
/// Can selectively import by type or name.
/// </summary>
public static void ImportStyles(
WordprocessingDocument targetDoc,
string sourcePath,
bool overwriteExisting = false,
Func<Style, bool>? filter = null)
{
// Open source as read-only
using var sourceDoc = WordprocessingDocument.Open(sourcePath, isEditable: false);
var sourceStylesPart = sourceDoc.MainDocumentPart?.StyleDefinitionsPart;
if (sourceStylesPart?.Styles == null) return;
var targetStylesPart = targetDoc.MainDocumentPart!.StyleDefinitionsPart;
if (targetStylesPart == null)
{
targetStylesPart = targetDoc.MainDocumentPart.AddNewPart<StyleDefinitionsPart>();
targetStylesPart.Styles = new Styles();
}
var targetStyles = targetStylesPart.Styles!;
var existingIds = targetStyles.Elements<Style>()
.Select(s => s.StyleId?.Value ?? "")
.ToHashSet();
foreach (var sourceStyle in sourceStylesPart.Styles.Elements<Style>())
{
// Apply filter if provided
if (filter != null && !filter(sourceStyle))
continue;
var styleId = sourceStyle.StyleId?.Value ?? "";
if (string.IsNullOrEmpty(styleId)) continue;
// Skip if exists and not overwriting
if (existingIds.Contains(styleId) && !overwriteExisting)
continue;
// Clone the style (deep copy to avoid shared part issues)
var clonedStyle = (Style)sourceStyle.CloneNode(true);
// If overwriting, remove existing first
if (existingIds.Contains(styleId))
{
var existing = targetStyles.Elements<Style>()
.FirstOrDefault(s => s.StyleId?.Value == styleId);
existing?.Remove();
}
targetStyles.Append(clonedStyle);
existingIds.Add(styleId);
}
targetStylesPart.Styles.Save();
}
/// <summary>
/// Imports only heading styles from source.
/// </summary>
public static void ImportHeadingStyles(WordprocessingDocument targetDoc, string sourcePath)
{
ImportStyles(
targetDoc,
sourcePath,
overwriteExisting: true,
filter: style => style.Name?.Val?.Value?.StartsWith("heading") == true ||
style.Name?.Val?.Value?.StartsWith("Heading") == true);
}
/// <summary>
/// Imports all paragraph styles (not character, table, or numbering).
/// </summary>
public static void ImportParagraphStyles(WordprocessingDocument targetDoc, string sourcePath)
{
ImportStyles(
targetDoc,
sourcePath,
overwriteExisting: false,
filter: style => style.Type?.Value == StyleValues.Paragraph);
}
}
```
---
## 3. Character Formatting (RunProperties) — EXHAUSTIVE
```csharp
// =============================================================================
// RUN PROPERTIES (CHARACTER FORMATTING) — COMPLETE REFERENCE
// =============================================================================
// RunProperties (w:rPr) controls inline text formatting. It can appear in:
// 1. Style definitions (w:style/w:rPr) — applies to all text using that style
// 2. Direct formatting in runs (w:r/w:rPr) — overrides style for specific text
//
// CHILD ELEMENT ORDER (w:rPr): MUST be in this order per OpenXML schema:
// rStyle, rFonts, b, bCs, i, iCs, caps, smallCaps, strike, dstrike, vanish,
// w:webHidden, color, sz, szCs, highlight, rendition/sz, u, vertAlign, shd,
// baseTextStyle, eastAsianLayout, ligatures, bg, kern, spc, indent, snapToGrid,
// glyphs, activeXfrm, legacy, specStyle, shadow, charsetConvert, iFormat,
// w:templ
// --- MINIMAL RUN WITH FORMATTING ---
// Any run can contain RunProperties to control appearance
Paragraph minimalFormattedPara = new Paragraph(
new Run(
new RunProperties(
new Bold(),
new FontSize { Val = "28" } // 14pt (28 half-pts)
),
new Text("Bold 14pt text")
)
);
// ===========================================================================
// 3.1 RUNFONTS (Ascii, HighAnsi, EastAsia, ComplexScript)
// ===========================================================================
// RunFonts has 4 font "slots" for different scripts. Word uses fallback:
// ASCII -> HighAnsi -> EastAsia -> ComplexScript
// IMPORTANT: Always set at least Ascii and HighAnsi (they're often the same).
// Basic font specification
RunProperties fonts1 = new RunProperties(
new RunFonts
{
Ascii = "Calibri", // Western European characters (primary)
HighAnsi = "Calibri", // Same as ASCII for Western docs
EastAsia = "SimSun", // Simplified Chinese / East Asian
ComplexScript = "Arial" // Arabic, Hebrew, Thai, Vietnamese
}
);
// Using theme fonts (references to theme definitions)
RunProperties themeFonts = new RunProperties(
new RunFonts
{
ASCIITheme = ThemeFontValues.Minor, // Minor font from theme (body)
HighAnsiTheme = ThemeFontValues.Minor,
EastAsiaTheme = ThemeFontValues.Major, // Major font from theme (headings)
ComplexScriptTheme = ThemeFontValues.Minor,
// When using theme, you can still override specific slots
Ascii = "Calibri", HighAnsi = "Calibri" // Override minor with explicit font
}
);
// Complex script fonts (Arabic example)
RunProperties arabicFonts = new RunProperties(
new RunFonts
{
ComplexScript = "Traditional Arabic",
// Word automatically handles Arabic shaping with complex script fonts
}
);
// East Asian with specific fallback
RunProperties cjkFonts = new RunProperties(
new RunFonts
{
Ascii = "Microsoft YaHei", // Western: Microsoft YaHei for Chinese
HighAnsi = "Microsoft YaHei",
EastAsia = "Microsoft YaHei", // East Asian: same font handles both
ComplexScript = "Microsoft YaHei"
}
);
// Font substitution hints (rarely needed, for special cases)
RunProperties hintFonts = new RunProperties(
new RunFonts
{
Ascii = "Times New Roman",
HighAnsi = "Times New Roman",
Hint = FontStringsValues.EastAsia // Hint to Word: treat as East Asian font
}
);
// ===========================================================================
// 3.2 FONTSIZE (sz, szCs) — HALF-POINTS!
// ===========================================================================
// CRITICAL: w:sz stores HALF-POINTS. 24 = 12pt, 48 = 24pt.
// szCs = complex script size (for Arabic, Hebrew, etc.)
// Common font sizes
RunProperties fontSize12pt = new RunProperties(
new FontSize { Val = "24" }, // 12pt = 24 half-pts
new FontSizeComplexScript { Val = "24" }
);
RunProperties fontSize14pt = new RunProperties(
new FontSize { Val = "28" }, // 14pt
new FontSizeComplexScript { Val = "28" }
);
RunProperties fontSize18pt = new RunProperties(
new FontSize { Val = "36" }, // 18pt
new FontSizeComplexScript { Val = "36" }
);
RunProperties fontSize24pt = new RunProperties(
new FontSize { Val = "48" }, // 24pt
new FontSizeComplexScript { Val = "48" }
);
// FontSize from double (helper)
double targetPt = 11.0;
int halfPts = (int)(targetPt * 2); // 22 for 11pt
RunProperties dynamicFontSize = new RunProperties(
new FontSize { Val = halfPts.ToString() },
new FontSizeComplexScript { Val = halfPts.ToString() }
);
// Legacy font size (some documents use csSize instead)
// csSize = complex script size only
// ===========================================================================
// 3.3 BOLD (b, bCs, b, bCs)
// ===========================================================================
// b = bold for ASCII/Latin
// bCs = bold for complex script
// Both should usually be set together
RunProperties bold = new RunProperties(
new Bold(),
new BoldComplexScript() // Always include both for consistent rendering
);
// Bold with state control (on/off)
RunProperties unbold = new RunProperties(
new Bold { Val = OnOffValueValues.Off } // Explicitly turn off bold
);
// Conditional bold (for complex scripts)
// b={} with val=Off actually means "not bold" even if parent style says bold
// This is how you "unbold" in a bold context
// ===========================================================================
// 3.4 ITALIC (i, iCs)
// ===========================================================================
RunProperties italic = new RunProperties(
new Italic(),
new ItalicComplexScript()
);
RunProperties unitalic = new RunProperties(
new Italic { Val = OnOffValueValues.Off }
);
// Word's "Italic" is the style. ComplexScript italic handles Arabic calligraphy etc.
// ===========================================================================
// 3.5 UNDERLINE (u)
// ===========================================================================
// UnderlineValues enum has MANY options:
// Single, Double, Thick, Wave, Dash, Dotted, DashDot, DashDotDot,
// SingleAccounting, DoubleAccounting, TriWave, Nasized, DotDash, DotDotDash,
// LongDash, ThickDash, LongDashDot, ThickLongDash, ThickDashDot, ThickDashDotDot
// Single underline (most common)
RunProperties underlineSingle = new RunProperties(
new Underline { Val = UnderlineValues.Single }
);
// Double underline (often for edits/changes)
RunProperties underlineDouble = new RunProperties(
new Underline { Val = UnderlineValues.Double }
);
// Thick single underline
RunProperties underlineThick = new RunProperties(
new Underline { Val = UnderlineValues.Thick }
);
// Wave underline (often used for spelling errors in red)
RunProperties underlineWave = new RunProperties(
new Underline { Val = UnderlineValues.Wave }
);
// Dotted underline
RunProperties underlineDotted = new RunProperties(
new Underline { Val = UnderlineValues.Dotted }
);
// Dashed underline
RunProperties underlineDashed = new RunProperties(
new Underline { Val = UnderlineValues.Dash }
);
// Dash-dot underline
RunProperties underlineDashDot = new RunProperties(
new Underline { Val = UnderlineValues.DotDash }
);
// Accounting double underline (extends to both sides like accounting)
RunProperties underlineAccounting = new RunProperties(
new Underline { Val = UnderlineValues.DoubleAccounting }
);
// With color specification
RunProperties underlineColored = new RunProperties(
new Underline { Val = UnderlineValues.Single, Color = "FF0000" } // Red underline
);
// Without color (color="auto" = black)
// With specific color using hex
RunProperties underlineBlue = new RunProperties(
new Underline { Val = UnderlineValues.Single, Color = "0000FF" }
);
// Theme color on underline
RunProperties underlineThemeColor = new RunProperties(
new Underline
{
Val = UnderlineValues.Single,
Color = "auto", // or omit for auto/black
ThemeColor = ThemeColorValues.Accent1,
ThemeTint = "99" // 60% opacity (hex 99 = 153/255 ≈ 60%)
}
);
// Turn off underline (in a underlined context)
RunProperties noUnderline = new RunProperties(
new Underline { Val = UnderlineValues.None }
);
// ===========================================================================
// 3.6 COLOR (color)
// ===========================================================================
// Color.Val is a 6-digit hex color (RRGGBB) WITHOUT the #
// Word also supports 8-digit (AARRGGBB) for transparency
// Basic color
RunProperties redText = new RunProperties(
new Color { Val = "FF0000" } // Pure red
);
RunProperties blueText = new RunProperties(
new Color { Val = "0070C0" } // Office blue
);
// Theme colors (references to document theme)
RunProperties themeColorText = new RunProperties(
new Color
{
Val = "FFFFFF", // Fallback
ThemeColor = ThemeColorValues.Accent1,
ThemeShade = "BF", // 75% darker (hex BF = 191/255 ≈ 75%)
ThemeTint = "99" // 60% lighter (hex 99 = 153/255 ≈ 60%)
}
);
// Theme color shorthand
RunProperties accent1Text = new RunProperties(
new Color { Val = "4472C4" } // Direct hex is often simpler
);
// With transparency (alpha channel, 8-digit hex)
// AA=fully transparent, FF=fully opaque
RunProperties transparentText = new RunProperties(
new Color { Val = "80FF0000" } // 50% transparent red (AA=half, FF=red)
);
// ===========================================================================
// 3.7 HIGHLIGHT (highlight)
// ===========================================================================
// Highlight is a BACKGROUND color applied to the entire run.
// Different from Shading (which is in run properties too).
// Highlight enum values: DarkYellow, Yellow, Green, Cyan, Magenta, Blue,
// DarkBlue, DarkCyan, DarkGreen, DarkMagenta, DarkRed, DarkYellow, LightGray,
// LightGreen, LightOrange, LightPurple, LightRed, LightYellow, Navy, None,
// Orange, Pink, Purple, Red, Teal, Turquoise, Yellow
// Yellow highlight (default for comments)
RunProperties yellowHighlight = new RunProperties(
new Highlight { Val = HighlightValues.Yellow }
);
// Green highlight (for insertions)
RunProperties greenHighlight = new RunProperties(
new Highlight { Val = HighlightValues.Green }
);
// Red highlight (for deletions)
RunProperties redHighlight = new RunProperties(
new Highlight { Val = HighlightValues.Red }
);
// Blue highlight
RunProperties blueHighlight = new RunProperties(
new Highlight { Val = HighlightValues.Blue }
);
// Cyan highlight (for feedback)
RunProperties cyanHighlight = new RunProperties(
new Highlight { Val = HighlightValues.Cyan }
);
// Gray highlight (for search)
RunProperties grayHighlight = new RunProperties(
new Highlight { Val = HighlightValues.LightGray }
);
// No highlight (turn off)
RunProperties noHighlight = new RunProperties(
new Highlight { Val = HighlightValues.None }
);
// ===========================================================================
// 3.8 STRIKETHROUGH (strike, dstrike)
// ===========================================================================
// strike = single strikethrough
// dstrike = double strikethrough
// Single strikethrough (standard)
RunProperties strikethrough = new RunProperties(
new Strikethrough()
);
// Double strikethrough (often for legal/editing)
RunProperties doubleStrikethrough = new RunProperties(
new DoubleStrike()
);
// Turn off strikethrough
RunProperties noStrikethrough = new RunProperties(
new Strikethrough { Val = OnOffValueValues.Off }
);
// ===========================================================================
// 3.9 SUBSCRIPT/SUPERSCRIPT (verticalAlign)
// ===========================================================================
// VerticalTextAlignment enum: Baseline (normal), Subscript, Superscript
// Subscript: lowers the text and reduces size
// Superscript: raises the text and reduces size
// Superscript (e.g., 2 in X²)
RunProperties superscript = new RunProperties(
new VerticalTextAlignment { Val = VerticalPositionValues.Superscript }
);
// Subscript (e.g., 2 in H₂O)
RunProperties subscript = new RunProperties(
new VerticalTextAlignment { Val = VerticalPositionValues.Subscript }
);
// Baseline (normal) — explicit
RunProperties baseline = new RunProperties(
new VerticalTextAlignment { Val = VerticalPositionValues.Baseline }
);
// ===========================================================================
// 3.10 CAPS / ALLCAPS / SMALLCAPS (caps, smallCaps)
// ===========================================================================
// caps = ALL CAPS (converts lowercase to uppercase visually)
// smallCaps = Small Caps (converts lowercase to uppercase but with smaller font)
// ALL CAPS (visual only, underlying text unchanged)
RunProperties allCaps = new RunProperties(
new Caps()
);
// Small Caps (lowercase appears as smaller uppercase letters)
RunProperties smallCaps = new RunProperties(
new SmallCaps()
);
// Both properties together (smallcaps takes precedence visually if both set)
RunProperties emphasisCaps = new RunProperties(
new SmallCaps(),
new Caps()
);
// Turn off caps
RunProperties noCaps = new RunProperties(
new Caps { Val = OnOffValueValues.Off }
);
// ===========================================================================
// 3.11 SPACING / KERNING (spacing)
// ===========================================================================
// Spacing.Val is in TWIPS (1/20 of a point, same as DXA)
// Positive = add space, Negative = remove space
// Range: -240 to +240 twips typically
// Add space between characters (letter spacing / kerning)
RunProperties expandedSpacing = new RunProperties(
new Spacing
{
Val = 100, // +100 twips = +5pt of space between characters
// Space "100" = 5 points (100/20 = 5)
}
);
// Compress characters
RunProperties compressedSpacing = new RunProperties(
new Spacing
{
Val = -50, // -50 twips = -2.5pt (characters closer together)
}
);
// Normal spacing (remove any spacing adjustments)
RunProperties normalSpacing = new RunProperties(
new Spacing { Val = 0 }
);
// Combined with other properties
RunProperties spacedBold = new RunProperties(
new Spacing { Val = 50 },
new Bold()
);
// ===========================================================================
// 3.12 POSITION (position) — RAISED/LOWERED TEXT
// ===========================================================================
// Position.Val is in HALF-POINTS (not DXA!)
// Positive = raise, Negative = lower
// Range: -1584 to +1584 half-pts (-792pt to +792pt!)
// Raise text 6pt (12 half-points)
RunProperties raised = new RunProperties(
new Position
{
Val = 12 // +12 half-pts = +6pt raised
}
);
// Lower text 3pt
RunProperties lowered = new RunProperties(
new Position
{
Val = -6 // -6 half-pts = -3pt lowered
}
);
// Position is often used for:
// - Footnote references
// - Baseline alignment adjustments
// - Mathematical subscripts/superscripts (though verticalAlign is better for these)
// ===========================================================================
// 3.13 TEXT EFFECTS (textEffect)
// ===========================================================================
// TextEffectValues: shimmer, blinkBackground, etc.
// These are decorative effects for special visual emphasis
// Shimmer effect (sparkle/light animation)
RunProperties shimmerEffect = new RunProperties(
new TextEffect
{
Val = TextEffectValues.Shimmer
}
);
// Blink background effect
RunProperties blinkEffect = new RunProperties(
new TextEffect
{
Val = TextEffectValues.BlinkBackground
}
);
// Anti-alias effect (smoother text rendering)
// (usually applied via document settings, not per-run)
// ===========================================================================
// 3.14 SHADING ON RUNS (shd)
// ===========================================================================
// Shading on a run applies a background color/pattern to the text background
// Different from Highlight: Shading uses pattern fills, Highlight is solid colors
// Solid fill shading (run background color)
RunProperties shadedRun = new RunProperties(
new Shading
{
Val = ShadingPatternValues.Clear, // Clear = solid color
Color = "auto", // auto = no border
Fill = "FFFF00" // Yellow background
}
);
// Horizontal line pattern shading
RunProperties hLineShading = new RunProperties(
new Shading
{
Val = ShadingPatternValues.HorizontalLine, // Horizontal line pattern
Color = "0000FF",
Fill = "FFFF00"
}
);
// Reverse pattern (for special effects)
RunProperties reverseShading = new RunProperties(
new Shading
{
Val = ShadingPatternValues.ReverseDiagonalStripe,
Color = "auto",
Fill = "E0E0E0"
}
);
// Clear shading (remove)
RunProperties noShading = new RunProperties(
new Shading
{
Val = ShadingPatternValues.Clear,
Fill = "auto"
}
);
// Thatch pattern (diagonal lines, like legal document)
RunProperties thatchShading = new RunProperties(
new Shading
{
Val = ShadingPatternValues.Thatch,
Color = "000000",
Fill = "FFFFFF"
}
);
// Common shading patterns:
// Clear, Solid, HorizStripe, VertStripe, RevDiagStripe, DiagCross, DiagStripe,
// ReverseDiagStripe, DiagHorizCross, ThinHorzStripe, ThinVertStripe,
// ThinReverseDiagStripe, ThinDiagStripe, ThinDiagHorzCross, ThickHorzStripe,
// ThickVertStripe, ThickDiagStripe, ThickDiagCross, ThickReverseDiagStripe,
// ThickDiagonalCross, Shingle, ThickSmallCheck, SmallCheck, LargeCheck,
// SmallConfetti, Confetti, Horizontal, Diagonal, BigConfetti, ZigZag
// ===========================================================================
// 3.15 RUN STYLE (rStyle) — APPLY CHARACTER STYLE
// ===========================================================================
// RunStyle applies a character style to a run
// The style must be defined in styles.xml first
// Apply character style by ID
RunProperties styledRun = new RunProperties(
new RunStyle { Val = "Emphasis" } // References a character style
);
// Combined with direct formatting (direct overrides style)
RunProperties styledWithOverride = new RunProperties(
new RunStyle { Val = "Emphasis" },
new Bold { Val = OnOffValueValues.Off } // Override: don't make it bold
);
// ===========================================================================
// 3.16 BORDER ON RUNS (rPr/bdr)
// ===========================================================================
// Run borders apply a border around individual characters (rarely used)
// WordArt-style character border
RunProperties borderedRun = new RunProperties(
new CharacterBorder(
new TopBorder { Val = BorderValues.Single, Size = 4, Color = "0000FF", Space = 1 },
new BottomBorder { Val = BorderValues.Single, Size = 4, Color = "0000FF", Space = 1 },
new LeftBorder { Val = BorderValues.Single, Size = 4, Color = "0000FF", Space = 1 },
new RightBorder { Val = BorderValues.Single, Size = 4, Color = "0000FF", Space = 1 }
)
);
// Single border (typically used)
RunProperties borderTop = new RunProperties(
new CharacterBorder(
new TopBorder { Val = BorderValues.Single, Size = 8, Color = "FF0000" }
)
);
// ===========================================================================
// 3.17 VANISH / HIDDEN TEXT (vanish, webHidden)
// ===========================================================================
// vanish = hidden in both UI and print (like hidden field codes)
// webHidden = hidden in web layout view
// Hidden text (doesn't appear in UI or print)
RunProperties hiddenText = new RunProperties(
new Vanish()
);
// Hidden in web view only
RunProperties webHiddenText = new RunProperties(
new WebHidden()
);
// Both combined
RunProperties hiddenBothViews = new RunProperties(
new Vanish(),
new WebHidden()
);
// Turn off hide (in a hidden context)
RunProperties visible = new RunProperties(
new Vanish { Val = OnOffValueValues.Off }
);
// ===========================================================================
// 3.18 RIGHT-TO-LEFT (bidi)
// ===========================================================================
// For bidirectional text (Arabic, Hebrew)
// Right-to-left text
RunProperties rtlRun = new RunProperties(
new RightToLeftText()
);
// Normal direction
RunProperties ltrRun = new RunProperties(
new RightToLeftText { Val = OnOffValueValues.Off }
);
// ===========================================================================
// 3.19 LIGATURES (ligatures)
// ===========================================================================
// Ligatures combine adjacent characters for typography (fi, fl, ff, etc.)
// Standard=0 means no ligatures, Standard=1 means common ligatures
// Standard ligatures (fi, fl, ff, ffi, ffl)
RunProperties standardLigatures = new RunProperties(
new Ligatures { Val = 1 } // Standard ligatures on
);
// No ligatures
RunProperties noLigatures = new RunProperties(
new Ligatures { Val = 0 }
);
// Historical ligatures (old-style, for fonts that support them)
RunProperties historicalLigatures = new RunProperties(
new Ligatures { Val = 2 } // Historical
);
// ===========================================================================
// 3.20 COMPLEX SCRIPT PROPERTIES (cs, csBdr, csShd, etc.)
// ===========================================================================
// Complex script properties mirror the ASCII properties but for
// complex scripts (Arabic, Hebrew, Thai, etc.)
// Complex script bold
RunProperties csBold = new RunProperties(
new Bold(),
new BoldComplexScript()
);
// Complex script italic
RunProperties csItalic = new RunProperties(
new Italic(),
new ItalicComplexScript()
);
// Complex script underline
RunProperties csUnderline = new RunProperties(
new Underline { Val = UnderlineValues.Single },
new UnderlineComplexScript { Val = UnderlineValues.Single }
);
// Complex script border
RunProperties csBorder = new RunProperties(
new CharacterBorder(
new TopBorder { Val = BorderValues.Single, Size = 4, Color = "000080" }
)
);
// Complex script shading
RunProperties csShading = new RunProperties(
new Shading
{
Val = ShadingPatternValues.Clear,
Color = "auto",
Fill = "E6E6E6"
}
);
// ===========================================================================
// 3.21 LANGUAGE (lang) — HYPHENATION/SPELL CHECK
// ===========================================================================
// Language determines hyphenation, spell-check dictionary, etc.
// English (US)
RunProperties enUsText = new RunProperties(
new Languages { Val = "en-US" }
);
// English (UK)
RunProperties enGbText = new RunProperties(
new Languages { Val = "en-GB" }
);
// French
RunProperties frenchText = new RunProperties(
new Languages { Val = "fr-FR" }
);
// German
RunProperties germanText = new RunProperties(
new Languages { Val = "de-DE" }
);
// Chinese (Simplified)
RunProperties chineseText = new RunProperties(
new Languages { Val = "zh-CN" }
);
// Japanese
RunProperties japaneseText = new RunProperties(
new Languages { Val = "ja-JP" }
);
// Arabic
RunProperties arabicText = new RunProperties(
new Languages { Val = "ar-SA" }
);
// Hebrew
RunProperties hebrewText = new RunProperties(
new Languages { Val = "he-IL" }
);
// No language (apply directly)
RunProperties noLangText = new RunProperties(
new Languages { Val = "" }
);
// ===========================================================================
// 3.22 KERNING (kern)
// ===========================================================================
// Kern adjusts character spacing based on character pairs
// Value is in hundredths of a point (100 = 1pt)
// Enable kerning
RunProperties kerning = new RunProperties(
new Kern { Val = 20 } // 20 = 0.2pt minimum kerning threshold
);
// Disable kerning
RunProperties noKerning = new RunProperties(
new Kern { Val = 0 }
);
// Standard document kerning
RunProperties standardKerning = new RunProperties(
new Kern { Val = 12 } // 12 = 0.12pt
);
// ===========================================================================
// 3.23 SNAP TO GRID (snapToGrid)
// ===========================================================================
// SnapToGrid aligns characters to a document grid for consistent line spacing
// Enable snap to grid
RunProperties snapToGrid = new RunProperties(
new SnapToGrid()
);
// Disable snap to grid
RunProperties noSnapToGrid = new RunProperties(
new SnapToGrid { Val = OnOffValueValues.Off }
);
// ===========================================================================
// 3.24 COMBINED RUN FORMATTING EXAMPLE
// ===========================================================================
// Complete run properties combining many options
RunProperties complexRunProps = new RunProperties(
// Style reference (should be first per schema)
new RunStyle { Val = "Emphasis" },
// Font
new RunFonts
{
Ascii = "Georgia",
HighAnsi = "Georgia",
EastAsia = "SimSun"
},
// Bold + Bold Complex Script
new Bold(),
new BoldComplexScript(),
// Italic + Italic Complex Script
new Italic(),
new ItalicComplexScript(),
// Underline
new Underline { Val = UnderlineValues.Single, Color = "000080" },
// Font size (14pt)
new FontSize { Val = "28" },
new FontSizeComplexScript { Val = "28" },
// Color
new Color { Val = "000080" }, // Navy blue
// Language
new Languages { Val = "en-US" },
// Spacing (slightly expanded)
new Spacing { Val = 50 },
// Small caps
new SmallCaps(),
// Highlight
new Highlight { Val = HighlightValues.LightGray },
// Shadow (decorative)
new Shadow()
);
// ===========================================================================
// 3.25 APPLYING RUN PROPERTIES TO RUNS
// ===========================================================================
// RunProperties can be applied in multiple ways:
// Method 1: Inline in Run (direct formatting)
Paragraph inlineFormatting = new Paragraph(
new Run(
new RunProperties(
new Bold(),
new Color { Val = "FF0000" }
),
new Text("This is bold red text")
)
);
// Method 2: Via RunStyle (character style)
Paragraph styleFormatting = new Paragraph(
new Run(
new RunStyle { Val = "MyCharStyle" },
new Text("This uses the MyCharStyle character style")
)
);
// Method 3: Mix (direct overrides style)
Paragraph mixedFormatting = new Paragraph(
new Run(
new RunProperties(
new RunStyle { Val = "Emphasis" }, // Apply style first
new Bold { Val = OnOffValueValues.Off } // Override: unbold
),
new Text("Emphasis style but not bold")
)
);
// Method 4: Empty RunProperties to clear formatting
Paragraph clearedFormatting = new Paragraph(
new Run(
new RunProperties(
new Bold { Val = OnOffValueValues.Off },
new Italic { Val = OnOffValueValues.Off },
new Underline { Val = UnderlineValues.None },
new Color { Val = "000000" },
new FontSize { Val = "22" }
),
new Text("Manually reset to defaults")
)
);
```
---
## 4. Paragraph Formatting (ParagraphProperties) — EXHAUSTIVE
```csharp
// =============================================================================
// PARAGRAPH FORMATTING (PARAGRAPHPROPERTIES) — COMPLETE REFERENCE
// =============================================================================
// ParagraphProperties (w:pPr) controls paragraph-level formatting. It can appear in:
// 1. Style definitions (w:style/w:pPr) — applies to all paragraphs using that style
// 2. Direct formatting in paragraphs (w:p/w:pPr) — overrides style for specific paragraphs
//
// CHILD ELEMENT ORDER (w:pPr): MUST be in this order per OpenXML schema:
// pStyle, keepNext, keepLines, pageBreakBefore, widowControl, numPr, pBdr,
// shd, tabs, suppressAutoHyphens, spacing, ind, contextualSpacing,
// mirrorIndents, oMath, textDirection, textAlignment, textboxTightWrap,
// outlineLvl, divId, cnfStyle, rPr, sectPr, pPrChange
//
// CRITICAL: sectPr must be LAST child of w:body, but LAST BUT ONE in w:pPr context.
// In body, sectPr defines section properties. In pPr, sectPr defines section break before paragraph.
// ===========================================================================
// 4.1 JUSTIFICATION / ALIGNMENT (jc)
// ===========================================================================
// JustificationValues enum: Left, Center, Right, Both (Justify), Distribute,
// ThaiDistribute, Justified (same as Both in most cases)
// Left justification (default for LTR languages)
ParagraphProperties justifyLeft = new ParagraphProperties(
new Justification { Val = JustificationValues.Left }
);
// Center justification
ParagraphProperties justifyCenter = new ParagraphProperties(
new Justification { Val = JustificationValues.Center }
);
// Right justification (common in Arabic/Hebrew documents)
ParagraphProperties justifyRight = new ParagraphProperties(
new Justification { Val = JustificationValues.Right }
);
// Both/Justify (stretches lines to fill width — standard for books/newspapers)
ParagraphProperties justifyBoth = new ParagraphProperties(
new Justification { Val = JustificationValues.Both }
);
// Distribute (each line individually stretched to fill — no ragging)
// Often used in Asian typography
ParagraphProperties justifyDistribute = new ParagraphProperties(
new Justification { Val = JustificationValues.Distribute }
);
// ThaiDistribute (special handling for Thai script)
ParagraphProperties justifyThaiDistribute = new ParagraphProperties(
new Justification { Val = JustificationValues.ThaiDistribute }
);
// Center justification on a line (for titles)
Paragraph titlePara = new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center }
),
new Run(new Text("Centered Title"))
);
// ===========================================================================
// 4.2 INDENTATION (ind)
// ===========================================================================
// All indentation values in DXA (1 inch = 1440 DXA, 1 cm ≈ 567 DXA)
// Positive = indent rightward, Negative = indent leftward
//
// Left/Right: from page edge
// FirstLine: extra indent for first line (positive = indent right, negative = outdent)
// Hanging: amount to "hang" first line (negative moves first line left of body)
// FirstLineChars: CJK-specific, specifies in character counts
// Basic left indent (1 inch from left edge)
ParagraphProperties indentLeft1Inch = new ParagraphProperties(
new Indentation { Left = "1440" } // 1440 DXA = 1 inch
);
// Left indent with hanging first line (negative FirstLine)
ParagraphProperties hangingIndent = new ParagraphProperties(
new Indentation
{
Left = "720", // Body starts 0.5 inch from left
FirstLine = "-720" // First line aligns with body start
}
);
// FirstLine positive (first line indented more than body)
ParagraphProperties firstLineIndent = new ParagraphProperties(
new Indentation
{
Left = "1440", // Body at 1 inch
FirstLine = "720" // First line at 1.5 inch (additional 0.5 inch)
}
);
// Right indent
ParagraphProperties indentRight = new ParagraphProperties(
new Indentation { Right = "1440" } // 1 inch from right edge
);
// Both left and right indent (centered block)
ParagraphProperties blockIndent = new ParagraphProperties(
new Indentation
{
Left = "1440", // 1 inch from left
Right = "1440" // 1 inch from right
}
);
// Hanging indent (classic for bibliographies, numbered lists)
// First line hangs to the left of the body
ParagraphProperties hangingIndent720 = new ParagraphProperties(
new Indentation
{
Left = "1440", // Body indent = 1 inch
Hanging = "720" // First line hangs 0.5 inch to the left of body
}
);
// Outdent (first line starts BEFORE body start)
ParagraphProperties outdent = new ParagraphProperties(
new Indentation
{
Left = "720", // Body at 0.5 inch
FirstLine = "-720" // First line at 0 (page edge)
}
);
// Line-specific: negative left indent (pull into margin)
ParagraphProperties negativeIndent = new ParagraphProperties(
new Indentation { Left = "-720" } // 0.5 inch into left margin
);
// CJK FirstLineChars (character-based first line indent)
// This converts character count to DXA based on font metrics
ParagraphProperties cjkFirstLine = new ParagraphProperties(
new Indentation
{
Left = "567", // Body at 1 cm
FirstLineChars = 200 // 2 characters extra indent (200 = 2 chars × 100)
}
);
// CJK HangingChars
ParagraphProperties cjkHanging = new ParagraphProperties(
new Indentation
{
Left = "567",
HangingChars = 100 // 1 character hanging
}
);
// ===========================================================================
// 4.3 SPACING BETWEEN LINES (spacing)
// ===========================================================================
// SpacingBetweenLines has multiple attributes:
// Before: space above paragraph in DXA
// After: space below paragraph in DXA
// Line: line height (in DXA for Exact/AtLeast, or value×240 for Auto)
// LineRule: Auto (multiple of single), Exact (fixed DXA), AtLeast (minimum DXA)
//
// Special Line values for Auto:
// 240 = single spacing
// 360 = 1.5 line spacing
// 480 = double spacing
// 120 = half spacing (rare)
// For other multiples: Line = (desired spacing in points) × 20
// Space before only
ParagraphProperties spaceBefore = new ParagraphProperties(
new SpacingBetweenLines { Before = "240" } // 240 DXA = 12pt before
);
// Space after only
ParagraphProperties spaceAfter = new ParagraphProperties(
new SpacingBetweenLines { After = "200" } // 200 DXA = 10pt after
);
// Both before and after
ParagraphProperties spaceBoth = new ParagraphProperties(
new SpacingBetweenLines
{
Before = "120",
After = "120"
}
);
// SINGLE LINE SPACING (Auto rule)
ParagraphProperties singleSpacing = new ParagraphProperties(
new SpacingBetweenLines
{
Line = "240",
LineRule = LineSpacingRuleValues.Auto // 240 = 1.0× line height
}
);
// DOUBLE LINE SPACING
ParagraphProperties doubleSpacing = new ParagraphProperties(
new SpacingBetweenLines
{
Line = "480",
LineRule = LineSpacingRuleValues.Auto // 480 = 2.0× line height
}
);
// 1.5 LINE SPACING
ParagraphProperties oneAndHalfSpacing = new ParagraphProperties(
new SpacingBetweenLines
{
Line = "360",
LineRule = LineSpacingRuleValues.Auto // 360 = 1.5× line height
}
);
// EXACT LINE HEIGHT (fixed height, regardless of content)
ParagraphProperties exactLineHeight = new ParagraphProperties(
new SpacingBetweenLines
{
Line = "360", // 360 DXA = 18pt
LineRule = LineSpacingRuleValues.Exact // Exactly 18pt, even if text overflows
}
);
// AT-LEAST LINE HEIGHT (minimum, grows if needed)
ParagraphProperties atLeastLineHeight = new ParagraphProperties(
new SpacingBetweenLines
{
Line = "288", // At least 14.4pt
LineRule = LineSpacingRuleValues.AtLeast // At least 14.4pt, more if content requires
}
);
// LINE SPACING WITH SPACE BEFORE/AFTER
ParagraphProperties paragraphWithSpacing = new ParagraphProperties(
new SpacingBetweenLines
{
Before = "480", // 24pt before (for heading paragraphs)
After = "240", // 12pt after
Line = "276", // 1.15× line spacing
LineRule = LineSpacingRuleValues.Auto
}
);
// SPACE BETWEEN LINES EXPLAINED:
// LineRule = Auto:
// - Line value is a multiple of 240 (single spacing = 240)
// - Word multiplies by the font size to get actual line height
// - Example: Line="360" with 11pt font = 11pt × 1.5 = 16.5pt actual
// - Most common setting for body text
//
// LineRule = Exact:
// - Line value is in DXA directly
// - Line="360" = exactly 18pt, period
// - Text that exceeds will overflow
// - Used for fixed-height rows in tables
//
// LineRule = AtLeast:
// - Line value is minimum in DXA
// - Line="288" = at least 14.4pt, grows if text is taller
// - Used when you need minimum spacing but content varies
// ===========================================================================
// 4.4 KEEP OPTIONS (keepNext, keepLines, widowControl)
// ===========================================================================
// These control how paragraphs interact with page breaks
// KEEP NEXT: Keep this paragraph on same page as the following paragraph
// Essential for headings (don't separate heading from first paragraph)
ParagraphProperties keepWithNext = new ParagraphProperties(
new KeepNext()
);
// KEEP LINES: Keep all lines of this paragraph together (no page break inside)
// Used for: table rows, list items, or paragraphs that shouldn't split
ParagraphProperties keepLinesTogether = new ParagraphProperties(
new KeepLines()
);
// BOTH: Keep next AND keep lines together
ParagraphProperties keepBoth = new ParagraphProperties(
new KeepNext(),
new KeepLines()
);
// WIDOW CONTROL: Prevent single lines at page top/bottom (widow/orphan control)
// Default is ON in Word. Only disable if you want orphans/widows.
ParagraphProperties widowControl = new ParagraphProperties(
new WidowControl()
);
// NO WIDOW CONTROL (allow single lines at page breaks)
ParagraphProperties noWidowControl = new ParagraphProperties(
new WidowControl { Val = OnOffValueValues.Off }
);
// PAGE BREAK BEFORE: Start this paragraph on a new page
ParagraphProperties pageBreakBefore = new ParagraphProperties(
new PageBreakBefore()
);
// Combined: Heading style (keep with next, keep lines, page break before)
ParagraphProperties headingProps = new ParagraphProperties(
new KeepNext(),
new KeepLines(),
new PageBreakBefore(),
new WidowControl(),
new SpacingBetweenLines { Before = "480", After = "120" }
);
// ===========================================================================
// 4.5 OUTLINE LEVEL (outlineLvl)
// ===========================================================================
// OutlineLevel defines the heading level for document structure (TOC, Navigation)
// Values 0-8 correspond to Heading 1 through Heading 9
// Word uses this to identify headings in the Navigation Pane
// Level 0 = Heading 1
ParagraphProperties outlineLevel1 = new ParagraphProperties(
new OutlineLevel { Val = 0 }
);
// Level 1 = Heading 2
ParagraphProperties outlineLevel2 = new ParagraphProperties(
new OutlineLevel { Val = 1 }
);
// Level 5 = Heading 6
ParagraphProperties outlineLevel6 = new ParagraphProperties(
new OutlineLevel { Val = 5 }
);
// Level 8 = last possible level
ParagraphProperties outlineLevel8 = new ParagraphProperties(
new OutlineLevel { Val = 8 }
);
// TOC integration: When you insert a TOC field, Word looks for paragraphs
// with outlineLevel to generate entries. Without outlineLevel, TOC won't
// recognize the heading.
// Heading 1 style example (combining with style reference)
Paragraph heading1 = new Paragraph(
new ParagraphProperties(
new ParagraphStyleId { Val = "Heading1" }, // Style reference
new OutlineLevel { Val = 0 } // Also set outline level directly
),
new Run(new Text("Chapter One"))
);
// ===========================================================================
// 4.6 PARAGRAPH BORDERS (pBdr)
// ===========================================================================
// Paragraph borders draw lines around/adjacent to paragraphs
// Four borders: Top, Left, Bottom, Right, Between, Bar
// Simple bottom border
ParagraphProperties bottomBorder = new ParagraphProperties(
new ParagraphBorders(
new BottomBorder
{
Val = BorderValues.Single,
Size = 4,
Color = "000000",
Space = 4 // Space between text and border in DXA
}
)
);
// Top border only
ParagraphProperties topBorder = new ParagraphProperties(
new ParagraphBorders(
new TopBorder
{
Val = BorderValues.Single,
Size = 8,
Color = "4472C4",
Space = 4
}
)
);
// Double line bottom border (common for headings)
ParagraphProperties doubleBottomBorder = new ParagraphProperties(
new ParagraphBorders(
new BottomBorder
{
Val = BorderValues.Double,
Size = 4,
Color = "000000",
Space = 4
}
)
);
// All four borders
ParagraphProperties allBorders = new ParagraphProperties(
new ParagraphBorders(
new TopBorder { Val = BorderValues.Single, Size = 4, Color = "CCCCCC", Space = 1 },
new LeftBorder { Val = BorderValues.Single, Size = 4, Color = "CCCCCC", Space = 4 },
new BottomBorder { Val = BorderValues.Single, Size = 4, Color = "CCCCCC", Space = 1 },
new RightBorder { Val = BorderValues.Single, Size = 4, Color = "CCCCCC", Space = 4 }
)
);
// Between border (line between adjacent paragraphs)
// Used for paragraph groups with separator lines
ParagraphProperties withBetweenBorder = new ParagraphProperties(
new ParagraphBorders(
new BetweenBorder
{
Val = BorderValues.Single,
Size = 2,
Color = "CCCCCC",
Space = 4
}
)
);
// Bar border (vertical bar on one side)
// Val can be Left or Right — a solid bar in the margin
ParagraphProperties leftBarBorder = new ParagraphProperties(
new ParagraphBorders(
new BarBorder { Val = BorderValues.Left, Color = "000080", Size = 12 }
)
);
// Thick top border with color
ParagraphProperties thickTopBorder = new ParagraphProperties(
new ParagraphBorders(
new TopBorder
{
Val = BorderValues.Thick,
Size = 12,
Color = "2F5496",
Space = 8
}
)
);
// Wave border (decorative)
ParagraphProperties waveBorder = new ParagraphProperties(
new ParagraphBorders(
new BottomBorder
{
Val = BorderValues.Wave,
Size = 6,
Color = "FF0000",
Space = 4
}
)
);
// Border.NONE to explicitly remove borders
ParagraphProperties noBorders = new ParagraphProperties(
new ParagraphBorders(
new BottomBorder { Val = BorderValues.None }
)
);
// ===========================================================================
// 4.7 SHADING / BACKGROUND (shd)
// ===========================================================================
// Shading applies background color/pattern to the paragraph area
// Different from run-level highlight (which only covers the text)
// Solid color shading (paragraph background)
ParagraphProperties shadedBackground = new ParagraphProperties(
new Shading
{
Val = ShadingPatternValues.Clear,
Color = "auto",
Fill = "E6F2FF" // Light blue
}
);
// Gray shading (common for quotes, notes)
ParagraphProperties grayBackground = new ParagraphProperties(
new Shading
{
Val = ShadingPatternValues.Clear,
Color = "auto",
Fill = "F2F2F2" // Light gray
}
);
// Accent1 theme color shading
ParagraphProperties themedBackground = new ParagraphProperties(
new Shading
{
Val = ShadingPatternValues.Clear,
Color = "auto",
Fill = "D9E2F3", // Light blue accent
ThemeColor = ThemeColorValues.Accent1,
ThemeShade = "80" // 50% shade
}
);
// Pattern shading (horizontal lines)
ParagraphProperties stripedBackground = new ParagraphProperties(
new Shading
{
Val = ShadingPatternValues.HorizStripe,
Color = "000000",
Fill = "FFFFFF"
}
);
// Diagonal stripe shading
ParagraphProperties diagonalBackground = new ParagraphProperties(
new Shading
{
Val = ShadingPatternValues.ReverseDiagStripe,
Color = "auto",
Fill = "FFF2CC" // Light yellow
}
);
// Clear shading (remove background)
ParagraphProperties noBackground = new ParagraphProperties(
new Shading
{
Val = ShadingPatternValues.Clear,
Fill = "auto"
}
);
// Combined shading and border (common for callout boxes)
ParagraphProperties calloutBox = new ParagraphProperties(
new ParagraphBorders(
new LeftBorder
{
Val = BorderValues.Single,
Size = 24,
Color = "4472C4",
Space = 8
}
),
new Shading
{
Val = ShadingPatternValues.Clear,
Color = "auto",
Fill = "D9E2F3"
},
new Indentation { Left = "720" }
);
// ===========================================================================
// 4.8 TABS (tabs)
// ===========================================================================
// TabStops define where tab characters position text
// Each tab has: position (DXA from left margin), alignment, leader
// Single left tab at 1 inch
ParagraphProperties leftTab = new ParagraphProperties(
new Tabs(
new TabStop { Position = 1440, Val = TabStopValues.Left }
)
);
// Multiple tabs
ParagraphProperties multipleTabs = new ParagraphProperties(
new Tabs(
new TabStop { Position = 1440, Val = TabStopValues.Left }, // 1"
new TabStop { Position = 2880, Val = TabStopValues.Center }, // 2"
new TabStop { Position = 4320, Val = TabStopValues.Right }, // 3"
new TabStop { Position = 5760, Val = TabStopValues.Decimal, TabChar = '.' } // 4" decimal
)
);
// Tab with dot leader (dots connecting to tab position)
ParagraphProperties dotLeaderTab = new ParagraphProperties(
new Tabs(
new TabStop
{
Position = 4320, // 3 inches
Val = TabStopValues.Left,
Leader = TabStopLeaderCharValues.Dot
}
)
);
// Tab with dash leader
ParagraphProperties dashLeaderTab = new ParagraphProperties(
new Tabs(
new TabStop
{
Position = 4320,
Val = TabStopValues.Left,
Leader = TabStopLeaderCharValues.Dash
}
)
);
// Tab with underscore leader
ParagraphProperties underscoreLeaderTab = new ParagraphProperties(
new Tabs(
new TabStop
{
Position = 4320,
Val = TabStopValues.Left,
Leader = TabStopLeaderCharValues.Underscore
}
)
);
// Tab with heavy line leader
ParagraphProperties heavyLeaderTab = new ParagraphProperties(
new TabStop
{
Position = 4320,
Val = TabStopValues.Left,
Leader = TabStopLeaderCharValues.Heavy
)
);
// Tab with middle dot leader
ParagraphProperties middleDotLeaderTab = new ParagraphProperties(
new Tabs(
new TabStop
{
Position = 4320,
Val = TabStopValues.Left,
Leader = TabStopLeaderCharValues.MiddleDot
}
)
);
// CENTER TAB (text centered at tab position)
ParagraphProperties centerTab = new ParagraphProperties(
new Tabs(
new TabStop { Position = 4320, Val = TabStopValues.Center }
)
);
// RIGHT TAB (text right-aligned at tab position)
ParagraphProperties rightTab = new ParagraphProperties(
new Tabs(
new TabStop { Position = 5760, Val = TabStopValues.Right }
)
);
// DECIMAL TAB (aligns on decimal point)
ParagraphProperties decimalTab = new ParagraphProperties(
new Tabs(
new TabStop
{
Position = 5040, // 3.5 inches
Val = TabStopValues.Decimal,
TabChar = '.' // Align on period (or specify comma for European)
}
)
);
// BAR TAB (vertical bar at tab position)
ParagraphProperties barTab = new ParagraphProperties(
new Tabs(
new TabStop { Position = 2880, Val = TabStopValues.Bar }
)
);
// CLEAR TAB (removes inherited tab at this position)
ParagraphProperties clearTab = new ParagraphProperties(
new Tabs(
new TabStop { Position = 1440, Val = TabStopValues.Clear }
)
);
// TAB STOP LEADER VALUES (Leader property):
// None = no leader
// Dot = ....... (dots)
// Dash = ------- (dashes)
// Underscore = _______ (underscores)
// Heavy = ═══════ (heavy line)
// MiddleDot = ········ (centered dots, European style)
// ===========================================================================
// 4.9 SUPPRESS AUTO HYPHENS (suppressAutoHyphens)
// ===========================================================================
// When true, Word won't auto-hyphenate this paragraph
// Prevent auto-hyphenation
ParagraphProperties noAutoHyphens = new ParagraphProperties(
new SuppressAutoHyphens()
);
// Allow hyphenation (default) — explicit
ParagraphProperties allowAutoHyphens = new ParagraphProperties(
new SuppressAutoHyphens { Val = OnOffValueValues.Off }
);
// ===========================================================================
// 4.10 NUMBERING PROPERTIES (numPr)
// ===========================================================================
// numPr links a paragraph to a numbering definition (bullets or lists)
// Simple bullet list item
Paragraph bulletItem = new Paragraph(
new ParagraphProperties(
new NumberingProperties(
new NumberingLevelReference { Val = 0 }, // Level 0 (top-level)
new NumberingId { Val = 1 } // References numbering definition
),
new Indentation { Left = "720", Hanging = "360" } // Standard hanging indent
),
new Run(new Text("First bullet item"))
);
// Numbered list item
Paragraph numberedItem = new Paragraph(
new ParagraphProperties(
new NumberingProperties(
new NumberingLevelReference { Val = 0 },
new NumberingId { Val = 2 }
),
new Indentation { Left = "720", Hanging = "360" }
),
new Run(new Text("First numbered item"))
);
// Multi-level list item (level 2)
Paragraph level2Item = new Paragraph(
new ParagraphProperties(
new NumberingProperties(
new NumberingLevelReference { Val = 2 }, // Level 2 (sub-sub-item)
new NumberingId { Val = 1 }
),
new Indentation { Left = "1440", Hanging = "360" } // Deeper indent
),
new Run(new Text("Sub-item under sub-item"))
);
// Restart numbering at this paragraph
Paragraph restartNumberedItem = new Paragraph(
new ParagraphProperties(
new NumberingProperties(
new NumberingLevelReference { Val = 0 },
new NumberingId { Val = 3 },
new NumberingRestart { Val = NumberingRestartValues.Restart } // Restart
),
new Indentation { Left = "720", Hanging = "360" }
),
new Run(new Text("Item 1 (restarted)"))
);
// Continue numbering (default)
Paragraph continueNumberedItem = new Paragraph(
new ParagraphProperties(
new NumberingProperties(
new NumberingLevelReference { Val = 0 },
new NumberingId { Val = 3 },
new NumberingRestart { Val = NumberingRestartValues.Continuous } // Continue
),
new Indentation { Left = "720", Hanging = "360" }
),
new Run(new Text("Item 4 (continued)"))
);
// ===========================================================================
// 4.11 PARAGRAPH STYLE (pStyle)
// ===========================================================================
// pStyle references a paragraph style by ID
// Apply Heading1 style
ParagraphProperties styledPara = new ParagraphProperties(
new ParagraphStyleId { Val = "Heading1" }
);
// Apply custom style
ParagraphProperties customStyledPara = new ParagraphProperties(
new ParagraphStyleId { Val = "MyCustomStyle" }
);
// Default paragraph style (Normal)
ParagraphProperties normalPara = new ParagraphProperties(
new ParagraphStyleId { Val = "Normal" }
);
// ===========================================================================
// 4.12 BIDIRECTIONAL (BiDi, rtl)
// ===========================================================================
// BiDi enables right-to-left paragraph layout for Arabic/Hebrew
// Right-to-left paragraph
ParagraphProperties rtlParagraph = new ParagraphProperties(
new BiDi()
);
// Left-to-right (default) — explicit
ParagraphProperties ltrParagraph = new ParagraphProperties(
new BiDi { Val = OnOffValueValues.Off }
);
// When BiDi is on:
// - Text flows right-to-left
// - Justification defaults to right
// - List numbering appears on the right
// ===========================================================================
// 4.13 CONTEXTUAL SPACING (contextualSpacing)
// ===========================================================================
// When true, suppresses space between paragraphs when they share the same style
// Useful for headings followed by body text within the same style
// Enable contextual spacing (suppress space between same-style paragraphs)
ParagraphProperties contextualSpacing = new ParagraphProperties(
new ContextualSpacing()
);
// Disable contextual spacing (normal space between all paragraphs)
ParagraphProperties noContextualSpacing = new ParagraphProperties(
new ContextualSpacing { Val = OnOffValueValues.Off }
);
// ===========================================================================
// 4.14 MIRROR IN DENTS (mirrorIndents)
// ===========================================================================
// When enabled, Left/Right indents are mirrored for odd/even pages
// (left indent on even pages becomes right indent on odd pages)
// Used for book-style printing with binding margin
// Enable mirror indents
ParagraphProperties mirrorIndents = new ParagraphProperties(
new MirrorIndents()
);
// Disable mirror indents (default)
ParagraphProperties noMirrorIndents = new ParagraphProperties(
new MirrorIndents { Val = OnOffValueValues.Off }
);
// ===========================================================================
// 4.15 TEXT DIRECTION (textDirection)
// ===========================================================================
// Controls text flow direction within the paragraph
// Left-to-right (default)
ParagraphProperties ltrTextFlow = new ParagraphProperties(
new TextDirection { Val = TextDirectionValues.LeftToRight }
);
// Right-to-left
ParagraphProperties rtlTextFlow = new ParagraphProperties(
new TextDirection { Val = TextDirectionValues.RightToLeft }
);
// Top-to-bottom (vertical, common in East Asian documents)
ParagraphProperties verticalTextFlow = new ParagraphProperties(
new TextDirection { Val = TextDirectionValues.TopToBottom }
);
// Bottom-to-top (vertical rotated 180°)
ParagraphProperties bottomToTopTextFlow = new ParagraphProperties(
new TextDirection { Val = TextDirectionValues.BottomToTop }
);
// Left-to-right rotated (90° clockwise)
ParagraphProperties leftToRightRotated = new ParagraphProperties(
new TextDirection { Val = TextDirectionValues.LeftToRightRotated }
);
// Right-to-left rotated (90° counter-clockwise)
ParagraphProperties rightToLeftRotated = new ParagraphProperties(
new TextDirection { Val = TextDirectionValues.RightToLeftRotated }
);
// ===========================================================================
// 4.16 SNAP TO GRID (snapToGrid)
// ===========================================================================
// Aligns paragraph to document grid for consistent vertical spacing
// Enable snap to grid
ParagraphProperties snapToGridPara = new ParagraphProperties(
new SnapToGrid()
);
// Disable snap to grid
ParagraphProperties noSnapToGridPara = new ParagraphProperties(
new SnapToGrid { Val = OnOffValueValues.Off }
);
// ===========================================================================
// 4.17 TEXT ALIGNMENT (textAlignment)
// ===========================================================================
// Vertical alignment of text within a line box (rarely used)
// Default is Auto (baseline)
// Baseline alignment (default)
ParagraphProperties baselineAlign = new ParagraphProperties(
new TextAlignment { Val = VerticalTextAlignmentValues.Auto }
);
// Top alignment
ParagraphProperties topAlign = new ParagraphProperties(
new TextAlignment { Val = VerticalTextAlignmentValues.Top }
);
// Center alignment
ParagraphProperties centerVerticalAlign = new ParagraphProperties(
new TextAlignment { Val = VerticalTextAlignmentValues.Center }
);
// Bottom alignment
ParagraphProperties bottomAlign = new ParagraphProperties(
new TextAlignment { Val = VerticalTextAlignmentValues.Bottom }
);
// Baseline alignment (explicit)
ParagraphProperties baselineAlignExplicit = new ParagraphProperties(
new TextAlignment { Val = VerticalTextAlignmentValues.Baseline }
);
// ===========================================================================
// 4.18 DIV ID (divId)
// ===========================================================================
// Associates paragraph with a div for HTML/CSS mapping (很少使用)
// Used when importing/exporting HTML content
ParagraphProperties divIdPara = new ParagraphProperties(
new DivId { Val = "myDiv123" }
);
// ===========================================================================
// 4.19 CNF STYLE (cnfStyle)
// ===========================================================================
// Conditional formatting style index (used by Word for table of contents,
// styles pane grouping, etc.) — typically set automatically by Word
ParagraphProperties cnfStylePara = new ParagraphProperties(
new CnfStyle { Val = 1 } // Index into style's cnfStyle definitions
);
// ===========================================================================
// 4.20 SECTION PROPERTIES IN PARAGRAPH (sectPr)
// ===========================================================================
// Section properties can appear INSIDE a paragraph to create a section break
// BEFORE that paragraph. This is how you have different page layouts
// in different parts of the document.
// Section break with continuous layout
Paragraph continuousSectionBreak = new Paragraph(
new ParagraphProperties(
new SectionProperties(
new SectionType { Val = SectionMarkValues.Continuous }
)
)
);
// Section break starting new page
Paragraph newPageSectionBreak = new Paragraph(
new ParagraphProperties(
new SectionProperties(
new SectionType { Val = SectionMarkValues.NextPage }
)
)
);
// Section break with even page
Paragraph evenPageSectionBreak = new Paragraph(
new ParagraphProperties(
new SectionProperties(
new SectionType { Val = SectionMarkValues.EvenPage }
)
)
);
// Section break with odd page
Paragraph oddPageSectionBreak = new Paragraph(
new ParagraphProperties(
new SectionProperties(
new SectionType { Val = SectionMarkValues.OddPage }
)
)
);
// Section with custom page size
Paragraph customSectionPara = new Paragraph(
new ParagraphProperties(
new SectionProperties(
new PageSize { Width = 12240u, Height = 15840u }, // Letter
new PageMargin
{
Top = 1440,
Bottom = 1440,
Left = 1440u,
Right = 1440u
}
)
)
);
// ===========================================================================
// 4.21 COMBINED PARAGRAPH FORMATTING EXAMPLE
// ===========================================================================
// Complete paragraph properties combining many options
ParagraphProperties complexParaProps = new ParagraphProperties(
// Style reference
new ParagraphStyleId { Val = "Normal" },
// Keep options
new KeepNext(),
new KeepLines(),
new WidowControl(),
// Spacing
new SpacingBetweenLines
{
Before = "240",
After = "200",
Line = "276",
LineRule = LineSpacingRuleValues.Auto
},
// Indentation
new Indentation
{
Left = "0",
Right = "0",
FirstLine = "0",
Hanging = "0"
},
// Alignment
new Justification { Val = JustificationValues.Left },
// Border (bottom line)
new ParagraphBorders(
new BottomBorder
{
Val = BorderValues.Single,
Size = 4,
Color = "CCCCCC",
Space = 4
}
),
// Shading
new Shading
{
Val = ShadingPatternValues.Clear,
Color = "auto",
Fill = "auto"
},
// Tabs
new Tabs(
new TabStop { Position = 1440, Val = TabStopValues.Left },
new TabStop { Position = 2880, Val = TabStopValues.Center, Leader = TabStopLeaderCharValues.Dot }
),
// Outline level (for TOC)
new OutlineLevel { Val = 0 },
// Bidirectional
new BiDi { Val = OnOffValueValues.Off },
// Contextual spacing
new ContextualSpacing(),
// Snap to grid
new SnapToGrid(),
// Suppress auto hyphens
new SuppressAutoHyphens { Val = OnOffValueValues.Off }
);
// ===========================================================================
// 4.22 APPLYING PARAGRAPH PROPERTIES
// ===========================================================================
// ParagraphProperties can be applied in multiple ways:
// Method 1: Inline in Paragraph (direct formatting)
Paragraph inlineParaProps = new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center },
new SpacingBetweenLines { After = "200" }
),
new Run(new Text("Centered paragraph with space after"))
);
// Method 2: Via ParagraphStyleId (paragraph style)
Paragraph styledParagraph = new Paragraph(
new ParagraphProperties(
new ParagraphStyleId { Val = "Heading1" }
),
new Run(new Text("This is Heading 1"))
);
// Method 3: In Style definition (style-level)
Style bodyTextStyle = new Style(
new StyleName { Val = "BodyText" },
new BasedOn { Val = "Normal" },
new StyleParagraphProperties(
new Justification { Val = JustificationValues.Both }, // Justify
new SpacingBetweenLines { After = "160", Line = "276", LineRule = LineSpacingRuleValues.Auto },
new Indentation { FirstLine = "568" } // First line indent 0.5"
),
new StyleRunProperties(
new FontSize { Val = "22" }
)
)
{ Type = StyleValues.Paragraph, StyleId = "BodyText" };
// Method 4: Combination (style + direct overrides)
Paragraph mixedParaProps = new Paragraph(
new ParagraphProperties(
new ParagraphStyleId { Val = "BodyText" }, // Apply style
new Justification { Val = JustificationValues.Left } // Override justification
),
new Run(new Text("Body text style but left-aligned"))
);
// ===========================================================================
// 4.23 COMMON PATTERNS
// ===========================================================================
// Heading paragraph (with style + keep options)
Paragraph headingPara = new Paragraph(
new ParagraphProperties(
new ParagraphStyleId { Val = "Heading1" },
new KeepNext(),
new KeepLines(),
new SpacingBetweenLines { Before = "480", After = "120" },
new OutlineLevel { Val = 0 }
),
new Run(new Text("Chapter One"))
);
// Quote paragraph (indented, italic)
Paragraph quotePara = new Paragraph(
new ParagraphProperties(
new Indentation { Left = "1440", Right = "1440" },
new SpacingBetweenLines { Before = "240", After = "240" },
new ParagraphBorders(
new LeftBorder
{
Val = BorderValues.Single,
Size = 24,
Color = "4472C4",
Space = 8
}
)
),
new Run(
new RunProperties(new Italic()),
new Text("To be, or not to be, that is the question."))
);
// List item paragraph (hanging indent pattern)
Paragraph listItemPara = new Paragraph(
new ParagraphProperties(
new NumberingProperties(
new NumberingLevelReference { Val = 0 },
new NumberingId { Val = 1 }
),
new Indentation { Left = "720", Hanging = "360" }
),
new Run(new Text("• List item text"))
);
// Block quote / callout (background, left border)
Paragraph blockQuotePara = new Paragraph(
new ParagraphProperties(
new Indentation { Left = "720" },
new Shading
{
Val = ShadingPatternValues.Clear,
Fill = "F5F5F5"
},
new ParagraphBorders(
new LeftBorder
{
Val = BorderValues.Single,
Size = 12,
Color = "999999",
Space = 8
}
),
new SpacingBetweenLines { Before = "120", After = "120" }
),
new Run(new Text("Block quote text"))
);
// Caption (centered, small text, below figure)
Paragraph captionPara = new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center },
new SpacingBetweenLines { Before = "0", After = "240" },
new ParagraphStyleId { Val = "Caption" }
),
new Run(
new RunProperties(
new FontSize { Val = "20" }, // 10pt
new Italic()
),
new Text("Figure 1: Sample caption"))
);
// Page title (large, centered, space after)
Paragraph pageTitlePara = new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center },
new SpacingBetweenLines { After = "480" },
new KeepLines(),
new ParagraphBorders(
new BottomBorder
{
Val = BorderValues.Single,
Size = 4,
Color = "000000",
Space = 4
}
)
),
new Run(
new RunProperties(
new FontSize { Val = "56" }, // 28pt
new Bold()
),
new Text("Document Title"))
);
// Signature line (right-aligned, with tab for signature)
Paragraph signatureLinePara = new Paragraph(
new ParagraphProperties(
new Tabs(
new TabStop { Position = 5760, Val = TabStopValues.Right } // 4" right tab
)
),
new Run(new Text("Name: ") { Space = SpaceProcessingModeValues.Preserve }),
new Run(new TabChar()),
new Run(new Text("Date: ") { Space = SpaceProcessingModeValues.Preserve }),
new Run(new TabChar()),
new Run(new Text("Signature: ") { Space = SpaceProcessingModeValues.Preserve })
);
// Bibliography entry (hanging indent, single-spaced)
Paragraph bibliographyEntry = new Paragraph(
new ParagraphProperties(
new Indentation { Left = "720", Hanging = "720" },
new SpacingBetweenLines { Line = "240", LineRule = LineSpacingRuleValues.Auto },
new Bibliography()
),
new Run(new Text("Smith, J. (2024). The Art of OpenXML. New York: Publisher."))
);
// ===========================================================================
// 4.24 UNIT SYSTEM QUICK REFERENCE
// ===========================================================================
// DXA (Twentieths of a DXA / Twips):
// 1 inch = 1440 DXA
// 1 cm ≈ 567 DXA
// 1 pt = 20 DXA
// Used for: margins, indents, spacing, tab stops, borders
//
// Half-Points (Font Size):
// 24 = 12pt
// 22 = 11pt
// 20 = 10pt
// Used for: FontSize.Val
//
// Points (pt):
// Used for: border widths, some line spacing values
//
// EMU (English Metric Units):
// 1 inch = 914400 EMU
// Used for: drawing objects, images, shapes
//
// COMMON DXA VALUES:
// 720 = 0.5 inch
// 1440 = 1 inch
// 2160 = 1.5 inches
// 2880 = 2 inches
// 4320 = 3 inches
// 5760 = 4 inches
// 8640 = 6 inches
```
---
## Appendix A: Complete Working Example
```csharp
// =============================================================================
// COMPLETE WORKING EXAMPLE: BUSINESS REPORT
// =============================================================================
// This example demonstrates a complete, professional document with
// all concepts covered in this encyclopedia.
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
namespace OpenXmlExamples;
public static class BusinessReportGenerator
{
public static void Generate(string outputPath)
{
using var doc = WordprocessingDocument.Create(
outputPath,
WordprocessingDocumentType.Document);
var mainPart = doc.MainDocumentPart!;
mainPart.Document = new Document(new Body());
var body = mainPart.Document.Body!;
// Add all parts
AddStyles(mainPart);
AddNumbering(mainPart);
AddSettings(mainPart);
AddTheme(mainPart);
AddHeadersAndFooters(mainPart);
// Add content
AddTitle(body);
AddTableOfContents(body);
AddExecutiveSummary(body);
AddSection(body, "Introduction", @"
This is the introduction section of the business report.
It contains multiple paragraphs with various formatting.");
AddSection(body, "Methodology", @"
Our methodology section describes the approach taken.
Bulleted lists are used for key points:");
AddBulletPoints(body, new[]
{
"First methodology point",
"Second methodology point",
"Third methodology point with more text to demonstrate wrapping"
});
AddSection(body, "Results", @"
The results section presents data in tables:");
AddSampleTable(body);
AddSection(body, "Conclusion", @"
In conclusion, this report demonstrates the capabilities of the OpenXML SDK.
The formatting options are comprehensive and allow for professional document generation.");
// Section properties (must be last)
body.Append(CreateSectionProperties(mainPart));
mainPart.Document.Save();
}
private static void AddStyles(MainDocumentPart mainPart)
{
var stylesPart = mainPart.AddNewPart<StyleDefinitionsPart>();
var styles = CreateBusinessStyles();
stylesPart.Styles = styles;
stylesPart.Styles.Save();
}
private static Styles CreateBusinessStyles()
{
var styles = new Styles();
// DocDefaults
styles.Append(new DocDefaults(
new RunPropertiesDefault(
new RunPropertiesBaseStyle(
new RunFonts { Ascii = "Calibri", HighAnsi = "Calibri" },
new FontSize { Val = "22" },
new FontSizeComplexScript { Val = "22" },
new Languages { Val = "en-US" }
)
),
new ParagraphPropertiesDefault(
new ParagraphPropertiesBaseStyle(
new SpacingBetweenLines { After = "200", Line = "276", LineRule = LineSpacingRuleValues.Auto }
)
)
));
// Normal
styles.Append(new Style(
new StyleName { Val = "Normal" },
new PrimaryStyle(),
new StyleRunProperties(
new RunFonts { Ascii = "Calibri", HighAnsi = "Calibri" },
new FontSize { Val = "22" }
)
)
{ Type = StyleValues.Paragraph, StyleId = "Normal", Default = true });
// Title
styles.Append(new Style(
new StyleName { Val = "Title" },
new BasedOn { Val = "Normal" },
new NextParagraphStyle { Val = "Normal" },
new PrimaryStyle(),
new QuickStyle(),
new StyleParagraphProperties(
new Justification { Val = JustificationValues.Center },
new SpacingBetweenLines { After = "300" },
new KeepNext(),
new KeepLines()
),
new StyleRunProperties(
new RunFonts { Ascii = "Calibri Light", HighAnsi = "Calibri Light" },
new FontSize { Val = "56" },
new Bold(),
new Color { Val = "1F497D" }
)
)
{ Type = StyleValues.Paragraph, StyleId = "Title" });
// Heading 1
styles.Append(new Style(
new StyleName { Val = "heading 1" },
new BasedOn { Val = "Normal" },
new NextParagraphStyle { Val = "Normal" },
new PrimaryStyle(),
new QuickStyle(),
new StyleParagraphProperties(
new KeepNext(),
new KeepLines(),
new SpacingBetweenLines { Before = "480", After = "120" },
new OutlineLevel { Val = 0 },
new ParagraphBorders(
new BottomBorder { Val = BorderValues.Single, Size = 4, Color = "4472C4", Space = 4 }
)
),
new StyleRunProperties(
new RunFonts { Ascii = "Calibri Light", HighAnsi = "Calibri Light" },
new FontSize { Val = "48" },
new Bold(),
new Color { Val = "1F497D" }
)
)
{ Type = StyleValues.Paragraph, StyleId = "Heading1" });
// Heading 2
styles.Append(new Style(
new StyleName { Val = "heading 2" },
new BasedOn { Val = "Normal" },
new NextParagraphStyle { Val = "Normal" },
new PrimaryStyle(),
new QuickStyle(),
new StyleParagraphProperties(
new KeepNext(),
new SpacingBetweenLines { Before = "240", After = "120" },
new OutlineLevel { Val = 1 }
),
new StyleRunProperties(
new FontSize { Val = "32" },
new Bold(),
new Color { Val = "2F5496" }
)
)
{ Type = StyleValues.Paragraph, StyleId = "Heading2" });
return styles;
}
private static void AddNumbering(MainDocumentPart mainPart)
{
var numberingPart = mainPart.AddNewPart<NumberingDefinitionsPart>();
var numbering = new Numbering();
var abstractNum = new AbstractNum { AbstractNumberId = 1 };
abstractNum.Append(new Level(
new StartNumberingValue { Val = 1 },
new NumberingFormat { Val = NumberFormatValues.Bullet },
new LevelText { Val = "•" },
new LevelJustification { Val = LevelJustificationValues.Left },
new PreviousParagraphProperties(
new Indentation { Left = "720", Hanging = "360" })
)
{ LevelIndex = 0 });
numbering.Append(abstractNum);
numbering.Append(new NumberingInstance(
new AbstractNumId { Val = 1 }
)
{ NumberID = 1 });
numberingPart.Numbering = numbering;
numberingPart.Numbering.Save();
}
private static void AddSettings(MainDocumentPart mainPart)
{
var settingsPart = mainPart.AddNewPart<DocumentSettingsPart>();
settingsPart.Settings = new Settings(
new Zoom { Val = "100", Percent = true },
new DefaultTabStop { Val = 720 },
new CharacterSpacingControl { Val = CharacterSpacingValues.CompressPunctuation }
);
settingsPart.Settings.Save();
}
private static void AddTheme(MainDocumentPart mainPart)
{
var themePart = mainPart.AddNewPart<ThemePart>();
themePart.Theme = new Theme(
new ThemeElements(
new ColorScheme(
new Dark1Color(new Color { Val = "000000" }),
new Light1Color(new Color { Val = "FFFFFF" }),
new Accent1Color(new Color { Val = "4472C4" }),
new Accent2Color(new Color { Val = "C0504D" }),
new Accent3Color(new Color { Val = "9BBB59" }),
new Accent4Color(new Color { Val = "8064A2" }),
new Accent5Color(new Color { Val = "4BACC6" }),
new Accent6Color(new Color { Val = "F79646" })
),
new FontScheme(
new MajorFont { Val = "Calibri Light" },
new MinorFont { Val = "Calibri" }
)
),
new ThemeName { Val = "Office Theme" }
);
themePart.Theme.Save();
}
private static void AddHeadersAndFooters(MainDocumentPart mainPart)
{
var headerPart = mainPart.AddNewPart<HeaderPart>();
headerPart.Header = new Header(
new Paragraph(
new ParagraphProperties(new Justification { Val = JustificationValues.Right }),
new Run(
new RunProperties(new RunFonts { Ascii = "Calibri Light" }, new Italic(), new FontSize { Val = "18" }),
new Text("Business Report"))
));
var headerId = mainPart.GetIdOfPart(headerPart);
var footerPart = mainPart.AddNewPart<FooterPart>();
footerPart.Footer = new Footer(
new Paragraph(
new ParagraphProperties(new Justification { Val = JustificationValues.Center }),
new Run(new Text("Page ") { Space = SpaceProcessingModeValues.Preserve }),
new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
new Run(new FieldCode(" PAGE ") { Space = SpaceProcessingModeValues.Preserve }),
new Run(new FieldChar { FieldCharType = FieldCharValues.End }),
new Run(new Text(" of ") { Space = SpaceProcessingModeValues.Preserve }),
new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
new Run(new FieldCode(" NUMPAGES ") { Space = SpaceProcessingModeValues.Preserve }),
new Run(new FieldChar { FieldCharType = FieldCharValues.End })
));
var footerId = mainPart.GetIdOfPart(footerPart);
// Store IDs for later use
mainPart.Document.Body!.Append(new Paragraph()); // Placeholder for sectPr
}
private static void AddTitle(Body body)
{
body.Append(new Paragraph(
new ParagraphProperties(new ParagraphStyleId { Val = "Title" }),
new Run(new Text("Business Report"))
));
body.Append(new Paragraph(
new ParagraphProperties(new ParagraphStyleId { Val = "Subtitle" }),
new Run(new Text("Quarterly Performance Analysis"))
));
body.Append(new Paragraph(
new ParagraphProperties(new SpacingBetweenLines { After = "400" }),
new Run(
new RunProperties(new Color { Val = "666666" }),
new Text("March 2026"))
));
}
private static void AddTableOfContents(Body body)
{
body.Append(new Paragraph(
new ParagraphProperties(new ParagraphStyleId { Val = "Heading1" }),
new Run(new Text("Table of Contents"))
));
var tocPara = new Paragraph();
tocPara.Append(new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }));
tocPara.Append(new Run(new FieldCode(" TOC \\o \"1-2\" \\h \\z \\u ") { Space = SpaceProcessingModeValues.Preserve }));
tocPara.Append(new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }));
tocPara.Append(new Run(new Text("Update field to generate Table of Contents")));
tocPara.Append(new Run(new FieldChar { FieldCharType = FieldCharValues.End }));
body.Append(tocPara);
body.Append(new Paragraph(new Run(new Break { Type = BreakValues.Page })));
}
private static void AddExecutiveSummary(Body body)
{
body.Append(new Paragraph(
new ParagraphProperties(new ParagraphStyleId { Val = "Heading1" }),
new Run(new Text("Executive Summary"))
));
body.Append(new Paragraph(
new Run(new Text("This executive summary provides a high-level overview of the quarterly performance. Key highlights include revenue growth, market expansion, and operational improvements."))
));
}
private static void AddSection(Body body, string title, string content)
{
body.Append(new Paragraph(
new ParagraphProperties(new ParagraphStyleId { Val = "Heading1" }),
new Run(new Text(title))
));
body.Append(new Paragraph(
new Run(new Text(content))
));
}
private static void AddBulletPoints(Body body, string[] points)
{
foreach (var point in points)
{
body.Append(new Paragraph(
new ParagraphProperties(
new NumberingProperties(
new NumberingLevelReference { Val = 0 },
new NumberingId { Val = 1 }
),
new Indentation { Left = "720", Hanging = "360" }
),
new Run(new Text(point))
));
}
}
private static void AddSampleTable(Body body)
{
var table = new Table(
new TableProperties(
new TableWidth { Width = "5000", Type = TableWidthUnitValues.Pct },
new TableBorders(
new TopBorder { Val = BorderValues.Single, Size = 4, Color = "000000" },
new BottomBorder { Val = BorderValues.Single, Size = 4, Color = "000000" },
new LeftBorder { Val = BorderValues.Single, Size = 4, Color = "000000" },
new RightBorder { Val = BorderValues.Single, Size = 4, Color = "000000" },
new InsideHorizontalBorder { Val = BorderValues.Single, Size = 2, Color = "CCCCCC" },
new InsideVerticalBorder { Val = BorderValues.Single, Size = 2, Color = "CCCCCC" }
),
new TableCellMarginDefault(
new TopMargin { Width = "50", Type = TableWidthUnitValues.DXA },
new BottomMargin { Width = "50", Type = TableWidthUnitValues.DXA },
new StartMargin { Width = "100", Type = TableWidthUnitValues.DXA },
new EndMargin { Width = "100", Type = TableWidthUnitValues.DXA }
)
),
new TableGrid(
new GridColumn { Width = "2000" },
new GridColumn { Width = "2000" },
new GridColumn { Width = "2000" }
)
);
// Header row
var headerRow = new TableRow(
new TableRowProperties(new TableHeader()),
CreateTableCell("Metric", bold: true),
CreateTableCell("Q1 2026", bold: true),
CreateTableCell("Q4 2025", bold: true)
);
table.Append(headerRow);
// Data rows
table.Append(CreateTableRow("Revenue", "$2.5M", "$2.1M"));
table.Append(CreateTableRow("Growth", "19%", "12%"));
table.Append(CreateTableRow("Customers", "1,250", "1,100"));
body.Append(table);
}
private static TableCell CreateTableCell(string text, bool bold = false)
{
var cell = new TableCell(
new Paragraph(
new Run(
bold
? new RunProperties(new Bold())
: new RunProperties(),
new Text(text))
)
);
return cell;
}
private static TableRow CreateTableRow(string metric, string q1, string q4)
{
return new TableRow(
CreateTableCell(metric),
CreateTableCell(q1),
CreateTableCell(q4)
);
}
private static SectionProperties CreateSectionProperties(MainDocumentPart mainPart)
{
var sectPr = new SectionProperties();
// Header/Footer references
var headerPart = mainPart.HeaderParts.FirstOrDefault();
var footerPart = mainPart.FooterParts.FirstOrDefault();
if (headerPart != null)
sectPr.Append(new HeaderReference { Type = HeaderFooterValues.Default, Id = mainPart.GetIdOfPart(headerPart) });
if (footerPart != null)
sectPr.Append(new FooterReference { Type = HeaderFooterValues.Default, Id = mainPart.GetIdOfPart(footerPart) });
// Page size
sectPr.Append(new PageSize { Width = 12240u, Height = 15840u });
// Page margins
sectPr.Append(new PageMargin
{
Top = 1440,
Bottom = 1440,
Left = 1440u,
Right = 1440u,
Header = 720u,
Footer = 720u
});
return sectPr;
}
}
// ===========================================================================
// USAGE
// ===========================================================================
/*
public static void Main(string[] args)
{
BusinessReportGenerator.Generate("C:\\Reports\\BusinessReport.docx");
Console.WriteLine("Report generated successfully!");
}
*/
```
---
## Appendix B: OpenXmlUnits Helper Class
```csharp
// =============================================================================
// UNIT CONVERSION HELPERS
// =============================================================================
// Copy this class into your project for convenient unit conversions.
public static class OpenXmlUnits
{
// DXA (Twentieths of a DXA / Twips) conversions
public static int InchesToDxa(double inches) => (int)(inches * 1440);
public static int CmToDxa(double cm) => (int)(cm * 567.0);
public static int PtToDxa(double pt) => (int)(pt * 20);
public static double DxaToInches(int dxa) => dxa / 1440.0;
public static double DxaToCm(int dxa) => dxa / 567.0;
public static double DxaToPt(int dxa) => dxa / 20.0;
// EMU (English Metric Units) conversions
public static long InchesToEmu(double inches) => (long)(inches * 914400);
public static long CmToEmu(double cm) => (long)(cm * 360000);
public static double EmuToInches(long emu) => emu / 914400.0;
public static double EmuToCm(long emu) => emu / 360000.0;
// Half-point conversions (font sizes)
public static int PtToHalfPt(double pt) => (int)(pt * 2);
public static int FontSizeToSz(double ptSize) => (int)(ptSize * 2);
public static double SzToPt(int sz) => sz / 2.0;
// Line spacing helpers
public static int SingleSpacing => 240;
public static int DoubleSpacing => 480;
public static int OneAndHalfSpacing => 360;
public static int LineSpacingPt(double pt) => (int)(pt * 20);
// Common measurements
public static int HalfInch => 720;
public static int OneInch => 1440;
public static int OneAndHalfInches => 2160;
public static int TwoInches => 2880;
}
```
---
*Document Version: 1.0*
*OpenXML SDK: 3.x*
*.NET Version: 10*
*C# Version: 13*