Files
mantle-ai-trader/skills/docx/scenes/exam.md
2026-06-06 05:21:10 +00:00

25 KiB
Executable File
Raw Blame History

Scene: Exam Paper

Overview

Exam papers are among the most critical document types in education. Unlike general documents, they require high precision in layout, print compatibility, and subject-specific formatting. This specification covers the complete workflow from page framework to subject-specific features.

→ Universal prohibitions — see references/common-rules.mdNote: Exam papers use their OWN font/layout specs (not Profile A defaults). All text is pure black/white/grey for photocopy clarity.


1. Page Setup & Framework

Paper Specifications

Type Paper Orientation Use Case
Practice / Unit quiz A4 Portrait Daily practice, homework, quizzes
Formal exam A3 Landscape + 2-column Midterm / final / standardized (requires OOXML)
Answer sheet A4 Portrait Standalone answer card

Margins

// A4 portrait — no seal line
page: { size: { width: 11906, height: 16838 },
  margin: { top: 850, bottom: 850, left: 1200, right: 1200 } }

// A4 portrait — with seal line (left binding area reserved)
page: { size: { width: 11906, height: 16838 },
  margin: { top: 850, bottom: 850, left: 2200, right: 850 } }

// A3 landscape dual-column (requires OOXML)
// ⚠️ A3 dual-column may render slightly differently in WPS vs Word. Test in both before batch printing.
page: { size: { width: 23812, height: 16838, orientation: PageOrientation.LANDSCAPE },
  margin: { top: 850, bottom: 850, left: 2200, right: 850 } }

Section Handling

Different parts should use section breaks (SectionType.NEXT_PAGE):

  • Header area (full-width): Title, instructions, score table (no columns)
  • Content area: Questions (may use columns)
  • Composition / answer sheet: Independent section, independent format
  • Attachment pages: Large maps/diagrams for geography/biology can be separate pages
sections: [
  { properties: { /* Header section — no columns */ }, children: [...] },
  { properties: { type: SectionType.CONTINUOUS, column: { count: 2, space: 720 } }, children: [...] },
  { properties: { type: SectionType.NEXT_PAGE }, children: [...] }, // Composition
]

Template-First Principle

⚠️ Build framework first, fill content second. Before writing questions, determine:

  1. Paper size + margins
  2. Whether seal line is needed
  3. Whether columns are used
  4. Question type structure and point allocation
  5. Whether composition grid / answer sheet is needed

2. Seal Line & Student Information Area

When to Use Seal Line

Scenario Seal Line Student Info Position
Formal standardized exam Required Left vertical info column
Midterm / Final Recommended Left vertical info column
Unit quiz Optional Header horizontal info row
Daily practice Skip Header horizontal info row

Seal Line Implementation

Method 1: Header horizontal prompt (simple)

headers: { default: new Header({ children: [
  new Paragraph({ alignment: AlignmentType.CENTER,
    children: [new TextRun({
      text: ".............. Seal ...... Line ...... Do ...... Not ...... Answer ...... Inside ..............",
      size: 16, color: "999999", font: "SimSun" })] })
] }) }

Method 2: Vertical text box (OOXML advanced)

<w:txbxContent>
  <w:p><w:pPr><w:jc w:val="center"/></w:pPr>
    <w:r><w:rPr><w:sz w:val="18"/><w:color w:val="999999"/></w:rPr>
      <w:t>Name:________  Class:________  ID:________</w:t></w:r>
  </w:p>
  <w:p><w:r><w:rPr><w:sz w:val="16"/><w:color w:val="CCCCCC"/></w:rPr>
      <w:t>- - - - - - - - - Seal Line - - - - - - - - -</w:t></w:r>
  </w:p>
</w:txbxContent>

Student Info Row

// Horizontal info row (when no seal line) — borderless 3-column table
new Table({
  alignment: AlignmentType.CENTER, columnWidths: [2800, 2800, 2800],
  rows: [new TableRow({ children: [
    cell("Name: ______________"),
    cell("Class: ______________", AlignmentType.CENTER),
    cell("ID: ______________", AlignmentType.RIGHT),
  ] })]
})

