Files
mantle-ai-trader/skills/docx/references/common-rules.md
2026-06-06 05:21:10 +00:00

24 KiB
Executable File
Raw Blame History

Common Rules

Shared rules referenced by all scene files. Scene-specific overrides take precedence.

Default Page Layout

A4 portrait. Unless the scene specifies otherwise, use:

Property Value Twips
Page width 21.0 cm 11906
Page height 29.7 cm 16838
Top margin 2.54 cm 1440
Bottom margin 2.54 cm 1440
Left margin 3.0 cm 1701
Right margin 2.5 cm 1417
page: {
  size: { width: 11906, height: 16838, orientation: PageOrientation.PORTRAIT },
  margin: { top: 1440, bottom: 1440, left: 1701, right: 1417 },
}

Scene overrides:

  • Official doc (GB/T 9704 red-header): top 2098, bottom 1984, left 1588, right 1474
  • Exam: top/bottom 1134 (2 cm), left/right 1134 (2 cm)

Default Font Specifications

Two font profiles exist. Each scene declares which profile it uses.

Profile A: Formal (report, academic, contract, official-doc, exam)

Element CN Font EN Font Size Notes
H1 SimHei Times New Roman 16 pt (size: 32) Bold, centered
H2 SimHei Times New Roman 15 pt (size: 30) Bold
H3 SimHei Times New Roman 14 pt (size: 28) Bold
Body SimSun Times New Roman 12 pt (size: 24)
Caption SimSun Times New Roman 10.5 pt (size: 21)
  • Text color: always pure black "000000" (never dark-blue-grey)
  • First-line indent: 480 twips (2 chars at SimSun 12pt)
  • Line spacing: 312 (1.3x).
  • Color routing for non-report documents: When the document is a short-form text (essay, evaluation, letter, speech, application, reflection, etc.) rather than a structured report/whitepaper/proposal/consulting deliverable, heading color MUST use pure black "000000" instead of palette.primary. Colored headings are reserved for documents that need brand/professional identity (reports with covers, whitepapers, proposals, consulting deliverables).

Profile B: Visual (resume, copywriting)

Element CN Font EN Font Size
Name/Title Microsoft YaHei Calibri Varies
Body Microsoft YaHei Calibri 1011 pt
Caption Microsoft YaHei Calibri 9 pt
  • First-line indent: 420 twips (2 chars at YaHei)
  • Color: per design-system palette

Official-Doc Font Override (GB/T 9704)

When needsRedHeader() = true:

Element Font Size
Red header org name STXiaoBiaoSong (or SimSun bold) 26 pt (size: 52)
Title STXiaoBiaoSong (or SimHei) 22 pt (size: 44)
Body FangSong 16 pt (size: 32)
Section heading FangSong_GB2312 bold (or HeiTi) 16 pt (size: 32)
  • Line spacing: 560 (28 pt fixed)
  • First-line indent: 640 twips (2 chars at FangSong 16pt)

Chinese Font Size Reference

Name Points Half-points (size:)
Chu Hao (initial) 42 84
Xiao Chu 36 72
Yi Hao (1st) 26 52
Xiao Yi 24 48
Er Hao (2nd) 22 44
Xiao Er 18 36
San Hao (3rd) 16 32
Xiao San 15 30
Si Hao (4th) 14 28
Xiao Si 12 24
Wu Hao (5th) 10.5 21
Xiao Wu 9 18
Liu Hao (6th) 7.5 15

Placeholder Convention

When required information is missing, use standardized placeholders so users can Find & Replace in Word.

Format: Always use full-width brackets 【 】.

Type Format Example
General field 【field name】 Name: 【company name】
Monetary amount 【RMB in words: yuan (lowercase: ¥)】 Amount: 【RMB in words】
Date field 【____/____/____】 Signing date: 【//____】
Long text 【Please fill in: ______】 Delivery criteria: 【Please fill in: ______】
Attachment ref 【See Appendix 1: ______】

Rules:

  1. Placeholder format must be consistent throughout the entire document
  2. Each placeholder must specify exactly what is needed (never use vague "TBD" or "to be completed")
  3. Never hard-code unconfirmed critical facts; use a placeholder instead
  4. Never use sloppy expressions like "to be refined", "omitted", "user fills in later"

Title Orphan Prevention (All Scenes)

Body headings (H1/H2/H3) and cover titles must avoid leaving 12 characters alone on the last line. This rule applies to ALL document types.

