Styles In Depth
Style System Overview
OOXML styles provide a centralized way to manage document formatting. Understanding styles is critical for consistent document generation.
Style File Location
Path: /word/styles.xml
Structure:
<w:styles>
<w:docDefaults>...</w:docDefaults>
<w:latentStyles>...</w:latentStyles>
<w:style w:type="paragraph" w:styleId="Normal">...</w:style>
<w:style w:type="character" w:styleId="DefaultParagraphFont">...</w:style>
<w:style w:type="table" w:styleId="TableGrid">...</w:style>
</w:styles>
Document Defaults
Purpose
Base formatting applied to ALL content unless overridden.
Structure
<w:docDefaults>
<w:rPrDefault>
<w:rPr>
<w:rFonts w:ascii="Times New Roman"
w:hAnsi="Times New Roman"/>
<w:sz w:val="22"/> <!-- 11pt -->
<w:lang w:val="en-US"/>
</w:rPr>
</w:rPrDefault>
<w:pPrDefault>
<w:pPr>
<w:spacing w:after="0" w:line="276" w:lineRule="auto"/>
</w:pPr>
</w:pPrDefault>
</w:docDefaults>
AppraiSys Pattern
From DefaultDocument.cs:
private static void CreateDocDefaults(Styles styles)
{
var docDefaults = new DocDefaults();
// Run defaults
var runPropertiesDefault = new RunPropertiesDefault();
var runPropertiesBaseStyle = new RunPropertiesBaseStyle();
runPropertiesBaseStyle.AppendChild(new RunFonts
{
Ascii = "Times New Roman",
HighAnsi = "Times New Roman",
EastAsia = "Times New Roman",
ComplexScript = "Times New Roman"
});
runPropertiesBaseStyle.AppendChild(new FontSize { Val = "22" }); // 11pt
runPropertiesBaseStyle.AppendChild(new FontSizeComplexScript { Val = "22" });
runPropertiesBaseStyle.AppendChild(new Languages
{
Val = "en-US",
EastAsia = "en-US",
Bidi = "ar-SA"
});
runPropertiesDefault.AppendChild(runPropertiesBaseStyle);
docDefaults.AppendChild(runPropertiesDefault);
// Paragraph defaults
var paragraphPropertiesDefault = new ParagraphPropertiesDefault();
var paragraphPropertiesBaseStyle = new ParagraphPropertiesBaseStyle();
paragraphPropertiesBaseStyle.AppendChild(new SpacingBetweenLines
{
After = "0",
Line = "276",
LineRule = LineSpacingRuleValues.Auto
});
paragraphPropertiesDefault.AppendChild(paragraphPropertiesBaseStyle);
docDefaults.AppendChild(paragraphPropertiesDefault);
styles.AppendChild(docDefaults);
}
Paragraph Styles
Complete Style Definition
<w:style w:type="paragraph" w:styleId="Heading1">
<!-- Display name -->
<w:name w:val="Heading 1"/>
<!-- Based on another style -->
<w:basedOn w:val="Normal"/>
<!-- Next paragraph style -->
<w:next w:val="Normal"/>
<!-- UI priority -->
<w:uiPriority w:val="9"/>
<!-- Quick style -->
<w:qFormat/>
<!-- Paragraph properties -->
<w:pPr>
<w:keepNext/>
<w:keepLines/>
<w:spacing w:before="240" w:after="120"/>
<w:outlineLevel w:val="0"/>
</w:pPr>
<!-- Run properties (default for text in this style) -->
<w:rPr>
<w:b/>
<w:sz w:val="32"/> <!-- 16pt -->
<w:color w:val="2F5496"/>
</w:rPr>
</w:style>
Style Properties
| Property | Purpose | Example |
|---|---|---|
w:styleId | Unique identifier | "Heading1" |
w:name | Display name | "Heading 1" |
w:basedOn | Parent style | "Normal" |
w:next | Next paragraph style | "Normal" |
w:qFormat | Quick style | Flag |
w:uiPriority | UI order | Lower = higher priority |
Code Example
private static void CreateParagraphStyle(
Styles styles,
string styleId,
string styleName,
int uiPriority,
string basedOn = null)
{
var style = new Style
{
Type = StyleValues.Paragraph,
StyleId = styleId
};
style.AppendChild(new Name { Val = styleName });
style.AppendChild(new UIPriority { Val = uiPriority });
if (basedOn != null)
{
style.AppendChild(new BasedOn { Val = basedOn });
}
// Heading-specific properties
if (styleId.StartsWith("Heading"))
{
style.AppendChild(new PrimaryStyle());
var paragraphProperties = new ParagraphProperties();
paragraphProperties.AppendChild(new KeepNext());
paragraphProperties.AppendChild(new KeepLines());
paragraphProperties.AppendChild(new SpacingBetweenLines
{
Before = "240",
After = "120"
});
var level = int.Parse(styleId.Replace("Heading", "")) - 1;
paragraphProperties.AppendChild(new OutlineLevel { Val = level });
style.AppendChild(paragraphProperties);
}
styles.AppendChild(style);
}
Character Styles
Purpose
Apply formatting to specific text runs within paragraphs.
Structure
<w:style w:type="character" w:styleId="Emphasis">
<w:name w:val="Emphasis"/>
<w:basedOn w:val="DefaultParagraphFont"/>
<w:uiPriority w:val="20"/>
<w:qFormat/>
<w:rPr>
<w:i/>
</w:rPr>
</w:style>
Code Example
private static void CreateCharacterStyle(
Styles styles,
string styleId,
string styleName,
int uiPriority)
{
var style = new Style
{
Type = StyleValues.Character,
StyleId = styleId
};
style.AppendChild(new Name { Val = styleName });
style.AppendChild(new BasedOn { Val = "DefaultParagraphFont" });
style.AppendChild(new UIPriority { Val = uiPriority });
style.AppendChild(new PrimaryStyle());
// Example: Emphasis style
if (styleId == "Emphasis")
{
var runProperties = new RunProperties();
runProperties.AppendChild(new Italic());
style.AppendChild(runProperties);
}
styles.AppendChild(style);
}
Applying Character Style
<w:r>
<w:rPr>
<w:rStyle w:val="Emphasis"/>
</w:rPr>
<w:t>Emphasized text</w:t>
</w:r>
var run = new Run();
var runProperties = new RunProperties();
runProperties.AppendChild(new RunStyle { Val = "Emphasis" });
run.AppendChild(runProperties);
run.AppendChild(new Text("Emphasized text"));
Table Styles
Purpose
Centralize table formatting: borders, shading, cell margins.
Structure
<w:style w:type="table" w:styleId="TableGrid">
<w:name w:val="Table Grid"/>
<w:basedOn w:val="TableNormal"/>
<w:uiPriority w:val="39"/>
<w:tblPr>
<w:tblBorders>
<w:top w:val="single" w:sz="4"/>
<w:left w:val="single" w:sz="4"/>
<w:bottom w:val="single" w:sz="4"/>
<w:right w:val="single" w:sz="4"/>
<w:insideH w:val="single" w:sz="4"/>
<w:insideV w:val="single" w:sz="4"/>
</w:tblBorders>
</w:tblPr>
</w:style>
AppraiSys Pattern
private static void CreateTableStyle(
Styles styles,
string styleId,
string styleName,
int uiPriority)
{
var style = new Style
{
Type = StyleValues.Table,
StyleId = styleId
};
style.AppendChild(new Name { Val = styleName });
style.AppendChild(new UIPriority { Val = uiPriority });
var tableProperties = new TableProperties();
// Zero indentation
tableProperties.AppendChild(new TableIndentation
{
Width = 0,
Type = TableWidthUnitValues.Dxa
});
// Cell margins
tableProperties.AppendChild(new TableCellMarginDefault(
new TopMargin { Width = "0", Type = TableWidthUnitValues.Dxa },
new TableCellLeftMargin { Width = 108, Type = TableWidthValues.Dxa },
new BottomMargin { Width = "0", Type = TableWidthUnitValues.Dxa },
new TableCellRightMargin { Width = 108, Type = TableWidthValues.Dxa }
));
style.AppendChild(tableProperties);
// Row properties
style.AppendChild(new TableRowProperties(
new CantSplit()
));
styles.AppendChild(style);
}
Applying Table Style
<w:tbl>
<w:tblPr>
<w:tblStyle w:val="TableGrid"/>
</w:tblPr>
...
</w:tbl>
var tableProperties = new TableProperties();
tableProperties.AppendChild(new TableStyle { Val = "TableGrid" });
table.AppendChild(tableProperties);
Style Inheritance
How Inheritance Works
- Document Defaults - Applied to everything
- Based On Style - Inherited properties
- Style Definition - Style-specific properties
- Local Overrides - Properties on specific elements
Example:
<!-- Doc Defaults: Times New Roman, 11pt -->
<w:docDefaults>
<w:rPr>
<w:rFonts w:ascii="Times New Roman"/>
<w:sz w:val="22"/>
</w:rPr>
</w:docDefaults>
<!-- Heading1: Based on Normal, adds bold + 16pt -->
<w:style w:styleId="Heading1">
<w:basedOn w:val="Normal"/>
<w:rPr>
<w:b/>
<w:sz w:val="32"/>
</w:rPr>
</w:style>
<!-- Paragraph: Uses Heading1, overrides color -->
<w:p>
<w:pPr>
<w:pStyle w:val="Heading1"/>
</w:pPr>
<w:r>
<w:rPr>
<w:color w:val="FF0000"/> <!-- Local override -->
</w:rPr>
<w:t>Red Heading</w:t>
</w:r>
</w:p>
<!-- Result: Times New Roman, 16pt, bold, red -->
Linked Styles
Concept: Paragraph and character styles linked together.
<!-- Paragraph style -->
<w:style w:type="paragraph" w:styleId="Heading1">
<w:name w:val="Heading 1"/>
<w:link w:val="Heading1Char"/> <!-- Link to character style -->
...
</w:style>
<!-- Character style -->
<w:style w:type="character" w:styleId="Heading1Char">
<w:name w:val="Heading 1 Char"/>
<w:link w:val="Heading1"/> <!-- Link back -->
<w:rPr>
<w:b/>
<w:sz w:val="32"/>
</w:rPr>
</w:style>
Usage: Apply paragraph formatting to entire paragraph OR just character formatting to specific text.
AppraiSys Custom Styles
ImageLineSpacing1_15
CreateParagraphStyle(styles, "ImageLineSpacing1_15", "Image Line Spacing 1.15", 50, "Normal");
// Add specific properties
var style = styles.Elements<Style>()
.First(s => s.StyleId == "ImageLineSpacing1_15");
var paragraphProperties = new ParagraphProperties();
paragraphProperties.AppendChild(new SpacingBetweenLines
{
Line = "276", // 1.15 line spacing
LineRule = LineSpacingRuleValues.Auto
});
paragraphProperties.AppendChild(new Justification { Val = JustificationValues.Center });
style.AppendChild(paragraphProperties);
Purpose: Used for image paragraphs to control spacing.
PropertyImagesTable
var style = new Style
{
Type = StyleValues.Table,
StyleId = "PropertyImagesTable"
};
style.AppendChild(new Name { Val = "Property Images Table" });
style.AppendChild(new UIPriority { Val = 50 });
var tableProperties = new TableProperties();
// Center table
tableProperties.AppendChild(new TableJustification
{
Val = TableRowAlignmentValues.Center
});
// Cell spacing (gap between cells)
tableProperties.AppendChild(new TableCellSpacing
{
Width = "100",
Type = TableWidthUnitValues.Dxa
});
// No borders
tableProperties.AppendChild(new TableBorders(
new TopBorder { Val = BorderValues.None },
new BottomBorder { Val = BorderValues.None },
new LeftBorder { Val = BorderValues.None },
new RightBorder { Val = BorderValues.None },
new InsideHorizontalBorder { Val = BorderValues.None },
new InsideVerticalBorder { Val = BorderValues.None }
));
style.AppendChild(tableProperties);
Purpose: Photo gallery tables in appraisal reports.
Style Best Practices
✅ DO:
- Define Document Defaults: Set base formatting
- Use Style Inheritance: Build on existing styles
- Keep Style IDs Consistent: Use clear naming
- Apply Styles, Not Direct Formatting: Easier to change later
- Test Style Cascade: Verify inheritance works
❌ DON'T:
- Don't Duplicate Styles: Reuse existing definitions
- Don't Skip Document Defaults: Every document needs them
- Don't Use Inline Formatting Everywhere: Reduces maintainability
- Don't Reference Non-Existent Styles: Causes errors
- Don't Forget Character Styles: Useful for inline emphasis
Debugging Styles
Missing Style Reference
Problem: Paragraph references non-existent style
<w:p>
<w:pPr>
<w:pStyle w:val="CustomStyle"/> <!-- Style doesn't exist! -->
</w:pPr>
...
</w:p>
Fix: Either create style or use existing one
// Check if style exists
var styleExists = stylesPart.Styles.Elements<Style>()
.Any(s => s.StyleId == "CustomStyle");
if (!styleExists)
{
CreateParagraphStyle(stylesPart.Styles, "CustomStyle", "Custom Style", 100);
}
Style Not Applied
Problem: Style exists but formatting doesn't appear
Check:
- Style inheritance - is
basedOnchain correct? - Local overrides - are properties overridden locally?
- Style type - using paragraph style on run?
SFDT Styles
SFDT Style Format
{
"name": "Heading 1",
"type": "Paragraph",
"paragraphFormat": {
"beforeSpacing": 12,
"afterSpacing": 6,
"lineSpacing": 1.15,
"lineSpacingType": "Multiple"
},
"characterFormat": {
"bold": true,
"fontSize": 16,
"fontColor": "#2F5496FF"
},
"basedOn": "Normal",
"next": "Normal"
}
Conversion Notes
- OOXML
w:sz(half-points) → SFDTfontSize(points) - OOXML spacing (twips) → SFDT spacing (points)
- SFDT requires
typefield: "Paragraph", "Character", "Table"
Quick Reference
Minimum Style Definition
<w:style w:type="paragraph" w:styleId="MyStyle">
<w:name w:val="My Style"/>
</w:style>
Common Style Properties
| Property | XML | Code |
|---|---|---|
| Style Type | w:type="paragraph" | Type = StyleValues.Paragraph |
| Style ID | w:styleId="Heading1" | StyleId = "Heading1" |
| Display Name | <w:name w:val="Heading 1"/> | new Name { Val = "Heading 1" } |
| Based On | <w:basedOn w:val="Normal"/> | new BasedOn { Val = "Normal" } |
| Next Style | <w:next w:val="Normal"/> | new NextParagraphStyle { Val = "Normal" } |
| Quick Style | <w:qFormat/> | new PrimaryStyle() |
Next Steps
➡️ Next: Syncfusion SFDT Format
Related:
💡 Remember: Styles = DRY principle for documents. Define once, apply everywhere!