Fill lines should be moderate length (1014 underscore chars). Label order: Name → Class → Student ID.


3. Paper Header & Title Area

Structure

School name (16pt SimHei, centered)
Exam title (14pt SimHei, centered) — e.g., "20252026 Academic Year Second Semester Midterm"
Subject title (14pt SimHei, centered) — e.g., "Grade 7 Mathematics"
Student info row
Instructions (10pt SimSun, centered, grey)
Score table (as needed)

Font Specifications

Element Font Size Style
School name SimHei 16pt (size:32) Bold, centered
Exam title SimHei 14pt (size:28) Bold, centered
Subject title SimHei 14pt (size:28) Bold, centered
Instructions SimSun 10pt (size:20) Grey 333333, centered
Student info SimSun 10.5pt (size:21) Normal

Instructions Content

Should include: total score, exam duration, answer method, special requirements (e.g., calculator allowed).

Score Table

  • Header row: light grey background F0F0F0, centered
  • Columns: Question type | Section names... | Total
  • Rows: Points | Section points... | Total points
  • Row: Score | blank... | blank
  • Table centered, 80% page width

⚠️ Header area should not be too full — title + info + instructions + score table should not exceed 1/3 of the page.


4. Content Layout Rules

Color Palette

// Exam papers use only black/white/grey for clear photocopying
const C = {
  title: "000000", body: "000000", section: "333333",
  seal: "999999", answerLine: "CCCCCC", headerBg: "F0F0F0", gridLine: "DDDDDD",
};

Column Usage

Subject / Question Type Recommendation
Math multiple choice + fill-in Suitable for columns
Physics multiple choice Suitable for columns
Chinese reading / composition Not suitable
English cloze / reading Not suitable
History source-based Not suitable
Geography map reading Not suitable

Question Numbering

Entire paper uses consistent three-level numbering:

  • Major sections: I, II, III, IV... (Chinese: 一、二、三、四…)
  • Questions: 1. 2. 3. ... (Arabic + period)
  • Sub-questions: (1) (2) (3) ... (parenthesized)

⚠️ No extra symbols before question numbers (no , , , -, *). The number itself is the only marker. Never use docx numbering/bullet list styles for question numbers — must use plain TextRun manual numbering.

// ✅ Correct — plain TextRun manual numbering
new Paragraph({ spacing: { before: 120, after: 60, line: 360 },
  children: [new TextRun({ text: `${i+1}. ${question}`, size: 21, font: { eastAsia: "SimSun" } })] })

// ❌ Wrong — numbering causes Word to add bullets
new Paragraph({ numbering: { reference: "xxx", level: 0 }, // ← Forbidden!
  children: [new TextRun({ text: question })] })

Question Spacing

sectionTitle: { before: 300, after: 150 }  // Major section headers
question: { before: 120, after: 80 }       // Between questions
subQuestion: { before: 60, after: 40 }     // Between sub-questions

Page Break Control

⚠️ Key principles:

  • Question stem and answer area must not split across pages
  • Source material and questions on same page
  • Figures adjacent to their questions
  • Avoid orphan lines — question stem, options, answer area appear as a group
new Paragraph({ keepNext: true, keepLines: true, children: [...] })

⚠️ Answer question page break rule (mandatory):

Complete combination (stem + figure + answer lines) must be considered as a unit. If remaining space cannot fit stem + figure + at least 3 answer lines, push entire question to next page.

Use keepNext: true to chain: stem → figure → first 3 answer lines.


5. Font & Paragraph Standards

Underline Formatting for "Underlined Parts" (Mandatory)

When a question references "underlined part" (划线部分), the relevant text MUST use actual underline formatting (underline: { type: UnderlineType.SINGLE }). Never show "划线部分为 XXX" as plain text annotation — the underline must be visually rendered.

