using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Wordprocessing;
namespace MiniMaxAIDocx.Core.Samples;
///
/// Exhaustive reference for every RunProperties (w:rPr) child element in OpenXML.
/// Each method demonstrates one formatting category with full XML doc comments,
/// unit explanations, and gotchas. All code compiles against DocumentFormat.OpenXml 3.5.1.
///
public static class CharacterFormattingSamples
{
// ──────────────────────────────────────────────────────────────────
// 1. Font Family (w:rFonts)
// ──────────────────────────────────────────────────────────────────
///
/// Sets the font family on a run using all four font slots defined in OOXML.
///
/// The four font slots:
///
/// - Ascii — Used for characters in the Basic Latin range (U+0000–U+007F).
/// This is the primary slot for English text.
/// - HighAnsi — Used for characters above U+007F that are NOT East Asian
/// and NOT Complex Script. Covers Latin Extended, Greek, Cyrillic, etc.
/// Typically set to the same value as Ascii.
/// - EastAsia — Used for CJK Unified Ideographs (U+4E00–U+9FFF),
/// Hiragana, Katakana, Hangul, CJK Compatibility, etc.
/// Set this for Chinese / Japanese / Korean content.
/// - ComplexScript — Used for Complex Script (BiDi) ranges:
/// Arabic (U+0600–U+06FF), Hebrew (U+0590–U+05FF), Thai, Devanagari,
/// and other right-to-left or complex-shaping scripts.
///
///
///
/// Gotcha: If HighAnsi is not set, Word may fall back to a different font
/// for characters like accented Latin (e.g., "e" uses Ascii, "e-acute" uses HighAnsi).
/// Always set both Ascii and HighAnsi together for consistent Western text rendering.
///
///
/// Gotcha: RunFonts also supports a Hint attribute
/// () that tells Word which slot to prefer when a
/// character could belong to multiple ranges. Values: Default, EastAsia, ComplexScript.
///
///
public static void ApplyFontFamily(Run run)
{
var rPr = run.GetOrCreateRunProperties();
rPr.RunFonts = new RunFonts
{
// Basic Latin characters (U+0000–U+007F)
Ascii = "Calibri",
// Non-CJK, non-complex characters above U+007F (Latin Extended, Greek, Cyrillic)
HighAnsi = "Calibri",
// CJK Ideographs, Hiragana, Katakana, Hangul
EastAsia = "SimSun",
// Arabic, Hebrew, Thai, Devanagari and other complex scripts
ComplexScript = "Arial",
// Hint tells Word which font slot to prefer for ambiguous characters.
// FontTypeHintValues.EastAsia makes Word prefer the EastAsia slot.
Hint = FontTypeHintValues.EastAsia
};
}
// ──────────────────────────────────────────────────────────────────
// 2. Font Size (w:sz, w:szCs)
// ──────────────────────────────────────────────────────────────────
///
/// Sets the font size on a run.
///
/// Unit: w:sz is in half-points. 12pt = 24 half-points, 10.5pt = 21 half-points.
///
///
/// w:szCs (FontSizeComplexScript) controls the size for Complex Script text
/// (Arabic, Hebrew, etc.). It must be set separately — it does NOT inherit from w:sz.
/// If you only set w:sz, Arabic/Hebrew text may render at a different size.
///
///
/// The run to modify.
/// Size in typographic points (e.g., 12.0 for 12pt).
public static void ApplyFontSize(Run run, double points)
{
var rPr = run.GetOrCreateRunProperties();
// Convert points to half-points: 12pt → "24"
var halfPoints = ((int)(points * 2)).ToString();
// w:sz — size for Latin / East Asian text
rPr.FontSize = new FontSize { Val = halfPoints };
// w:szCs — size for Complex Script text (Arabic, Hebrew, Thai, etc.)
// Must be set independently; does NOT inherit from w:sz.
rPr.FontSizeComplexScript = new FontSizeComplexScript { Val = halfPoints };
}
// ──────────────────────────────────────────────────────────────────
// 3. Bold and Italic (w:b, w:bCs, w:i, w:iCs)
// ──────────────────────────────────────────────────────────────────
///
/// Applies bold and italic formatting to a run.
///
/// Complex Script variants: w:bCs and w:iCs control bold/italic for Complex
/// Script text (Arabic, Hebrew). They must be set independently.
///
///
/// Gotcha: Bold with no Val attribute means "true".
/// To explicitly disable bold (override a style), set Val = false.
/// An absent element means "inherit from style".
///
///
public static void ApplyBoldItalic(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// Bold for Latin / East Asian text
// (no val) is equivalent to
rPr.Bold = new Bold();
// Bold for Complex Script (Arabic, Hebrew, etc.)
rPr.BoldComplexScript = new BoldComplexScript();
// Italic for Latin / East Asian text
rPr.Italic = new Italic();
// Italic for Complex Script
rPr.ItalicComplexScript = new ItalicComplexScript();
// To DISABLE bold (e.g., override a bold style), explicitly set Val = false:
// rPr.Bold = new Bold { Val = false };
}
// ──────────────────────────────────────────────────────────────────
// 4. Underline (w:u) — ALL UnderlineValues
// ──────────────────────────────────────────────────────────────────
///
/// Demonstrates every underline style available in OOXML.
///
/// Underline color: By default, the underline color matches the text color.
/// Override with Color (hex) and/or ThemeColor.
///
///
/// All 18 styles: Single, Words, Double, Thick, Dotted, DottedHeavy,
/// Dash, DashedHeavy, DashLong, DashLongHeavy, DotDash, DashDotHeavy,
/// DotDotDash, DashDotDotHeavy, Wave, WavyHeavy, WavyDouble, None.
///
///
public static void ApplyAllUnderlineStyles(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// ── Standard underlines ──
// Single: standard single underline (most common)
rPr.Underline = new Underline { Val = UnderlineValues.Single };
// Words: underlines words only, not spaces
// rPr.Underline = new Underline { Val = UnderlineValues.Words };
// Double: two parallel lines
// rPr.Underline = new Underline { Val = UnderlineValues.Double };
// Thick: single thick line
// rPr.Underline = new Underline { Val = UnderlineValues.Thick };
// ── Dotted variants ──
// Dotted: dots
// rPr.Underline = new Underline { Val = UnderlineValues.Dotted };
// DottedHeavy: thick dots
// rPr.Underline = new Underline { Val = UnderlineValues.DottedHeavy };
// ── Dash variants ──
// Dash: short dashes
// rPr.Underline = new Underline { Val = UnderlineValues.Dash };
// DashedHeavy: thick short dashes
// rPr.Underline = new Underline { Val = UnderlineValues.DashedHeavy };
// DashLong: long dashes
// rPr.Underline = new Underline { Val = UnderlineValues.DashLong };
// DashLongHeavy: thick long dashes
// rPr.Underline = new Underline { Val = UnderlineValues.DashLongHeavy };
// ── Dash-dot combinations ──
// DotDash: alternating dot-dash (._._.)
// rPr.Underline = new Underline { Val = UnderlineValues.DotDash };
// DashDotHeavy: thick dot-dash
// rPr.Underline = new Underline { Val = UnderlineValues.DashDotHeavy };
// DotDotDash: dot-dot-dash (.._.._)
// rPr.Underline = new Underline { Val = UnderlineValues.DotDotDash };
// DashDotDotHeavy: thick dot-dot-dash
// rPr.Underline = new Underline { Val = UnderlineValues.DashDotDotHeavy };
// ── Wave variants ──
// Wave: wavy line
// rPr.Underline = new Underline { Val = UnderlineValues.Wave };
// WavyHeavy: thick wavy line
// rPr.Underline = new Underline { Val = UnderlineValues.WavyHeavy };
// WavyDouble: double wavy line
// rPr.Underline = new Underline { Val = UnderlineValues.WavyDouble };
// ── Remove underline ──
// None: explicitly remove underline (override style)
// rPr.Underline = new Underline { Val = UnderlineValues.None };
// ── Underline with custom color ──
// rPr.Underline = new Underline
// {
// Val = UnderlineValues.Single,
// Color = "FF0000", // Red underline, independent of text color
// ThemeColor = ThemeColorValues.Accent1 // Or use theme color
// };
}
// ──────────────────────────────────────────────────────────────────
// 5. Text Color (w:color)
// ──────────────────────────────────────────────────────────────────
///
/// Sets the text color on a run using hex value and/or theme colors.
///
/// Val: 6-digit hex RGB string WITHOUT the "#" prefix (e.g., "FF0000" for red).
/// The special value "auto" means the application decides (usually black).
///
///
/// ThemeColor: References a theme color slot. When set alongside Val, the
/// theme color takes precedence in theme-aware applications, but Val is the fallback.
///
///
/// ThemeShade: Darkens the theme color. Value is a 2-digit hex string (00–FF).
/// 00 = no change, FF = fully darkened. Applied as a multiplier.
///
///
/// ThemeTint: Lightens the theme color. Value is a 2-digit hex string (00–FF).
/// 00 = no change, FF = fully lightened (white). Applied as a multiplier.
///
///
/// Gotcha: ThemeShade and ThemeTint are mutually exclusive — only one should
/// be set. If both are present, behavior is undefined.
///
///
public static void ApplyColor(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// Simple hex color (no theme)
rPr.Color = new Color { Val = "FF0000" }; // Red
// Theme-based color with fallback hex value
rPr.Color = new Color
{
Val = "2F5496", // Fallback hex for non-theme-aware renderers
ThemeColor = ThemeColorValues.Accent1, // Theme color slot
ThemeTint = "99" // Lighten: 99 hex → ~60% tint
};
// Theme color darkened
rPr.Color = new Color
{
Val = "1F3864",
ThemeColor = ThemeColorValues.Accent1,
ThemeShade = "BF" // Darken: BF hex → ~75% shade
};
// Auto color (application-determined, typically black on white)
rPr.Color = new Color { Val = "auto" };
}
// ──────────────────────────────────────────────────────────────────
// 6. Highlight (w:highlight)
// ──────────────────────────────────────────────────────────────────
///
/// Applies text highlighting (the "marker pen" effect in Word's UI).
///
/// All HighlightColorValues: Yellow, Green, Cyan, Magenta, Blue, Red,
/// DarkBlue, DarkCyan, DarkGreen, DarkMagenta, DarkRed, DarkYellow,
/// DarkGray, LightGray, Black, White, None.
///
///
/// Gotcha: Highlighting is limited to the 17 preset colors above.
/// For arbitrary background colors, use on RunProperties
/// instead — it supports any hex color.
///
///
public static void ApplyHighlight(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// Standard yellow highlight (most common for "tracked" or "review" marks)
rPr.Highlight = new Highlight { Val = HighlightColorValues.Yellow };
// All available highlight colors for reference:
// rPr.Highlight = new Highlight { Val = HighlightColorValues.Green };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.Cyan };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.Magenta };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.Blue };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.Red };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.DarkBlue };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.DarkCyan };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.DarkGreen };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.DarkMagenta };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.DarkRed };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.DarkYellow };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.DarkGray };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.LightGray };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.Black };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.White };
// rPr.Highlight = new Highlight { Val = HighlightColorValues.None }; // Remove
}
// ──────────────────────────────────────────────────────────────────
// 7. Strikethrough (w:strike, w:dstrike)
// ──────────────────────────────────────────────────────────────────
///
/// Applies strikethrough or double-strikethrough formatting.
///
/// Gotcha: w:strike and w:dstrike are mutually exclusive.
/// If both are present, behavior is undefined (Word typically uses the last one set).
///
///
public static void ApplyStrikethrough(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// Single strikethrough: a single horizontal line through the text
rPr.Strike = new Strike(); // No Val = true
// Double strikethrough: two horizontal lines through the text
// rPr.DoubleStrike = new DoubleStrike();
// To explicitly disable (override a style that has strikethrough):
// rPr.Strike = new Strike { Val = false };
}
// ──────────────────────────────────────────────────────────────────
// 8. Superscript / Subscript (w:vertAlign)
// ──────────────────────────────────────────────────────────────────
///
/// Applies superscript or subscript vertical alignment.
///
/// Values:
///
/// - Superscript — raised text, reduced size (e.g., x²)
/// - Subscript — lowered text, reduced size (e.g., H₂O)
/// - Baseline — normal position (use to override style)
///
///
///
/// Gotcha: This is NOT the same as .
/// VerticalTextAlignment changes both position AND size (like Word's superscript button).
/// Position (w:position) only shifts the baseline without changing font size.
///
///
public static void ApplySuperSubscript(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// Superscript (raised + smaller font)
rPr.VerticalTextAlignment = new VerticalTextAlignment
{
Val = VerticalPositionValues.Superscript
};
// Subscript (lowered + smaller font)
// rPr.VerticalTextAlignment = new VerticalTextAlignment
// {
// Val = VerticalPositionValues.Subscript
// };
// Baseline — explicitly reset to normal (override a style)
// rPr.VerticalTextAlignment = new VerticalTextAlignment
// {
// Val = VerticalPositionValues.Baseline
// };
}
// ──────────────────────────────────────────────────────────────────
// 9. Caps / Small Caps (w:caps, w:smallCaps)
// ──────────────────────────────────────────────────────────────────
///
/// Applies ALL CAPS or Small Caps display formatting.
///
/// Caps (w:caps): Displays all characters as uppercase. The underlying text
/// is NOT modified — it remains lowercase in the XML. This is a display-only transform.
///
///
/// SmallCaps (w:smallCaps): Displays lowercase letters as smaller uppercase
/// glyphs. Original uppercase letters remain full size. Common in legal and academic
/// documents for author names and section references.
///
///
/// Gotcha: w:caps and w:smallCaps are mutually exclusive.
/// If both are present, w:caps wins.
///
///
public static void ApplyCapsSmallCaps(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// ALL CAPS display (text stored as-is, displayed uppercase)
rPr.Caps = new Caps();
// Small Caps display (lowercase → small uppercase glyphs)
// rPr.SmallCaps = new SmallCaps();
// Disable (override a style):
// rPr.Caps = new Caps { Val = false };
}
// ──────────────────────────────────────────────────────────────────
// 10. Character Spacing (w:spacing)
// ──────────────────────────────────────────────────────────────────
///
/// Adjusts the spacing between characters (tracking / character spacing).
///
/// Unit: Value is in twips (1/20 of a point).
/// Positive values = expanded (letters spread apart).
/// Negative values = condensed (letters squeezed together).
///
///
/// Examples: 20 twips = 1pt expanded, -10 twips = 0.5pt condensed,
/// 40 twips = 2pt expanded.
///
///
/// Gotcha: This is NOT the same as kerning (w:kern).
/// Spacing applies a uniform offset between ALL characters.
/// Kerning adjusts spacing between specific character PAIRS based on font metrics.
///
///
public static void ApplyCharacterSpacing(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// Expanded by 1pt (20 twips)
rPr.Spacing = new Spacing { Val = 20 };
// Condensed by 0.5pt (-10 twips)
// rPr.Spacing = new Spacing { Val = -10 };
}
// ──────────────────────────────────────────────────────────────────
// 11. Position — raised/lowered baseline (w:position)
// ──────────────────────────────────────────────────────────────────
///
/// Raises or lowers the text position relative to the baseline.
///
/// Unit: Value is in half-points.
/// Positive values = raised above baseline.
/// Negative values = lowered below baseline.
///
///
/// Examples: 6 half-points = 3pt raised, -4 half-points = 2pt lowered.
///
///
/// Gotcha: Unlike , Position does NOT change
/// the font size. It only shifts the vertical position. Use this for fine-tuning
/// baseline alignment (e.g., aligning inline images with text).
///
///
public static void ApplyPosition(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// Raise text by 3pt (6 half-points)
rPr.Position = new Position { Val = "6" };
// Lower text by 2pt (-4 half-points)
// rPr.Position = new Position { Val = "-4" };
}
// ──────────────────────────────────────────────────────────────────
// 12. Run Shading (w:shd) — arbitrary background color on text
// ──────────────────────────────────────────────────────────────────
///
/// Applies shading (background color) to a run.
///
/// Use case: When you need a background color that is NOT one of the 17
/// preset highlight colors. Shading supports any hex RGB value.
///
///
/// Fill: The background color (hex RGB, e.g., "FFFF00" for yellow).
/// Val: The shading pattern. Use ShadingPatternValues.Clear for a
/// solid background fill (most common). Other patterns overlay a foreground color.
/// Color: The foreground/pattern color (only meaningful for non-Clear patterns).
///
///
/// Gotcha: If Val is omitted or set to Nil, the shading may not render.
/// Always set Val = Clear for solid backgrounds.
///
///
public static void ApplyShading(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// Solid light-blue background
rPr.Shading = new Shading
{
Val = ShadingPatternValues.Clear, // Solid fill (no pattern)
Fill = "DAEEF3", // Background color: light blue
Color = "auto" // Foreground/pattern color: auto
};
// Theme-colored shading
// rPr.Shading = new Shading
// {
// Val = ShadingPatternValues.Clear,
// Fill = "auto",
// ThemeFill = ThemeColorValues.Accent1,
// ThemeFillTint = "33" // Light tint of accent1
// };
}
// ──────────────────────────────────────────────────────────────────
// 13. Text Border (w:bdr)
// ──────────────────────────────────────────────────────────────────
///
/// Applies a border around a run of text.
///
/// Val: Border style — Single, Double, Dotted, Dashed, DotDash, DotDotDash,
/// Triple, ThickThinSmallGap, ThinThickSmallGap, ThickThinMediumGap, etc.
/// Use BorderValues.None to remove.
///
///
/// Size: Border width in eighths of a point. 4 = 0.5pt, 8 = 1pt, 12 = 1.5pt.
/// Valid range: 2–96 (0.25pt–12pt).
///
///
/// Space: Padding between text and border in points. Range: 0–31.
///
///
/// Gotcha: Run borders look like "boxed text" in Word. Adjacent runs with
/// borders will have separate boxes — they do NOT merge into one box.
///
///
public static void ApplyBorder(Run run)
{
var rPr = run.GetOrCreateRunProperties();
rPr.Border = new Border
{
Val = BorderValues.Single, // Single-line border
Size = 4, // 0.5pt wide (4 eighths of a point)
Space = 1, // 1pt padding between text and border
Color = "4472C4" // Border color (blue)
};
// Double border
// rPr.Border = new Border
// {
// Val = BorderValues.Double,
// Size = 4,
// Space = 1,
// Color = "auto"
// };
// Theme-colored border
// rPr.Border = new Border
// {
// Val = BorderValues.Single,
// Size = 8,
// Space = 1,
// Color = "auto",
// ThemeColor = ThemeColorValues.Accent1
// };
}
// ──────────────────────────────────────────────────────────────────
// 14. Run Style Reference (w:rStyle)
// ──────────────────────────────────────────────────────────────────
///
/// Applies a named character style to a run.
///
/// Val: The style ID (not the display name). For example, Word's built-in
/// "Strong" style has ID "Strong", "Emphasis" has ID "Emphasis".
/// Custom styles use their internal ID which may differ from the display name.
///
///
/// Gotcha: The style must exist in the document's styles.xml (StyleDefinitionsPart).
/// Referencing a non-existent style ID will not cause an error, but the formatting
/// defined by that style will not be applied — Word silently ignores unknown style IDs.
///
///
/// Gotcha: RunProperties set directly on the run override properties from the
/// style (direct formatting wins). To inherit everything from the style, do not set
/// additional properties on the RunProperties.
///
///
public static void ApplyRunStyle(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// Reference the built-in "Strong" character style (bold)
rPr.RunStyle = new RunStyle { Val = "Strong" };
// Common built-in character style IDs:
// "Strong" — Bold
// "Emphasis" — Italic
// "IntenseEmphasis" — Bold + Italic + Accent color
// "SubtleEmphasis" — Italic + gray color
// "BookTitle" — Small caps + spacing
// "IntenseReference" — Bold + Small caps + Accent color + Underline
// "SubtleReference" — Small caps + Accent color
// "Hyperlink" — Blue + Underline
// "FollowedHyperlink" — Purple + Underline
// "FootnoteReference" — Superscript
}
// ──────────────────────────────────────────────────────────────────
// 15. Hidden Text (w:vanish)
// ──────────────────────────────────────────────────────────────────
///
/// Makes text hidden (invisible in normal view, shown with dotted underline
/// when "Show/Hide" is toggled in Word).
///
/// Use cases: Hidden text for internal notes, index entries, TOC field codes.
/// Hidden text is NOT printed by default (controlled by Word's print settings).
///
///
/// Gotcha: Hidden text still participates in page layout calculations in some
/// modes. It can affect pagination when revealed.
///
///
public static void ApplyHiddenText(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// Make text hidden
rPr.Vanish = new Vanish();
// Explicitly un-hide (override a style that hides text):
// rPr.Vanish = new Vanish { Val = false };
}
// ──────────────────────────────────────────────────────────────────
// 16. Right-to-Left / Complex Script (w:rtl, w:cs)
// ──────────────────────────────────────────────────────────────────
///
/// Marks a run as right-to-left and/or complex script.
///
/// RightToLeft (w:rtl): Indicates the run contains right-to-left text.
/// This affects character ordering and cursor movement. Required for Arabic/Hebrew text.
///
///
/// ComplexScript (w:cs): Marks the run as containing complex script text.
/// When set, Word uses the ComplexScript variants of font properties:
/// w:szCs instead of w:sz, w:bCs instead of w:b, rFonts@cs instead of rFonts@ascii.
///
///
/// Gotcha: For Arabic/Hebrew content, you typically need BOTH w:rtl and w:cs.
/// Thai text needs w:cs but NOT w:rtl (Thai is left-to-right but uses complex shaping).
///
///
public static void ApplyRightToLeft(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// Mark as right-to-left (for Arabic/Hebrew)
rPr.RightToLeftText = new RightToLeftText();
// Mark as complex script (use CS font/size/bold/italic variants)
rPr.ComplexScript = new ComplexScript();
}
// ──────────────────────────────────────────────────────────────────
// 17. Emphasis Mark (w:em) — CJK emphasis dots
// ──────────────────────────────────────────────────────────────────
///
/// Applies emphasis marks (dots/circles above or below characters).
/// Primarily used in CJK (Chinese, Japanese, Korean) typography.
///
/// Values:
///
/// - Dot — small filled dot above each character (Japanese: 傍点)
/// - Comma — small comma-like mark above (used in some CJK styles)
/// - Circle — small open circle above each character
/// - UnderDot — small filled dot below each character (Chinese style)
/// - None — remove emphasis marks
///
///
///
/// Gotcha: Emphasis marks are distinct from underlines. They appear as individual
/// marks above/below each character, not as a continuous line.
///
///
public static void ApplyEmphasisMark(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// Dot emphasis (most common in Japanese)
rPr.Emphasis = new Emphasis { Val = EmphasisMarkValues.Dot };
// Other emphasis mark styles:
// rPr.Emphasis = new Emphasis { Val = EmphasisMarkValues.Comma };
// rPr.Emphasis = new Emphasis { Val = EmphasisMarkValues.Circle };
// rPr.Emphasis = new Emphasis { Val = EmphasisMarkValues.UnderDot };
// rPr.Emphasis = new Emphasis { Val = EmphasisMarkValues.None };
}
// ──────────────────────────────────────────────────────────────────
// 18. Kerning (w:kern)
// ──────────────────────────────────────────────────────────────────
///
/// Sets the kerning threshold for automatic font-based kerning.
///
/// Unit: Value is in half-points. Characters at or above this size
/// will have kerning applied (the font's kern table adjusts spacing between
/// specific character pairs, e.g., "AV", "To", "WA").
///
///
/// Common values:
///
/// - 0 — Disable kerning entirely
/// - 2 (1pt) — Kern all text (including body text)
/// - 28 (14pt) — Kern only headings (Word's typical default threshold)
///
///
///
/// Gotcha: Kerning only works if the font contains a kern table.
/// Most professional fonts (Times New Roman, Calibri, Arial) include kern data.
///
///
public static void ApplyKerning(Run run)
{
var rPr = run.GetOrCreateRunProperties();
// Kern text at 14pt and above (28 half-points)
rPr.Kern = new Kern { Val = 28 };
// Kern all text regardless of size (0 half-points is "no threshold"
// but some renderers interpret 0 as "off". Use 1 or 2 to be safe.)
// rPr.Kern = new Kern { Val = 2 };
}
// ──────────────────────────────────────────────────────────────────
// 19. Fully Formatted Run (combining multiple properties)
// ──────────────────────────────────────────────────────────────────
///
/// Creates a fully formatted run combining multiple character properties.
/// Demonstrates the correct way to build a run with RunProperties.
///
/// Key principle: Create RunProperties first, add all child elements,
/// then set it on the run BEFORE adding text. The run's XML structure must be:
/// <w:r><w:rPr>...</w:rPr><w:t>text</w:t></w:r>
///
///
/// Gotcha: If you add RunProperties AFTER the Text element, it will appear
/// after w:t in the XML, which is technically invalid OOXML ordering. Word tolerates
/// it but some third-party parsers may not. Always add rPr first.
///
///
public static Run CreateFullyFormattedRun()
{
// Build RunProperties with all desired formatting
var rPr = new RunProperties();
// 1. Style reference (must be first child per schema order)
rPr.RunStyle = new RunStyle { Val = "Strong" };
// 2. Font family
rPr.RunFonts = new RunFonts
{
Ascii = "Georgia",
HighAnsi = "Georgia",
EastAsia = "SimSun",
ComplexScript = "Times New Roman"
};
// 3. Bold
rPr.Bold = new Bold();
rPr.BoldComplexScript = new BoldComplexScript();
// 4. Italic
rPr.Italic = new Italic();
rPr.ItalicComplexScript = new ItalicComplexScript();
// 5. Caps — omitted here (mutually exclusive with SmallCaps)
// rPr.Caps = new Caps();
// 6. SmallCaps
rPr.SmallCaps = new SmallCaps();
// 7. Strikethrough
rPr.Strike = new Strike();
// 8. Hidden — typically NOT combined with visible formatting
// rPr.Vanish = new Vanish();
// 9. Color
rPr.Color = new Color { Val = "2F5496" };
// 10. Font size
rPr.FontSize = new FontSize { Val = "28" }; // 14pt
rPr.FontSizeComplexScript = new FontSizeComplexScript { Val = "28" };
// 11. Underline
rPr.Underline = new Underline { Val = UnderlineValues.Single };
// 12. Shading (text background)
rPr.Shading = new Shading
{
Val = ShadingPatternValues.Clear,
Fill = "FFFFCC"
};
// 13. Highlight (preset colors only)
// rPr.Highlight = new Highlight { Val = HighlightColorValues.Yellow };
// 14. Character spacing
rPr.Spacing = new Spacing { Val = 10 }; // 0.5pt expanded
// 15. Kerning threshold
rPr.Kern = new Kern { Val = 2 };
// 16. Position (raised/lowered)
// rPr.Position = new Position { Val = "4" }; // 2pt raised
// 17. Vertical alignment (super/subscript)
// rPr.VerticalTextAlignment = new VerticalTextAlignment
// {
// Val = VerticalPositionValues.Superscript
// };
// 18. Border
rPr.Border = new Border
{
Val = BorderValues.Single,
Size = 4,
Space = 1,
Color = "auto"
};
// Build the Run: RunProperties MUST come before Text content
var run = new Run();
run.RunProperties = rPr;
// Add text content
// PreserveSpace is needed when text has leading/trailing spaces
run.AppendChild(new Text("Fully formatted text")
{
Space = SpaceProcessingModeValues.Preserve
});
return run;
}
// ──────────────────────────────────────────────────────────────────
// 20. BuildRunProperties helper — recommended property order
// ──────────────────────────────────────────────────────────────────
///
/// Helper that constructs a RunProperties with elements in the correct schema order.
///
/// OOXML schema order for w:rPr children (ISO 29500-1, section 17.3.2.28):
///
/// - w:rStyle — Character style reference
/// - w:rFonts — Font family
/// - w:b — Bold
/// - w:bCs — Bold Complex Script
/// - w:i — Italic
/// - w:iCs — Italic Complex Script
/// - w:caps — All Caps
/// - w:smallCaps — Small Caps
/// - w:strike — Strikethrough
/// - w:dstrike — Double Strikethrough
/// - w:outline — Outline effect
/// - w:shadow — Shadow effect
/// - w:emboss — Emboss effect
/// - w:imprint — Imprint/Engrave effect
/// - w:noProof — Skip proofing
/// - w:snapToGrid — Snap to document grid
/// - w:vanish — Hidden text
/// - w:webHidden — Hidden in web view
/// - w:color — Text color
/// - w:spacing — Character spacing
/// - w:w — Character width scaling (%)
/// - w:kern — Kerning threshold
/// - w:position — Raised/lowered position
/// - w:sz — Font size
/// - w:szCs — Font size Complex Script
/// - w:highlight — Highlight color
/// - w:u — Underline
/// - w:effect — Animation effect (deprecated)
/// - w:bdr — Text border
/// - w:shd — Shading
/// - w:fitText — Fit text to width
/// - w:vertAlign — Vertical alignment (super/subscript)
/// - w:rtl — Right-to-left
/// - w:cs — Complex Script
/// - w:em — Emphasis mark
/// - w:lang — Language
/// - w:eastAsianLayout — East Asian typography
/// - w:specVanish — Special vanish
/// - w:oMath — Math formatting
/// - w:rPrChange — Revision tracking for run properties
///
///
///
/// Gotcha: When using the strongly-typed SDK (setting properties like
/// rPr.Bold = new Bold()), the SDK handles ordering automatically when
/// serializing. However, if you use rPr.AppendChild(), you must add
/// elements in the correct order yourself, or call
/// rPr.SetElement() which inserts at the correct position.
///
///
/// Font name for Ascii and HighAnsi slots. Null to skip.
/// Font size in points. Null to skip.
/// True to apply bold, false to explicitly disable, null to inherit.
/// True to apply italic, false to explicitly disable, null to inherit.
/// Six-digit hex color (e.g., "FF0000"). Null to skip.
/// Underline style. Null to skip.
/// A well-ordered RunProperties element ready to attach to a Run.
public static RunProperties BuildRunProperties(
string? fontFamily = null,
double? sizePoints = null,
bool? bold = null,
bool? italic = null,
string? colorHex = null,
UnderlineValues? underline = null)
{
var rPr = new RunProperties();
// Using the strongly-typed properties ensures the SDK serializes
// child elements in the correct schema order automatically.
if (fontFamily is not null)
{
rPr.RunFonts = new RunFonts
{
Ascii = fontFamily,
HighAnsi = fontFamily
};
}
if (bold == true)
{
rPr.Bold = new Bold();
rPr.BoldComplexScript = new BoldComplexScript();
}
else if (bold == false)
{
rPr.Bold = new Bold { Val = false };
rPr.BoldComplexScript = new BoldComplexScript { Val = false };
}
if (italic == true)
{
rPr.Italic = new Italic();
rPr.ItalicComplexScript = new ItalicComplexScript();
}
else if (italic == false)
{
rPr.Italic = new Italic { Val = false };
rPr.ItalicComplexScript = new ItalicComplexScript { Val = false };
}
if (colorHex is not null)
{
rPr.Color = new Color { Val = colorHex };
}
if (sizePoints is not null)
{
var halfPts = ((int)(sizePoints.Value * 2)).ToString();
rPr.FontSize = new FontSize { Val = halfPts };
rPr.FontSizeComplexScript = new FontSizeComplexScript { Val = halfPts };
}
if (underline is not null)
{
rPr.Underline = new Underline { Val = underline };
}
return rPr;
}
// ──────────────────────────────────────────────────────────────────
// Internal helper: get or create RunProperties on a Run
// ──────────────────────────────────────────────────────────────────
///
/// Gets the existing RunProperties from a run or creates and attaches a new one.
/// Ensures RunProperties is always the first child element of the run.
///
private static RunProperties GetOrCreateRunProperties(this Run run)
{
if (run.RunProperties is not null)
return run.RunProperties;
var rPr = new RunProperties();
run.RunProperties = rPr;
return rPr;
}
}