using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Wordprocessing; namespace MiniMaxAIDocx.Core.Samples; /// /// Exhaustive reference for every ParagraphProperties (w:pPr) 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 ParagraphFormattingSamples { // ────────────────────────────────────────────────────────────────── // 1. Justification / Alignment (w:jc) // ────────────────────────────────────────────────────────────────── /// /// Sets paragraph horizontal alignment (justification). /// /// All JustificationValues: /// /// Left — Left-aligned (default for LTR documents). Ragged right edge. /// Center — Centered text. /// Right — Right-aligned. Ragged left edge. /// Both — Justified: text stretches to fill the full line width. /// The last line is left-aligned. Word adjusts inter-word spacing. /// Distribute — Like justify, but also stretches the last line. /// Word adjusts both inter-word AND inter-character spacing. Used in CJK typography. /// ThaiDistribute — Special distribute mode for Thai script, /// which has unique spacing rules around vowel marks and tone marks. /// /// /// /// Gotcha: In RTL paragraphs (w:bidi), "Left" actually means the START edge /// (right side in RTL) and "Right" means the END edge. Use Start/End values if /// you need direction-independent alignment (not all renderers support them). /// /// public static void ApplyJustification(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); // Left-aligned (default for Western text) pPr.Justification = new Justification { Val = JustificationValues.Left }; // Center-aligned // pPr.Justification = new Justification { Val = JustificationValues.Center }; // Right-aligned // pPr.Justification = new Justification { Val = JustificationValues.Right }; // Justified (both edges flush, last line left-aligned) // pPr.Justification = new Justification { Val = JustificationValues.Both }; // Distribute (all lines justified including last, with inter-character spacing) // pPr.Justification = new Justification { Val = JustificationValues.Distribute }; // Thai distribute (specialized Thai script distribution) // pPr.Justification = new Justification { Val = JustificationValues.ThaiDistribute }; } // ────────────────────────────────────────────────────────────────── // 2. Indentation (w:ind) // ────────────────────────────────────────────────────────────────── /// /// Sets paragraph indentation: left, right, first-line, and hanging. /// /// Units: All values are in DXA (twentieths of a point). /// 1 inch = 1440 DXA, 1 cm ≈ 567 DXA, 1 pt = 20 DXA. /// /// /// Properties: /// /// Left — Left indent for the entire paragraph (shifts all lines right). /// Right — Right indent (shifts the right boundary left). /// FirstLine — Additional indent for the FIRST line only (added to Left). /// 720 DXA = 0.5 inch first-line indent. /// Hanging — The first line hangs LEFT of the paragraph body. /// Used for numbered/bulleted lists. Mutually exclusive with FirstLine. /// /// /// /// CJK character units: /// /// FirstLineChars — First-line indent in hundredths of a character width. /// 200 = 2 character widths. Takes precedence over FirstLine when set. /// LeftChars — Left indent in hundredths of a character width. /// RightChars — Right indent in hundredths of a character width. /// HangingChars — Hanging indent in hundredths of a character width. /// /// /// /// Gotcha: FirstLine and Hanging are mutually exclusive. If both are set, /// behavior is undefined. Setting one should clear the other. /// /// /// Gotcha: When using character-based units (FirstLineChars, etc.), /// the corresponding DXA value (FirstLine, etc.) should also be set as a fallback /// for renderers that do not support character-based indentation. /// /// public static void ApplyIndentation(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); // Standard indentation in DXA pPr.Indentation = new Indentation { Left = "720", // 0.5 inch left indent (720 DXA) Right = "360", // 0.25 inch right indent (360 DXA) FirstLine = "720" // 0.5 inch first-line indent (720 DXA) }; // Hanging indent (commonly used with bullets/numbering) // pPr.Indentation = new Indentation // { // Left = "720", // Overall paragraph indent // Hanging = "360" // First line hangs back 0.25 inch // // Effective first line position: 720 - 360 = 360 DXA from margin // }; // CJK character-based indent // pPr.Indentation = new Indentation // { // FirstLineChars = 200, // 2 character widths (200 hundredths) // FirstLine = "480" // DXA fallback (approx 2 chars at ~10.5pt SimSun) // }; } // ────────────────────────────────────────────────────────────────── // 3. Line Spacing (w:spacing — line, lineRule) // ────────────────────────────────────────────────────────────────── /// /// Sets the spacing between lines within a paragraph. /// /// LineRule values and their Line units: /// /// Auto — Line is in 240ths of a line (proportional). /// 240 = single spacing (1.0), 276 = 1.15 (Word default), 360 = 1.5, 480 = 2.0. /// Formula: value = desiredMultiplier * 240. /// Exact — Line is in DXA (twentieths of a point). /// The line height is fixed at exactly this value. Text may be clipped if too tall. /// Example: 240 DXA = 12pt exact line height. /// AtLeast — Line is in DXA. The line height is at least this /// value, but can grow larger to accommodate tall content (images, large fonts). /// Example: 240 DXA = at least 12pt. /// /// /// /// Gotcha: The unit of "Line" changes depending on LineRule! /// Auto = 240ths of a line, Exact/AtLeast = DXA (twips). This is a very common /// source of bugs. If you set Line="360" with LineRule=Auto, you get 1.5x spacing. /// If you set Line="360" with LineRule=Exact, you get 18pt fixed height. /// /// /// Gotcha: If LineRule is omitted, it defaults to Auto. /// /// public static void ApplyLineSpacing(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); // Single spacing (1.0x) — Auto mode, 240/240 = 1.0 // pPr.SpacingBetweenLines = new SpacingBetweenLines // { // Line = "240", // LineRule = LineSpacingRuleValues.Auto // }; // 1.15x spacing (Word's default) — Auto mode, 276/240 = 1.15 pPr.SpacingBetweenLines = new SpacingBetweenLines { Line = "276", LineRule = LineSpacingRuleValues.Auto }; // 1.5x spacing — Auto mode, 360/240 = 1.5 // pPr.SpacingBetweenLines = new SpacingBetweenLines // { // Line = "360", // LineRule = LineSpacingRuleValues.Auto // }; // Double spacing (2.0x) — Auto mode, 480/240 = 2.0 // pPr.SpacingBetweenLines = new SpacingBetweenLines // { // Line = "480", // LineRule = LineSpacingRuleValues.Auto // }; // Exact 14pt line height — no growing for tall content // pPr.SpacingBetweenLines = new SpacingBetweenLines // { // Line = "280", // 14pt × 20 DXA/pt = 280 DXA // LineRule = LineSpacingRuleValues.Exact // }; // At-least 12pt — minimum height, can grow // pPr.SpacingBetweenLines = new SpacingBetweenLines // { // Line = "240", // 12pt × 20 = 240 DXA // LineRule = LineSpacingRuleValues.AtLeast // }; } // ────────────────────────────────────────────────────────────────── // 4. Paragraph Spacing — Before/After (w:spacing — before, after) // ────────────────────────────────────────────────────────────────── /// /// Sets the space before and after a paragraph. /// /// Unit: Before and After are in DXA (twentieths of a point). /// 1pt = 20 DXA. Common values: 0 DXA = 0pt, 120 DXA = 6pt, 200 DXA = 10pt, /// 240 DXA = 12pt. /// /// /// CJK line units: /// /// BeforeLines — Space before in hundredths of a line. /// 100 = 1 line of space. Takes precedence over Before when set. /// AfterLines — Space after in hundredths of a line. /// /// /// /// Gotcha: Paragraph spacing collapses: when two paragraphs are adjacent, /// the space between them is the LARGER of paragraph1.After and paragraph2.Before, /// NOT the sum. This is standard Word behavior. /// /// /// Gotcha: can suppress spacing between /// paragraphs of the same style, overriding Before/After. /// /// /// BeforeAutoSpacing / AfterAutoSpacing: When set to true, Word auto-calculates /// the spacing (typically 14pt for HTML-imported paragraphs). Overrides Before/After. /// /// public static void ApplyParagraphSpacing(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); // 6pt before, 10pt after (typical body text spacing) pPr.SpacingBetweenLines = new SpacingBetweenLines { Before = "120", // 6pt × 20 = 120 DXA After = "200" // 10pt × 20 = 200 DXA }; // Combined with line spacing // pPr.SpacingBetweenLines = new SpacingBetweenLines // { // Before = "240", // 12pt before // After = "120", // 6pt after // Line = "276", // 1.15x line spacing // LineRule = LineSpacingRuleValues.Auto // }; // CJK line-based spacing // pPr.SpacingBetweenLines = new SpacingBetweenLines // { // BeforeLines = 50, // 0.5 line before // AfterLines = 100, // 1 line after // Before = "120", // DXA fallback // After = "240" // DXA fallback // }; // Auto spacing (used in HTML imports) // pPr.SpacingBetweenLines = new SpacingBetweenLines // { // BeforeAutoSpacing = true, // Word decides (typically 14pt) // AfterAutoSpacing = true // }; } // ────────────────────────────────────────────────────────────────── // 5. Pagination Control (w:keepNext, w:keepLines, w:widowControl, // w:pageBreakBefore) // ────────────────────────────────────────────────────────────────── /// /// Controls how a paragraph interacts with page breaks. /// /// Properties: /// /// KeepNext — Keeps this paragraph on the same page as the NEXT paragraph. /// Essential for headings (so a heading is never orphaned at the bottom of a page /// while its body text starts on the next page). /// KeepLines — Prevents a page break within this paragraph. /// All lines of the paragraph stay on the same page. If it doesn't fit, the entire /// paragraph moves to the next page. /// WidowControl — Prevents widows (a single last line of a paragraph at /// the top of a page) and orphans (a single first line at the bottom of a page). /// Default is ON. Set Val=false to allow widows/orphans. /// PageBreakBefore — Forces a page break immediately before this paragraph. /// Used for chapter headings and section starts. /// /// /// /// Gotcha: These properties can cause unexpected pagination behavior. /// A chain of KeepNext paragraphs can push an entire group to the next page. /// KeepLines on a very long paragraph can cause a full blank page. /// /// public static void ApplyKeepTogether(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); // Keep with next paragraph (typical for headings) pPr.KeepNext = new KeepNext(); // Keep all lines of this paragraph together pPr.KeepLines = new KeepLines(); // Widow/orphan control (on by default, explicitly setting here) pPr.WidowControl = new WidowControl(); // Force page break before this paragraph // pPr.PageBreakBefore = new PageBreakBefore(); // Disable widow/orphan control (allow widows/orphans) // pPr.WidowControl = new WidowControl { Val = false }; } // ────────────────────────────────────────────────────────────────── // 6. Outline Level (w:outlineLvl) // ────────────────────────────────────────────────────────────────── /// /// Sets the outline level for Table of Contents (TOC) integration /// and Navigation Pane display. /// /// Values: 0–8 (where 0 = top-level heading, 8 = deepest level). /// Level 9 (BodyTextLevel) means "body text" (not included in TOC). /// /// /// Relationship to heading styles: Word's built-in Heading 1 through Heading 9 /// styles have outlineLvl 0–8 respectively. You can assign an outline level to ANY /// paragraph style, making it appear in the TOC without using a Heading style. /// /// /// Gotcha: If you set outlineLvl directly on paragraphs (not in a style), /// each paragraph needs the property. It is more maintainable to define a style /// with the outline level and apply the style. /// /// public static void ApplyOutlineLevel(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); // Level 0 = equivalent to Heading 1 in TOC pPr.OutlineLevel = new OutlineLevel { Val = 0 }; // Level 1 = Heading 2 equivalent // pPr.OutlineLevel = new OutlineLevel { Val = 1 }; // Level 2 = Heading 3 equivalent // pPr.OutlineLevel = new OutlineLevel { Val = 2 }; // Body text (explicitly not in TOC) // pPr.OutlineLevel = new OutlineLevel { Val = 9 }; } // ────────────────────────────────────────────────────────────────── // 7. Paragraph Borders (w:pBdr) // ────────────────────────────────────────────────────────────────── /// /// Applies borders to a paragraph (top, bottom, left, right, between, bar). /// /// Border properties: /// /// Val — Border style. Common values: Single, Double, Dotted, Dashed, /// DotDash, DotDotDash, Triple, ThickThinSmallGap, ThinThickSmallGap, /// ThickThinMediumGap, ThinThickMediumGap, ThickThinLargeGap, ThinThickLargeGap, /// Wave, DoubleWave, DashSmallGap, DashDotStroked, ThreeDEmboss, ThreeDEngrave, /// Outset, Inset, None, Nil. /// Size — Width in eighths of a point. 4 = 0.5pt, 8 = 1pt, 12 = 1.5pt. /// Range: 2–96. /// Space — Distance from text to border in points. Range: 0–31. /// Color — Hex RGB color (e.g., "000000") or "auto". /// /// /// /// Between border: Renders between consecutive paragraphs that BOTH have the /// "between" border set. This is how Word creates a visually grouped block of bordered /// paragraphs without doubling up borders between them. /// /// /// Bar border: A vertical line at the start edge of the paragraph (left for LTR, /// right for RTL). Not the same as the left border — the bar appears in the margin area. /// /// public static void ApplyParagraphBorders(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); pPr.ParagraphBorders = new ParagraphBorders( // Top border new TopBorder { Val = BorderValues.Single, Size = 4, // 0.5pt Space = 1, // 1pt from text Color = "000000" }, // Bottom border new BottomBorder { Val = BorderValues.Single, Size = 4, Space = 1, Color = "000000" }, // Left border new LeftBorder { Val = BorderValues.Single, Size = 4, Space = 4, // 4pt from text Color = "000000" }, // Right border new RightBorder { Val = BorderValues.Single, Size = 4, Space = 4, Color = "000000" } ); // Add "between" border for consecutive bordered paragraphs // pPr.ParagraphBorders.AppendChild(new BetweenBorder // { // Val = BorderValues.Single, // Size = 4, // Space = 1, // Color = "000000" // }); // Add "bar" border (vertical bar in the margin) // pPr.ParagraphBorders.AppendChild(new BarBorder // { // Val = BorderValues.Single, // Size = 4, // Space = 0, // Color = "FF0000" // Red bar // }); } // ────────────────────────────────────────────────────────────────── // 8. Paragraph Shading (w:shd) // ────────────────────────────────────────────────────────────────── /// /// Applies a background color or pattern to the entire paragraph. /// /// Properties: /// /// Val — Shading pattern. Use ShadingPatternValues.Clear for a /// solid background (most common). Other patterns: HorizontalStripe, VerticalStripe, /// ReverseDiagonalStripe, DiagonalStripe, DiagonalCross, HorizontalCross, /// ThinHorizontalStripe, ThinVerticalStripe, Percent5 through Percent95, etc. /// Fill — Background color as hex RGB (e.g., "FFFF00"). "auto" = no fill. /// Color — Foreground/pattern color. Only visible with non-Clear patterns. /// "auto" = automatic. /// /// /// /// Theme-based shading: Use ThemeFill, ThemeFillTint, ThemeFillShade for /// theme-aware background colors. The Fill attribute serves as a fallback. /// /// public static void ApplyParagraphShading(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); // Solid light yellow background pPr.Shading = new Shading { Val = ShadingPatternValues.Clear, // Solid fill Fill = "FFFFCC", // Light yellow Color = "auto" }; // Theme-based background // pPr.Shading = new Shading // { // Val = ShadingPatternValues.Clear, // Fill = "D9E2F3", // Hex fallback // ThemeFill = ThemeColorValues.Accent1, // ThemeFillTint = "33" // Light tint // }; // Patterned shading (rare, but valid) // pPr.Shading = new Shading // { // Val = ShadingPatternValues.Percent10, // 10% dot pattern // Fill = "FFFFFF", // White background // Color = "000000" // Black dots // }; } // ────────────────────────────────────────────────────────────────── // 9. Tab Stops (w:tabs) // ────────────────────────────────────────────────────────────────── /// /// Defines custom tab stops with alignment and leader characters. /// /// Tab alignment values: /// /// Left — Text starts at the tab position (default). /// Center — Text is centered on the tab position. /// Right — Text ends at the tab position (text flows leftward). /// Decimal — Aligns on the decimal point (for numbers like 1,234.56). /// Bar — Draws a vertical bar at the tab position (text is not affected). /// Clear — Clears an inherited tab stop at this position. /// Number — Tab position for list numbering. /// /// /// /// Tab leader values: The character that fills the space before the tab stop. /// /// None — Blank space (default). /// Dot — Dots (. . . . . .) — common in TOC. /// Hyphen — Hyphens (- - - - -). /// Underscore — Continuous underline (__________). /// Heavy — Thick underline. /// MiddleDot — Middle dots (· · · · ·). /// /// /// /// Position unit: Tab stop position is in DXA (twentieths of a point). /// 1440 DXA = 1 inch, 720 DXA = 0.5 inch. /// /// /// Gotcha: Tab stops are cumulative with style-defined tabs unless you use /// a Clear tab to remove an inherited one. Order tab stops by position. /// /// public static void ApplyTabStops(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); pPr.Tabs = new Tabs( // Left tab at 1 inch new TabStop { Val = TabStopValues.Left, Position = 1440 // 1 inch = 1440 DXA }, // Center tab at 3 inches new TabStop { Val = TabStopValues.Center, Position = 4320 // 3 inches = 4320 DXA }, // Right tab at 6 inches with dot leader (TOC style) new TabStop { Val = TabStopValues.Right, Position = 8640, // 6 inches = 8640 DXA Leader = TabStopLeaderCharValues.Dot }, // Decimal tab at 4 inches (for aligning numbers) new TabStop { Val = TabStopValues.Decimal, Position = 5760 // 4 inches = 5760 DXA } ); // Clear an inherited tab stop at 2 inches // pPr.Tabs.AppendChild(new TabStop // { // Val = TabStopValues.Clear, // Position = 2880 // }); // Bar tab at 0.5 inch (draws a vertical line, does not move text) // pPr.Tabs.AppendChild(new TabStop // { // Val = TabStopValues.Bar, // Position = 720 // }); } // ────────────────────────────────────────────────────────────────── // 10. Numbering / List (w:numPr) // ────────────────────────────────────────────────────────────────── /// /// Associates a paragraph with a numbering definition (bulleted or numbered list). /// /// NumberingId (w:numId): References a numbering definition instance /// in the numbering.xml part (NumberingDefinitionsPart). This ID links to an /// AbstractNum that defines the list format. /// /// /// NumberingLevelReference (w:ilvl): The nesting level (0-based). /// 0 = top-level item, 1 = first sub-level, etc. Maximum depth: 8 (9 levels total). /// /// /// Gotcha: The numbering definition must exist in numbering.xml. /// Creating a paragraph with numPr that references a non-existent numId will cause /// Word to show an error or ignore the numbering. /// /// /// Gotcha: To remove numbering from a paragraph that inherits it from a style, /// set NumberingId to 0: new NumberingId { Val = 0 } /// /// /// NumberingChange: For tracked changes, wrap numPr changes in a /// NumberingChange element to record the revision. /// /// public static void ApplyNumbering(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); // Associate with numbering definition #1, level 0 (top-level) pPr.NumberingProperties = new NumberingProperties { NumberingLevelReference = new NumberingLevelReference { Val = 0 }, NumberingId = new NumberingId { Val = 1 } }; // Sub-level item (indented bullet/number) // pPr.NumberingProperties = new NumberingProperties // { // NumberingLevelReference = new NumberingLevelReference { Val = 1 }, // NumberingId = new NumberingId { Val = 1 } // }; // Remove numbering inherited from style // pPr.NumberingProperties = new NumberingProperties // { // NumberingId = new NumberingId { Val = 0 } // 0 = no numbering // }; } // ────────────────────────────────────────────────────────────────── // 11. Bidirectional (w:bidi, w:textDirection) // ────────────────────────────────────────────────────────────────── /// /// Sets the paragraph as right-to-left (for Arabic/Hebrew text). /// /// BiDi (w:bidi): When set, the paragraph direction is right-to-left. /// This affects: text flow direction, default alignment (right becomes default), /// indentation sides (left/right swap meaning), tab stop behavior. /// /// /// TextDirection (w:textDirection): Controls text flow direction within /// the paragraph's text area. Values include LrTb (left-to-right, top-to-bottom, /// default), TbRl (top-to-bottom, right-to-left — vertical CJK), BtLr /// (bottom-to-top, left-to-right — rotated). /// /// /// Gotcha: For RTL paragraphs, also set Justification to Right /// (which visually aligns to the RIGHT = start edge in RTL context). /// /// public static void ApplyBidirectional(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); // Set paragraph as right-to-left pPr.BiDi = new BiDi(); // Also set right-alignment (the "start" edge for RTL) pPr.Justification = new Justification { Val = JustificationValues.Right }; // Text direction for vertical CJK layout // pPr.TextDirection = new TextDirection // { // Val = TextDirectionValues.TopToBottomRightToLeft // Vertical CJK // }; // Text direction for rotated layout // pPr.TextDirection = new TextDirection // { // Val = TextDirectionValues.BottomToTopLeftToRight // 90° rotation // }; } // ────────────────────────────────────────────────────────────────── // 12. Contextual Spacing (w:contextualSpacing) // ────────────────────────────────────────────────────────────────── /// /// Suppresses Before/After spacing between consecutive paragraphs that /// share the same paragraph style. /// /// Use case: List items. When multiple "List Paragraph" items follow each other, /// contextual spacing removes the gap between them while preserving spacing when /// the list meets a different style (e.g., body text). /// /// /// How it works: When two adjacent paragraphs have the same ParagraphStyleId /// AND both have ContextualSpacing set, the Before spacing of the second paragraph /// and the After spacing of the first paragraph are suppressed (set to 0). /// /// /// Gotcha: Both paragraphs must have the same style AND contextual spacing /// enabled. If only one has it, the spacing is NOT suppressed. /// /// public static void ApplyContextualSpacing(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); pPr.ContextualSpacing = new ContextualSpacing(); // Disable (override a style that enables it): // pPr.ContextualSpacing = new ContextualSpacing { Val = false }; } // ────────────────────────────────────────────────────────────────── // 13. Mirror Indents (w:mirrorIndents) // ────────────────────────────────────────────────────────────────── /// /// Swaps left and right indentation on even/odd pages for book-style layouts. /// /// Use case: In bound documents (books, reports), you want wider inner margins /// (the binding side). On odd pages the binding is on the left; on even pages it's /// on the right. MirrorIndents makes "Left" become "Inside" and "Right" become "Outside". /// /// /// Gotcha: This property works in conjunction with the section's mirror margins /// setting (w:mirrorMargins in sectPr). If the section does not have mirror margins /// enabled, this property has limited effect. /// /// public static void ApplyMirrorIndents(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); pPr.MirrorIndents = new MirrorIndents(); } // ────────────────────────────────────────────────────────────────── // 14. Snap to Grid (w:snapToGrid) // ────────────────────────────────────────────────────────────────── /// /// Controls whether paragraph text aligns to the document grid. /// /// Document grid: Defined in the section properties (w:docGrid), the grid /// specifies a fixed layout for character and line placement. This is primarily /// used in CJK documents where characters should align to a uniform grid. /// /// /// Val = true (default): Text snaps to the grid positions, ensuring uniform /// character spacing and line heights across the page. /// /// /// Val = false: Text ignores the document grid. Useful for paragraphs /// that contain only Western text in a CJK document, where grid alignment /// would create too much spacing. /// /// public static void ApplySnapToGrid(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); // Disable grid snapping for this paragraph pPr.SnapToGrid = new SnapToGrid { Val = false }; // Re-enable (explicit, same as default) // pPr.SnapToGrid = new SnapToGrid { Val = true }; } // ────────────────────────────────────────────────────────────────── // 15. Suppress Auto-Hyphenation (w:suppressAutoHyphens) // ────────────────────────────────────────────────────────────────── /// /// Disables automatic hyphenation for this paragraph. /// /// Background: When document-level auto-hyphenation is enabled /// (in document settings), Word breaks long words at line endings with hyphens. /// This property overrides that for specific paragraphs. /// /// /// Use case: Disable hyphenation for headings, proper nouns, code blocks, /// or any text where breaking words would be inappropriate. /// /// public static void ApplySuppressAutoHyphens(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); pPr.SuppressAutoHyphens = new SuppressAutoHyphens(); // Re-enable auto-hyphenation (override style that suppresses): // pPr.SuppressAutoHyphens = new SuppressAutoHyphens { Val = false }; } // ────────────────────────────────────────────────────────────────── // 16. Paragraph Style (w:pStyle) // ────────────────────────────────────────────────────────────────── /// /// Applies a named paragraph style. /// /// Val: The style ID (not the display name). Built-in style IDs include: /// "Normal", "Heading1" through "Heading9", "Title", "Subtitle", /// "ListParagraph", "NoSpacing", "Quote", "IntenseQuote", /// "TOCHeading", "TOC1" through "TOC9", "Header", "Footer", /// "FootnoteText", "EndnoteText", "Caption", "Bibliography", etc. /// /// /// Gotcha: Style IDs are locale-independent (always English) even in /// non-English installations of Word. The display name is localized, but the /// ID stays the same. "Heading1" is always "Heading1" regardless of language. /// /// /// Gotcha: Custom styles use whatever ID was assigned at creation time. /// The ID may contain spaces or special characters. Always verify the actual /// style ID in styles.xml rather than guessing from the display name. /// /// /// Gotcha: If the referenced style does not exist in styles.xml, Word /// falls back to the "Normal" style silently. /// /// public static void ApplyParagraphStyle(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); // Apply Heading 1 style pPr.ParagraphStyleId = new ParagraphStyleId { Val = "Heading1" }; // Other common built-in style IDs: // pPr.ParagraphStyleId = new ParagraphStyleId { Val = "Normal" }; // pPr.ParagraphStyleId = new ParagraphStyleId { Val = "Heading2" }; // pPr.ParagraphStyleId = new ParagraphStyleId { Val = "Title" }; // pPr.ParagraphStyleId = new ParagraphStyleId { Val = "Subtitle" }; // pPr.ParagraphStyleId = new ParagraphStyleId { Val = "ListParagraph" }; // pPr.ParagraphStyleId = new ParagraphStyleId { Val = "NoSpacing" }; // pPr.ParagraphStyleId = new ParagraphStyleId { Val = "Quote" }; // pPr.ParagraphStyleId = new ParagraphStyleId { Val = "TOC1" }; // pPr.ParagraphStyleId = new ParagraphStyleId { Val = "Header" }; // pPr.ParagraphStyleId = new ParagraphStyleId { Val = "Footer" }; // pPr.ParagraphStyleId = new ParagraphStyleId { Val = "FootnoteText" }; // pPr.ParagraphStyleId = new ParagraphStyleId { Val = "Caption" }; } // ────────────────────────────────────────────────────────────────── // 17. Frame Properties (w:framePr) — positioned paragraph // ────────────────────────────────────────────────────────────────── /// /// Makes a paragraph into a positioned text frame (an anchored box of text). /// /// Use case: Drop caps, pull quotes, sidebar text, positioned labels. /// FrameProperties turns a paragraph into a floating frame that can be positioned /// relative to the page, margin, or text. /// /// /// Properties: /// /// Width (w) — Frame width in DXA. 0 = auto (fit content). /// Height (h) — Frame height in DXA. 0 = auto. /// HeightRule (hRule) — Auto, AtLeast, or Exact. /// HorizontalPosition (x) — Horizontal offset in DXA. /// VerticalPosition (y) — Vertical offset in DXA. /// HorizontalSpace (hSpace) — Horizontal clearance in DXA. /// VerticalSpace (vSpace) — Vertical clearance in DXA. /// Anchor — Vertical anchor: Text, Margin, or Page. /// AnchorLock — Prevents repositioning in Word UI. /// DropCap — DropCapLocationValues: None, Drop, Margin. /// Lines — Number of lines for a drop cap (typically 2–4). /// Wrap — Text wrapping: Auto, NotBeside, Around, Tight, Through, None. /// /// /// /// Gotcha: Frame properties are a legacy positioning mechanism. For modern /// documents, consider using DrawingML text boxes instead. However, framePr is still /// the standard way to create drop caps in OOXML. /// /// public static void ApplyFrameProperties(Paragraph para) { var pPr = para.GetOrCreateParagraphProperties(); // Drop cap: 3-line dropped initial capital pPr.FrameProperties = new FrameProperties { DropCap = DropCapLocationValues.Drop, Lines = 3, // Span 3 lines of body text HorizontalSpace = "72", // 72 DXA = ~3.6pt clearance Wrap = TextWrappingValues.Around // Body text wraps around }; // Positioned frame (floating text box) // pPr.FrameProperties = new FrameProperties // { // Width = "2880", // 2 inches wide // Height = "1440", // 1 inch tall // HeightRule = HeightRuleValues.AtLeast, // X = "4320", // 3 inches from anchor // Y = "1440", // 1 inch from anchor // HorizontalSpace = "144", // 0.1 inch horizontal clearance // VerticalSpace = "72", // ~3.6pt vertical clearance // VerticalAnchor = VerticalAnchorValues.Text, // Wrap = TextWrappingValues.Around, // AnchorLock = true // }; } // ────────────────────────────────────────────────────────────────── // 18. Fully Formatted Paragraph (combining multiple properties) // ────────────────────────────────────────────────────────────────── /// /// Creates a fully formatted paragraph combining multiple paragraph properties. /// Demonstrates the correct construction order and element nesting. /// /// Structure: /// <w:p><w:pPr>...</w:pPr><w:r>...</w:r></w:p> /// /// /// Key principle: ParagraphProperties must be the FIRST child of the paragraph. /// Runs, hyperlinks, and other content follow after. /// /// public static Paragraph CreateFullyFormattedParagraph() { // Build ParagraphProperties first var pPr = new ParagraphProperties(); // 1. Style (must be first child per schema) pPr.ParagraphStyleId = new ParagraphStyleId { Val = "Heading1" }; // 2. Pagination pPr.KeepNext = new KeepNext(); pPr.KeepLines = new KeepLines(); // 3. Page break // pPr.PageBreakBefore = new PageBreakBefore(); // 4. Widow/orphan control pPr.WidowControl = new WidowControl(); // 5. Numbering // pPr.NumberingProperties = new NumberingProperties // { // NumberingLevelReference = new NumberingLevelReference { Val = 0 }, // NumberingId = new NumberingId { Val = 1 } // }; // 6. Borders pPr.ParagraphBorders = new ParagraphBorders( new BottomBorder { Val = BorderValues.Single, Size = 8, Space = 4, Color = "4472C4" } ); // 7. Shading pPr.Shading = new Shading { Val = ShadingPatternValues.Clear, Fill = "F2F2F2" }; // 8. Tab stops pPr.Tabs = new Tabs( new TabStop { Val = TabStopValues.Right, Position = 9360, // Right margin tab Leader = TabStopLeaderCharValues.Dot } ); // 9. Suppress hyphenation pPr.SuppressAutoHyphens = new SuppressAutoHyphens(); // 10. Spacing (before/after + line spacing) pPr.SpacingBetweenLines = new SpacingBetweenLines { Before = "240", // 12pt before After = "120", // 6pt after Line = "276", // 1.15x line spacing LineRule = LineSpacingRuleValues.Auto }; // 11. Indentation pPr.Indentation = new Indentation { Left = "360", // 0.25 inch left indent FirstLine = "0" // No additional first-line indent }; // 12. Justification pPr.Justification = new Justification { Val = JustificationValues.Both }; // 13. Outline level (for TOC) pPr.OutlineLevel = new OutlineLevel { Val = 0 }; // 14. Paragraph-level run properties (default formatting for runs in this para) // This sets the DEFAULT run formatting — individual runs can override. pPr.ParagraphMarkRunProperties = new ParagraphMarkRunProperties( new RunFonts { Ascii = "Georgia", HighAnsi = "Georgia" }, new Bold(), new FontSize { Val = "28" }, // 14pt new Color { Val = "2F5496" } ); // Build the paragraph var para = new Paragraph(); para.ParagraphProperties = pPr; // Add a run with text var run = new Run( new RunProperties( new RunFonts { Ascii = "Georgia", HighAnsi = "Georgia" }, new Bold(), new FontSize { Val = "28" }, new Color { Val = "2F5496" } ), new Text("Chapter 1: Introduction") { Space = SpaceProcessingModeValues.Preserve } ); para.AppendChild(run); return para; } // ────────────────────────────────────────────────────────────────── // 19. BuildParagraphProperties helper — recommended property order // ────────────────────────────────────────────────────────────────── /// /// Helper that constructs ParagraphProperties with elements in the correct schema order. /// /// OOXML schema order for w:pPr children (ISO 29500-1, section 17.3.1.26): /// /// w:pStyle — Paragraph style reference /// w:keepNext — Keep with next paragraph /// w:keepLines — Keep lines together /// w:pageBreakBefore — Page break before /// w:framePr — Frame/text-box properties /// w:widowControl — Widow/orphan control /// w:numPr — Numbering properties /// w:suppressLineNumbers — Suppress line numbers /// w:pBdr — Paragraph borders /// w:shd — Shading /// w:tabs — Tab stops /// w:suppressAutoHyphens — Suppress auto-hyphenation /// w:kinsoku — CJK line-breaking rules /// w:wordWrap — Allow word-level wrapping (CJK) /// w:overflowPunct — Allow overflow punctuation (CJK) /// w:topLinePunct — Top-line punctuation compression (CJK) /// w:autoSpaceDE — Auto-space between CJK and Western text /// w:autoSpaceDN — Auto-space between CJK text and numbers /// w:bidi — Bidirectional (RTL paragraph) /// w:adjustRightInd — Auto-adjust right indent for grid /// w:snapToGrid — Snap to document grid /// w:spacing — Line and paragraph spacing /// w:ind — Indentation /// w:contextualSpacing — Contextual spacing /// w:mirrorIndents — Mirror indents for odd/even pages /// w:suppressOverlap — Suppress frame overlap /// w:jc — Justification (alignment) /// w:textDirection — Text direction /// w:textAlignment — Text vertical alignment within line /// w:textboxTightWrap — Text box tight wrap /// w:outlineLvl — Outline level /// w:divId — HTML div ID /// w:cnfStyle — Conditional formatting style /// w:rPr — Paragraph mark run properties /// w:sectPr — Section properties (last pPr in section) /// w:pPrChange — Revision tracking for paragraph properties /// /// /// /// Gotcha: Like RunProperties, when using strongly-typed SDK properties /// (e.g., pPr.Justification = new Justification { ... }), the SDK handles /// serialization order automatically. If using AppendChild(), you must /// maintain the schema order yourself. /// /// /// Paragraph style ID. Null to skip. /// Alignment. Null to skip. /// Space before in points. Null to skip. /// Space after in points. Null to skip. /// Line spacing multiplier (e.g., 1.0, 1.15, 1.5, 2.0). /// Null to skip. Only applies Auto line rule. /// Left indent in DXA. Null to skip. /// First line indent in DXA. Null to skip. /// Use negative value for hanging indent (will be set as Hanging). /// A well-ordered ParagraphProperties element. public static ParagraphProperties BuildParagraphProperties( string? styleId = null, JustificationValues? justification = null, double? spacingBeforePt = null, double? spacingAfterPt = null, double? lineSpacingMultiplier = null, int? leftIndentDxa = null, int? firstLineIndentDxa = null) { var pPr = new ParagraphProperties(); // Style reference (schema position 1) if (styleId is not null) { pPr.ParagraphStyleId = new ParagraphStyleId { Val = styleId }; } // Spacing (schema position 22) — combines para spacing and line spacing if (spacingBeforePt is not null || spacingAfterPt is not null || lineSpacingMultiplier is not null) { var spacing = new SpacingBetweenLines(); if (spacingBeforePt is not null) { // Points to DXA: 1pt = 20 DXA spacing.Before = ((int)(spacingBeforePt.Value * 20)).ToString(); } if (spacingAfterPt is not null) { spacing.After = ((int)(spacingAfterPt.Value * 20)).ToString(); } if (lineSpacingMultiplier is not null) { // Auto mode: multiplier × 240 = value spacing.Line = ((int)(lineSpacingMultiplier.Value * 240)).ToString(); spacing.LineRule = LineSpacingRuleValues.Auto; } pPr.SpacingBetweenLines = spacing; } // Indentation (schema position 23) if (leftIndentDxa is not null || firstLineIndentDxa is not null) { var ind = new Indentation(); if (leftIndentDxa is not null) { ind.Left = leftIndentDxa.Value.ToString(); } if (firstLineIndentDxa is not null) { if (firstLineIndentDxa.Value >= 0) { ind.FirstLine = firstLineIndentDxa.Value.ToString(); } else { // Negative value → hanging indent (positive DXA stored in Hanging) ind.Hanging = Math.Abs(firstLineIndentDxa.Value).ToString(); } } pPr.Indentation = ind; } // Justification (schema position 26) if (justification is not null) { pPr.Justification = new Justification { Val = justification }; } return pPr; } // ────────────────────────────────────────────────────────────────── // Internal helper: get or create ParagraphProperties // ────────────────────────────────────────────────────────────────── /// /// Gets the existing ParagraphProperties or creates and attaches a new one. /// Ensures ParagraphProperties is always the first child of the paragraph. /// private static ParagraphProperties GetOrCreateParagraphProperties(this Paragraph para) { if (para.ParagraphProperties is not null) return para.ParagraphProperties; var pPr = new ParagraphProperties(); para.ParagraphProperties = pPr; return pPr; } }