For cover titles: Always use calcTitleLayout() + splitTitleLines() from design-system.md — these handle orphan prevention automatically (merges ≤2-char last lines into the previous line).

For body headings (H1/H2/H3): When a heading text is long enough to wrap, apply the same splitTitleLines() logic. If the heading would cause a single-character orphan in Word's auto-wrapping, manually split into multiple TextRun elements with a Break (soft line break) at a semantic boundary.

const { Break } = require("docx");

// Check if heading needs manual line break to prevent orphan
function buildHeadingRuns(text, maxCharsPerLine, runProps) {
  // If text fits in one line, no action needed
  if (text.length <= maxCharsPerLine) {
    return [new TextRun({ text, ...runProps })];
  }
  // Use splitTitleLines to find semantic break points
  const lines = splitTitleLines(text, maxCharsPerLine);
  const runs = [];
  for (let i = 0; i < lines.length; i++) {
    if (i > 0) runs.push(new TextRun({ break: 1, ...runProps, text: "" })); // soft line break
    runs.push(new TextRun({ text: lines[i], ...runProps }));
  }
  return runs;
}

Estimation for maxCharsPerLine: For centered headings, estimate available width = page width - left margin - right margin. For SimHei at a given pt size, each CJK char ≈ pt × 20 twips wide. Divide available width by char width to get maxCharsPerLine.


Undefined / Null Value Prevention (Mandatory)

Generated code MUST guard against outputting literal undefined, null, NaN, or empty strings for any visible text field. This is a hard requirement — these are never acceptable in a delivered document.

// ✅ MANDATORY: Safe text helper — use for ALL user-facing text values
function safeText(value, placeholder) {
  if (value === undefined || value === null || value === "" || String(value) === "NaN" || String(value) === "undefined") {
    return placeholder || "【Please fill in】";
  }
  return String(value);
}

// Usage:
new TextRun({ text: safeText(config.contact, "【Contact person】") })
new TextRun({ text: safeText(row.phone, "【Phone number】") })

Rules:

  1. Every TextRun displaying user-provided or config-derived data MUST use safeText() or equivalent guard
  2. If a field is optional and not provided, use 【Please fill in: field_name】 placeholder (full-width brackets)
  3. Table cells with missing data: show 【Please fill in】, never leave as empty string or undefined
  4. This applies to ALL scenes — contracts, reports, academic, exams, etc.

WPS / Office Word Compatibility (Mandatory)

Generated .docx files must render consistently in both Microsoft Office Word and WPS Office. The following OOXML features have known compatibility issues — avoid or use carefully.

Features to AVOID (high incompatibility risk)

Feature Issue Alternative
Text-character decorative lines (e.g., ───, ━━━, ═══, ——————) Character-drawn lines depend on font metrics and rendering engine — they appear different widths/lengths in MS Office vs WPS, often truncated or misaligned. They cannot span a controlled width. Always use paragraph borders (border.top, border.bottom) for horizontal decorative lines. Paragraph borders render consistently across engines and respect indent for precise width control. See recipe R2 for correct implementation.
Default table borders on cover wrapper tables (forgetting allNoBorders) docx-js default table borders are single/auto/sz=4. On the 16838-high cover wrapper, these borders add ~8 twips of extra height per edge. MS Office includes border thickness in height calculation, causing content to overflow by a few twips → blank page 2. WPS is more lenient and may absorb the overflow. Every cover wrapper table MUST explicitly set borders: allNoBorders (all 6 border positions = NONE). Never rely on defaults. Define the allNoBorders constant and use it consistently.
verticalAlign: "center" or "bottom" in exact-height TableRow WPS ignores vertical alignment in exact-height rows; content may clip or shift Use verticalAlign: "top" + spacing.before to position content. Avoid margins.top/margins.bottom in exact-height cells — they reduce available height unpredictably across engines
characterSpacing (large values) WPS renders differently from Word; letter spacing may collapse or expand Keep characterSpacing ≤ 80; for cover English labels, test both renderers
margins.top/margins.bottom inside exact-height cells MS Office and WPS calculate remaining height differently when cell margins are present Use spacing.before on the first paragraph for vertical positioning; only use margins.left/margins.right
Complex nested Tables inside exact-height cells WPS height calculation differs from Word; content may overflow or clip Wrap everything in a single 16838 outer wrapper cell (R1 architecture). Nested tables inside are acceptable when the outer wrapper provides a safety net
Large font without explicit spacing.line Paragraph inherits small line spacing from document default (e.g., 560tw for body); font taller than line height → top of characters clipped Always set spacing: { line: fontPt * 23, lineRule: "atLeast" } on paragraphs with font size > body text
ShadingType.SOLID WPS shows solid black instead of intended color Always use ShadingType.CLEAR
OOXML raw XML for columns (w:cols) WPS column rendering may differ Use only when explicitly needed (A3 exam papers); test output
titlePage: true with complex headers/footers WPS may not properly suppress first-page header/footer Use separate sections instead of titlePage flag
Tab stops for alignment WPS tab width may differ from Word Use borderless Tables for alignment instead