// ✅ Correct — actual underline on the referenced text
new Paragraph({ children: [
  new TextRun({ text: "1. It is ", size: 21, font: { ascii: "Times New Roman" } }),
  new TextRun({ text: "a butterfly", size: 21, font: { ascii: "Times New Roman" },
    underline: { type: UnderlineType.SINGLE, color: "000000" } }),
  new TextRun({ text: ". (Ask about the underlined part)", size: 21, font: { ascii: "Times New Roman" } }),
]})

// ❌ Wrong — underlined part described as annotation text
new TextRun({ text: "1. It is a butterfly. (对划线部分提问) 注:划线部分为 a butterfly" })

Font Hierarchy

Element Font Size Style
Section title SimHei 11pt (size:22) Bold
Question content SimSun 10.5pt (size:21) Normal
Points annotation SimSun 10pt (size:20) In parentheses
Reading material KaiTi/SimSun 10.5pt (size:21) KaiTi to differentiate
Notes/source SimSun 9pt (size:18) Grey 666666
Seal line SimSun 8pt (size:16) Grey 999999
Page number SimSun 9pt (size:18) Centered

Line Spacing

line: 360  // ~1.5x for readability
answerLine: 500  // Answer line spacing for writing room

Paragraph Rules

  • ⚠️ Never use consecutive returns for whitespace — use spacing.before/after
  • Chinese questions use Chinese punctuation; English materials use English punctuation
  • Mixed CN/EN: use Times New Roman or Calibri for English text

6. Multiple Choice Layout

Core Rule

⚠️ Options must NEVER be aligned with spaces! Must use borderless tables.

Option Layout — Borderless Table

// Short options: 4 columns in 1 row
new Table({
  columnWidths: [2200, 2200, 2200, 2200],
  rows: [new TableRow({ children: ["A","B","C","D"].map((label, i) =>
    new TableCell({ borders: NBs, width: { size: 2200, type: WidthType.DXA },
      margins: { top: 0, bottom: 0, left: 60, right: 60 },
      children: [new Paragraph({ spacing: { before: 0, after: 0 },
        children: [new TextRun({ text: `${label}. ${options[i]}`, size: 21, font: "SimSun" })] })]
    })
  ) })]
})
// Medium options: 2 columns, 2 rows
// Long options: 1 column, 4 rows

Option Length Detection

function getOptionLayout(options) {
  const maxLen = Math.max(...options.map(o => o.length));
  if (maxLen <= 6) return "4col";
  if (maxLen <= 15) return "2col";
  return "1col";
}

7. Fill-in-the-Blank Layout

// Blank line length matches expected answer:
// Short answer (number/word): 8 underscores
// Medium (phrase): 14 underscores
// Long (sentence): 20 underscores
new Paragraph({ spacing: { before: 140, after: 80, line: 400 },
  children: [new TextRun({ text: `${num}. Question text ________________.`, size: 21, font: "SimSun" })] })

⚠️ Fill-in lines must not break across lines — if line is too long, put the blank on the next line.


8. Short Answer / Problem-Solving Layout

Question + Points

new Paragraph({ spacing: { before: 200, after: 60, line: 360 }, keepNext: true,
  children: [new TextRun({ text: `${num}. (${points} pts) ${question}`, size: 21, font: "SimSun" })] })

Answer Lines

