137 KiB
137 KiB
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
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
Table of Contents
- Document Creation Skeleton
- Style System Deep Dive
- Character Formatting (RunProperties)
- Paragraph Formatting (ParagraphProperties)
1. Document Creation Skeleton
1.1 Complete Flow: Create to Save
// =============================================================================
// 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
// =============================================================================
// 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)
// =============================================================================
// 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
// =============================================================================
// 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
// =============================================================================
// 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
// =============================================================================
// 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
// =============================================================================
// 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
// =============================================================================
// 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
// =============================================================================
// 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
// =============================================================================
// 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
// =============================================================================
// 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
// =============================================================================
// 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
// =============================================================================
// 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
// =============================================================================
// 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
// =============================================================================
// 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