Features that are SAFE (consistent rendering)

Feature Notes
Borderless Tables for layout Both renderers handle well
ShadingType.CLEAR with fill color Consistent
rule: "exact" on single-level TableRow Works in both (avoid with nested Tables)
Paragraph borders (left, bottom, etc.) Consistent
spacing.before / spacing.after Consistent
Standard fonts (SimHei, SimSun, YaHei, TNR, Calibri) Available on both platforms
PageBreak inside Paragraph Consistent
Section breaks (SectionType.NEXT_PAGE) Consistent

Mandatory Compatibility Checks (Post-Generation)

Add to quality self-check:

  • No ShadingType.SOLID anywhere (search codebase)
  • No verticalAlign: "center" or "bottom" in exact-height rows
  • No tab-stop alignment for party info or data alignment (use Tables)
  • Covers use the 16838 outer wrapper architecture (R1 pattern) with spacing.before for positioning; no margins.top/margins.bottom in exact-height cells
  • Cover section margin = { top: 0, bottom: 0, left: 0, right: 0 } — non-zero margins cause wrapper to shrink away from page edges
  • Cover wrapper row has height: { value: 16838, rule: "exact" } — without this, content overflows or leaves whitespace
  • Cover is in a separate section from body content — cover and body must not share a section
  • Cover wrapper table uses explicit allNoBorders — never rely on default table borders (causes blank page 2 in MS Office)
  • No text-character decorative lines (───, ━━━, ═══, ——————) — use paragraph borders instead
  • characterSpacing values ≤ 80 throughout
  • TOC: follow references/toc.md checklist (heading style, TableOfContents element, PageBreak, post-processing script)
  • All tables use WidthType.PERCENTAGE for column widths (WPS tblGrid bug; if DXA is unavoidable, set columnWidths explicitly)
// ✅ Correct — percentage widths, WPS-safe
new Table({
  width: { size: 100, type: WidthType.PERCENTAGE },
  rows: [new TableRow({ children: [
    new TableCell({ width: { size: 30, type: WidthType.PERCENTAGE }, children: [...] }),
    new TableCell({ width: { size: 70, type: WidthType.PERCENTAGE }, children: [...] }),
  ]})],
});

// ❌ WRONG — DXA widths cause WPS tblGrid mismatch (all gridCol=100)
new TableCell({ width: { size: 3000, type: WidthType.DXA }, ... })

Universal Prohibitions

These apply to ALL scenes. Scene files may add scene-specific prohibitions.

  1. No outlines-only — always produce a complete, finished document
  2. No chat-style output — the document must not read like a conversation or explanation
  3. No fake TOC / page numbers / headers — use proper docx-js structures
  4. No excessive blank lines to pad layout
  5. No dirty formatting — no stray annotations, template fragments, broken hyperlinks, garbled markers
  6. No sloppy placeholders — "TBD", "omitted", "略", "to be refined" are forbidden; use proper 【】 placeholders
  7. No fabricated data — do not invent statistics, citations, legal references, or facts to appear professional
  8. No inconsistent heading/numbering — one numbering system per document, no level-skipping
  9. No Markdown artifacts — no #, **, - list markers, > blockquotes, and no Markdown table syntax (| col1 | col2 |, |---|---|) in the final docx. Any tabular data MUST be rendered as a proper docx Table object — never as plain-text pipe-delimited lines. This applies to ALL scenes including exam paper data tables, report statistics, and academic result tables.
  10. No bullet-list documents — body text must be proper paragraphs, not endless bullet points

Letter / Correspondence Format (Universal)

When generating any letter-style document (invitation letter, thank-you letter, cover letter, recommendation letter, English essay in letter format, etc.), the following layout rules apply regardless of scene:

  1. Complimentary close and sender name MUST be right-aligned — e.g., "Yours sincerely,", "Best regards,", "Yours,", and the sender name below it must use alignment: AlignmentType.RIGHT
  2. Date — if placed at the top of the letter, right-aligned; if at the bottom, right-aligned with the closing
  3. Salutation ("Dear Mr. Smith," / "Dear Mike,") — left-aligned, followed by a blank line or spacing.after
  4. Body paragraphs — left-aligned (English) or justified (CJK), with appropriate spacing.after between paragraphs