// Light grey answer lines (CCCCCC), NOT black
// ⚠️ Answer lines are ONLY for writing space within each question — never as dividers between questions
function answerLines(count) {
  return Array(count).fill(null).map(() =>
    new Paragraph({ spacing: { before: 0, after: 0, line: 500 },
      borders: { bottom: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" } },
      children: [new TextRun({ text: " ", size: 21 })] })
  );
}

⚠️ Separation between questions:

Use only spacing (spacing.before: 200) for visual separation between questions. Forbidden:

  • Grey horizontal lines (borders)
  • Color block dividers (Table-simulated separators)
  • Symbol dividers (e.g., ───────)
  • Any visual separator decoration

Answer Space vs. Points

Points Suggested Lines Description
24 34 lines Simple calculation / short answer
58 68 lines Medium problem
1012 810 lines Complex problem
1420 1014 lines Comprehensive / essay question

9. Source-Based / Reading Question Layout

Material vs. Question Separation

// Material area — indented + KaiTi to differentiate
new Paragraph({ indent: { left: 420, right: 420 }, spacing: { before: 100, after: 100, line: 380 },
  children: [new TextRun({ text: materialText, size: 21, font: "KaiTi" })] })
// Source attribution
new Paragraph({ alignment: AlignmentType.RIGHT, indent: { right: 420 },
  children: [new TextRun({ text: "— from \"XXX\"", size: 18, color: "666666", font: "SimSun" })] })

Key Principles

  • Material title, source, body, and notes use different fonts
  • Long materials: increase line spacing (line: 380400)
  • Material and corresponding questions on same page
  • Sub-question numbers (1)(2)(3) clearly correspond to material
  • Data tables in materials MUST use proper docx Table objects — never render tabular data as Markdown plain text (| col | col |). This includes statistics tables, climate data tables, comparison tables, and any structured data within question materials. Use bordered tables (see § 13 Table Usage Standards) with appropriate header row styling.

10. Composition / Writing Area

Grid Count Calculation

⚠️ Grid count must exceed required word count by 2030% (for title, paragraph indents, line breaks).

Required Words Min Grid Count Recommended Layout
400 500 25 rows × 20 cols
600 750 38 rows × 20 cols
800 1000 50 rows × 20 cols
1000 1250 63 rows × 20 cols
function calcGridSize(requiredWords, colsPerRow = 20) {
  const totalCells = Math.ceil(requiredWords * 1.25);
  const rows = Math.ceil(totalCells / colsPerRow);
  return { rows, colsPerRow, totalCells: rows * colsPerRow };
}

Chinese Composition Grid

function compositionGrid(rows, colsPerRow) {
  const cellSize = Math.floor(8800 / colsPerRow);
  return new Table({
    columnWidths: Array(colsPerRow).fill(cellSize),
    rows: Array(rows).fill(null).map(() =>
      new TableRow({
        height: { value: cellSize, rule: HeightRule.EXACT },
        children: Array(colsPerRow).fill(null).map(() =>
          new TableCell({ borders: thinBs("DDDDDD"), width: { size: cellSize, type: WidthType.DXA },
            children: [new Paragraph({ children: [] })] })
        )
      })
    )
  });
}

English Writing Area (Horizontal Lines) — MANDATORY for English Writing Questions

⚠️ Every English writing/composition question MUST include ruled horizontal lines. A blank area without lines is FORBIDDEN — students need lines to write on.

function writingLines(count) {
  return Array(count).fill(null).map(() =>
    new Paragraph({ spacing: { before: 0, after: 0, line: 560 },
      borders: { bottom: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" } },
      children: [new TextRun({ text: " ", size: 21 })] })
  );
}

Line count by word requirement:

Required Words Lines
≤50 8
5080 10
80120 12
120+ 15

Rules:

  1. Lines must appear immediately after the writing prompt paragraph
  2. Line color: light grey CCCCCC (print-friendly, not visually heavy)
  3. Line spacing: line: 560 (provides adequate writing room)
  4. Chinese composition uses grid (compositionGrid), English uses lines (writingLines) — never mix them up

### Composition Area Requirements
- Independent section or clear separation
- Title space reserved (for self-chosen topics)
- Word count prompt visible ("No fewer than 800 words" / "About 120 words")
- Grid/line colors light — must not interfere with writing
- Pages continuous, not split

---

## 11. Answer Key (参考答案)

### Output Rules

1. **Default (user does not request answers in the same file):** Generate the answer key as a **separate .docx file** (e.g., `exam.docx` + `exam_answers.docx`). This prevents students from accidentally seeing answers.
2. **User explicitly requests answers in the same file:** Place the answer key on an **independent page** using `SectionType.NEXT_PAGE`. Answer key MUST NOT appear on the same page as any exam question.

### Separate File Format (Default)

The answer key file should include:
- Title: "《{exam title}》参考答案" (SimHei, 14pt/size:28, bold, centered)
- Same question numbering as the exam
- Concise answers (letter choices, key words, short solutions)
- Font: SimSun 10.5pt (size: 21)

### Same File Format (When User Requests)

```js
// Answer key as a separate section — MUST use SectionType.NEXT_PAGE
{
  properties: { type: SectionType.NEXT_PAGE,
    page: { margin: { top: 850, bottom: 850, left: 1200, right: 1200 } } },
  children: [
    new Paragraph({
      alignment: AlignmentType.CENTER, spacing: { after: 300 },
      children: [new TextRun({ text: "参考答案", size: 28, bold: true,
        font: { eastAsia: "SimHei" } })],
    }),
    // ... answer content paragraphs
  ],
}

Rules

  1. ⚠️ Never place answer content directly after the last question without a page/section break
  2. Answer content should be concise — no answer lines, no grid, plain text only
  3. Calculation/proof questions: show key steps, not just final answer
  4. If the exam has figures, answers may reference "see Figure X" without re-embedding

12. Figures & Illustrations

Image Insertion

new Paragraph({ alignment: AlignmentType.CENTER, spacing: { before: 100, after: 60 },
  children: [new ImageRun({ data: imageBuffer, transformation: { width: 300, height: 200 }, type: "png" })] })
new Paragraph({ alignment: AlignmentType.CENTER, spacing: { after: 100 },
  children: [new TextRun({ text: "(Figure 1)", size: 18, color: "666666", font: "SimSun" })] })

Key Principles

  • Images set as inline (default) to prevent floating
  • Resolution sufficient for print clarity
  • B&W print compatible: images must remain distinguishable when printed in grayscale
  • Figure numbers and captions complete
  • Figures adjacent to corresponding questions
  • Maps must have: scale bar, north arrow, legend
  • Coordinate graphs must have: axis labels, tick marks, units

⚠️ Figure-Text Order (Strictly Enforced)

For questions with figures, element order must be:

1. Question stem (keepNext: true)
2. Figure (centered, keepNext: true)
3. Answer lines / answer area

Forbidden: answer lines between stem and figure, or figure after answer lines.

Figure Content Matching

  • Figures must be semantically consistent with question stem: if question says "triangle ABC", figure must label vertices A, B, C
  • Geometry annotations must match described angles, side lengths
  • Function graphs must mark key points mentioned in the question
  • Physics experiment diagrams must match described apparatus
  • Figure width: geometry ≤ 50% page width, data/experiment ≤ 70%

⚠️ Figure Diversity Rule (Mandatory)

No duplicate figures in the entire paper. Even if two questions involve the same type (e.g., both triangles), each must have a distinct figure:

  1. Different labels (different vertex letters, angles, side lengths)
  2. Different shapes (acute vs. right vs. obtuse triangle)
  3. Different styling (if applicable)

If using matplotlib, each call must use different parameters and data — never copy the same generation code.

Subject-Specific Figure Requirements

Subject Common Types Special Requirements
Math Geometry, functions, coordinates No distortion, clear labels
Physics Circuits, mechanics, apparatus Standard symbols, correct arrows
Chemistry Apparatus, molecular structures Reagent names labeled
Biology Cell, organ, ecosystem diagrams Labels not too small
Geography Maps, contour lines, statistics Legend + scale + north arrow

13. Formulas & Special Symbols

Formulas

Math/physics/chemistry formulas use LaTeX → docx-js Math mapping (see references/math-formulas.md):

  • Basic (fractions, sub/superscript, roots) → docx-js Math components
  • Complex (3+ nesting, matrices) → matplotlib PNG fallback
  • Never hand-type Unicode formula approximations

Common Unicode Math Symbols

× ÷ ± ∓ ≠ ≈ ≤ ≥ ∞ √ ∑ ∏ ∫ ∂ ∆ ∇
α β γ δ ε θ λ μ π σ φ ω
⊂ ⊃ ∈ ∉  ∩ ∅ ∀ ∃
→ ← ↑ ↓ ⇒ ⇔  °  ″ ‰  ² ³ ⁴ ⁿ ₁ ₂ ₃

Chemical Formulas

Subscripts/superscripts must be correct: H₂O, CO₂, Fe₂O₃, Ca(OH)₂ Reaction arrows: → ⇌ ↑ ↓


14. Table Usage Standards

Borderless Tables (for alignment)

For: option alignment, info rows, question number + points alignment

const NB = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
const NBs = { top: NB, bottom: NB, left: NB, right: NB };

Bordered Tables (for data display)

For: score tables, data tables, statistics

const thinB = (c="000000") => ({ style: BorderStyle.SINGLE, size: 1, color: c });
const thinBs = (c="000000") => ({ top: thinB(c), bottom: thinB(c), left: thinB(c), right: thinB(c) });

Table Standards

  • Cell padding moderate (margins: top/bottom 4060, left/right 6080)
  • Consistent border thickness
  • Header row: light grey F0F0F0 background
  • Avoid cross-page tables
  • Tables centered (alignment: AlignmentType.CENTER)

15. Headers & Footers

Page Numbers

footers: { default: new Footer({ children: [
  new Paragraph({ alignment: AlignmentType.CENTER,
    children: [
      new TextRun({ children: [PageNumber.CURRENT], size: 18, font: "SimSun" }),
    ] })
] }) }

⚠️ Denominator FORBIDDEN — never use PageNumber.TOTAL_PAGES or "Page X of Y". Show only current page number.

Headers

  • May contain seal line prompt or subject name
  • Small font (89pt), grey color (999999)
  • Should not be visually heavy — must not compete with content

16. Subject-Specific Standards

Chinese Language

  • Reading, classical poetry, composition: no columns
  • Poetry preserves original line breaks
  • Classical text needs annotation area (smaller font, indented)
  • Composition grid in independent section, grid count via calcGridSize (800 words → 50×20 = 1000 cells)
  • Dictation questions: horizontal lines, moderate length
  • Reading materials: use KaiTi to differentiate

Mathematics

  • Multiple choice, fill-in: suitable for neat layout
  • Formulas: Unicode symbols or OOXML
  • Geometry/function graphs must be clear, undistorted
  • Problem-solving: sufficient working space
  • Coordinate graphs: labeled axes, tick marks

English

  • English font: Times New Roman, moderate character spacing
  • Cloze: numbers in text, options after passage
  • Reading comprehension: material + questions as groups
  • Writing area: horizontal lines, not grid
  • Listening (if any): numbers aligned with options

Physics / Chemistry / Biology

  • Experiment/apparatus diagrams must be clear and accurate
  • Unit symbols standardized (m/s, kg, mol/L, etc.)
  • Chemical formula subscripts correct
  • Calculation and experiment analysis: sufficient answer space
  • Biology structure diagrams: labels not too small

History / Politics

  • Source-based questions are lengthy — no columns
  • Dates, figures, events clearly labeled
  • Essay questions: more whitespace than multiple choice
  • Historical sources cite provenance
  • Chart materials in logical order

Geography

  • Maps are the focus — must be clear
  • Legend, scale bar, north arrow required
  • Map and question close together — avoid page turns
  • Map reading questions: balance figure and text space
  • Contour line values clearly labeled

Final Review Checklist

After generating an exam paper, check every item:

  • Question numbers sequential, points correct, total correct
  • Question stems match options / materials / illustrations one-to-one
  • Figures come after stem, before answer area (strict order)
  • Figure content matches question semantics (labels, symbols match)
  • Composition grid count ≥ required words × 1.25 (800 words → at least 1000 cells)
  • Options aligned with borderless tables (not spaces)
  • No wrong pages, missing pages, no extra blank pages
  • Images / tables / formulas positioned correctly
  • No Markdown table syntax in document — all data tables use proper docx Table objects
  • Fonts, sizes, line spacing consistent
  • Answer space matches difficulty and point value
  • Clear when printed in B&W
  • Subject-specific layout handled properly
  • Seal line / page numbers / headers formatted correctly
  • Header info complete (school, subject, duration, total score)
  • No extra PageBreak at end of last section
  • Answer key is either a separate file (default) or on a separate page (if user requested in same file) — never on the same page as questions