// ✅ Correct — closing and sender right-aligned
new Paragraph({ alignment: AlignmentType.RIGHT, spacing: { before: 400 },
  children: [new TextRun({ text: "Yours sincerely,", size: 24 })] }),
new Paragraph({ alignment: AlignmentType.RIGHT,
  children: [new TextRun({ text: "Li Hua", size: 24 })] }),

// ❌ WRONG — closing left-aligned (default)
new Paragraph({
  children: [new TextRun({ text: "Yours sincerely," })] }),

Quality Self-Check (Universal)

→ See SKILL.md § Post-Generation — Two-Layer Verification for the complete checklist.

Scene files add scene-specific checks on top of that universal checklist.

Execution Priority

When rules conflict, follow this precedence (highest first):

  1. User-provided template or explicit instructions — always override defaults
  2. Scene-specific rules — override common rules and design-system defaults
  3. Common rules (this file) — override design-system aesthetic defaults
  4. Design-system defaults — baseline aesthetics

Cover Recipes

See references/design-system.md for the 7 validated cover recipes (R1R7) and 14 color palettes.

Cover recipe selection: selectCoverRecipe(docType, industry, titleLength) — defined in references/design-system.md (authoritative source).


Cover Title Layout Rules (Mandatory)

These rules apply to ALL cover recipes (R1R7). They prevent the most common cover quality issues: title overflow, content spilling to page 2, and mid-word line breaks.

Rule 1: Always use calcTitleLayout()

Every cover MUST call calcTitleLayout(title, availableWidth) from design-system.md to determine:

  • Font size (dynamically calculated, never hardcoded above 40pt)
  • Line breaks (semantically split, never mid-word)

Forbidden: Passing the full title as a single long TextRun and letting Word auto-wrap. This causes uncontrolled line breaks at arbitrary character positions.

Rule 2: No single-character orphan lines

If the last line of a title contains only 12 characters, merge it into the previous line. The splitTitleLines() function handles this automatically.

Rule 3: No mid-word breaks for CJK text

Line breaks must occur at semantic boundaries: after particles (e.g., de/yu/he/ji/zhi), punctuation, connectors, spaces, or underscores. Never split a compound term (e.g., a 4-character term like a management specification must not be split into 3+1 characters).

For mixed Chinese+English titles (e.g., "基于Transformer架构的..."), use estimateTextWidth() instead of character count for line break calculation. Chinese characters are ~2× wider than English characters at the same font size.

Rule 4: Maximum 3 title lines on cover

Cover titles must not exceed 3 lines. If the title is too long, reduce font size (down to minimum 24pt) before adding more lines. If it still exceeds 3 lines at 24pt, force 3 lines with longer line lengths.

Rule 5: Always use calcCoverSpacing() for whitespace

Spacing values (spacing.before) in cover elements must be dynamically calculated, not hardcoded. Fixed values like before: 4500 assume a specific title length and will cause overflow with longer titles.

Rule 6: Cover height budget validation

Before generating, verify that total content height stays within 15638 twips (16838 page height minus 1200 twips safety margin — MS Office renders large fonts taller than calculated). Each recipe in design-system.md includes height budget annotations — verify during generation.

Rule 7: R5 meta info table (academic covers)

Academic cover meta info must use a 2-column table with percentage widths only (NOT DXA — WPS breaks with DXA widths):

  • Table width: adaptive 5575% of page, calculated by calcR5MetaLayout() in design-system.md. Table is centered via alignment: CENTER.
  • Label column: adaptive 2545% of table width, LEFT aligned, plain text label + "". NO full-width space padding, NO right-alignment, NO distributed alignment.
  • Value column: remaining percentage, LEFT aligned, bottom border single sz=4 = fixed-length underline (same length for all rows regardless of value text length).
  • Label column borders: none (NO bottom border on label cells).
  • ⚠️ Do NOT use DXA widths, full-width space padding (\u3000), spacer columns, or tab stops — these render inconsistently between MS Office and WPS.

Rule 8: Large font paragraphs must set explicit line spacing

When a paragraph uses a font size larger than the document body text (e.g., cover titles at 36pt+), it MUST set explicit spacing.line to prevent clipping. Without it, the paragraph inherits the document/style default line spacing (often 560 twips for body text), which is smaller than the font height → the top of characters gets clipped.

Formula: spacing.line = Math.ceil(fontPt * 23) with lineRule: "atLeast"

Example: A 36pt title needs spacing: { line: 828, lineRule: "atLeast" }. Without this, the inherited line=560 clips the top 160 twips of the text.

This applies to ALL large-font paragraphs (cover titles, chapter headings, decorative text), not just covers.

Rule 9: Every TextRun on a colored background MUST set explicit color

⚠️ CRITICAL: When a TextRun is inside a cell/area with a dark or colored background (shading), it MUST explicitly set the color property. Omitting color defaults to black (#000000), which is invisible on dark backgrounds.

Common mistake: Subtitle or meta text on R1/R2/R4 dark cover blocks without color → appears as invisible black text on dark bg.

Rule: For any TextRun inside a shaded cell:

  • Use P.cover.titleColor for title text
  • Use P.cover.subtitleColor for subtitle text
  • Use P.cover.metaColor for meta info text
  • Use P.cover.footerColor for footer text
  • NEVER rely on default color when background is not white

Rule 10: Page number API nesting and 3-section numbering

⚠️ CRITICAL: Page number settings MUST be nested inside page.pageNumbers:

// ❌ WRONG — docx-js ignores top-level pageNumberStart/pageNumberFormatType
properties: { pageNumberStart: 1, pageNumberFormatType: NumberFormat.DECIMAL }

// ✅ CORRECT
properties: { page: { pageNumbers: { start: 1, formatType: NumberFormat.DECIMAL } } }

Standard page numbering (5-zone convention):

All multi-section documents MUST follow this five-zone page numbering scheme unless the user explicitly requests otherwise.

Zone Section pageNumbers Footer instrText Notes
1. Cover Title page None (no footer) Always logical page 1, but number is hidden
2. Front matter Abstract, TOC, Preface { start: 1, formatType: UPPER_ROMAN } PAGE \* ROMAN \* MERGEFORMAT Separate Roman numeral sequence (i, ii, iii…)
3. Body Main content { start: 1, formatType: DECIMAL } PAGE \* arabic \* MERGEFORMAT Resets to 1
4. Appendix Appendices (A, B, C…) Continues body (no reset) Same as body No section break needed unless different headers required
5. References Bibliography Continues body (no reset) Same as body If body ends on p.42, references continue from p.43

Key rules: 0. NEVER use "Page X of Y" denominator format. Footer must show only the current page number (e.g., 1, 2, iii). Do NOT display total page count. No Page 3 of 12, no 3 / 12, no 第3页/共12页. Just the bare number. PageNumber.TOTAL_PAGES / NUMPAGES is FORBIDDEN in footers.

  1. Cover is always page 1 internally but the page number is never displayed. Suppress footer in cover section.
  2. Front matter uses independent Roman numerals starting at i. This sequence is separate from the body.
  3. Body resets to Arabic 1. The first page of main content is always page 1.
  4. Appendix and references continue the body sequence. No reset between body → appendix → references.
  5. Documents without front matter skip zone 2 (cover hidden, body starts at Arabic 1).
  6. Documents without cover start body (or front matter) at page 1 directly.
  7. Short documents (≤3 pages): simple Arabic 1, 2, 3 throughout, no cover/frontmatter distinction.
  8. Single-page documents (certificates, letters): no page numbering at all.

3-section docx-js implementation (for documents with TOC):

At minimum, implement zones 13 as separate docx sections:

// Section 1: Cover — no page number
properties: { page: { /* no pageNumbers */ } }
// No footer children, or empty footer

// Section 2: Front matter — Roman numerals
properties: { page: { pageNumbers: { start: 1, formatType: NumberFormat.UPPER_ROMAN } } }
// Footer: PAGE \* ROMAN \* MERGEFORMAT

// Section 3: Body — Arabic, reset to 1
properties: { page: { pageNumbers: { start: 1, formatType: NumberFormat.DECIMAL } } }
// Footer: PAGE \* arabic \* MERGEFORMAT

// Appendix and References: same section as body (continues numbering)
// Only create a new section if different header/footer content is needed

Post-processing required (WPS compatibility):

  1. Remove empty <w:pgNumType/> from cover section XML
  2. Patch footer instrText: replace bare PAGE with format-specific PAGE \* ROMAN or PAGE \* arabic

See toc.md § Page Number API for full details.