Skip to main content
Glama

Hacker News MCP Server

by az9713
docx-js.md16.5 kB
# DOCX Library Tutorial Generate .docx files with JavaScript/TypeScript. **Important: Read this entire document before starting.** Critical formatting rules and common pitfalls are covered throughout - skipping sections may result in corrupted files or rendering issues. ## Setup Assumes docx is already installed globally If not installed: `npm install -g docx` ```javascript const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell, ImageRun, Media, Header, Footer, AlignmentType, PageOrientation, LevelFormat, ExternalHyperlink, InternalHyperlink, TableOfContents, HeadingLevel, BorderStyle, WidthType, TabStopType, TabStopPosition, UnderlineType, ShadingType, VerticalAlign, SymbolRun, PageNumber, FootnoteReferenceRun, Footnote, PageBreak } = require('docx'); // Create & Save const doc = new Document({ sections: [{ children: [/* content */] }] }); Packer.toBuffer(doc).then(buffer => fs.writeFileSync("doc.docx", buffer)); // Node.js Packer.toBlob(doc).then(blob => { /* download logic */ }); // Browser ``` ## Text & Formatting ```javascript // IMPORTANT: Never use \n for line breaks - always use separate Paragraph elements // ❌ WRONG: new TextRun("Line 1\nLine 2") // ✅ CORRECT: new Paragraph({ children: [new TextRun("Line 1")] }), new Paragraph({ children: [new TextRun("Line 2")] }) // Basic text with all formatting options new Paragraph({ alignment: AlignmentType.CENTER, spacing: { before: 200, after: 200 }, indent: { left: 720, right: 720 }, children: [ new TextRun({ text: "Bold", bold: true }), new TextRun({ text: "Italic", italics: true }), new TextRun({ text: "Underlined", underline: { type: UnderlineType.DOUBLE, color: "FF0000" } }), new TextRun({ text: "Colored", color: "FF0000", size: 28, font: "Arial" }), // Arial default new TextRun({ text: "Highlighted", highlight: "yellow" }), new TextRun({ text: "Strikethrough", strike: true }), new TextRun({ text: "x2", superScript: true }), new TextRun({ text: "H2O", subScript: true }), new TextRun({ text: "SMALL CAPS", smallCaps: true }), new SymbolRun({ char: "2022", font: "Symbol" }), // Bullet • new SymbolRun({ char: "00A9", font: "Arial" }) // Copyright © - Arial for symbols ] }) ``` ## Styles & Professional Formatting ```javascript const doc = new Document({ styles: { default: { document: { run: { font: "Arial", size: 24 } } }, // 12pt default paragraphStyles: [ // Document title style - override built-in Title style { id: "Title", name: "Title", basedOn: "Normal", run: { size: 56, bold: true, color: "000000", font: "Arial" }, paragraph: { spacing: { before: 240, after: 120 }, alignment: AlignmentType.CENTER } }, // IMPORTANT: Override built-in heading styles by using their exact IDs { id: "Heading1", name: "Heading 1", basedOn: "Normal", next: "Normal", quickFormat: true, run: { size: 32, bold: true, color: "000000", font: "Arial" }, // 16pt paragraph: { spacing: { before: 240, after: 240 }, outlineLevel: 0 } }, // Required for TOC { id: "Heading2", name: "Heading 2", basedOn: "Normal", next: "Normal", quickFormat: true, run: { size: 28, bold: true, color: "000000", font: "Arial" }, // 14pt paragraph: { spacing: { before: 180, after: 180 }, outlineLevel: 1 } }, // Custom styles use your own IDs { id: "myStyle", name: "My Style", basedOn: "Normal", run: { size: 28, bold: true, color: "000000" }, paragraph: { spacing: { after: 120 }, alignment: AlignmentType.CENTER } } ], characterStyles: [{ id: "myCharStyle", name: "My Char Style", run: { color: "FF0000", bold: true, underline: { type: UnderlineType.SINGLE } } }] }, sections: [{ properties: { page: { margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } } }, children: [ new Paragraph({ heading: HeadingLevel.TITLE, children: [new TextRun("Document Title")] }), // Uses overridden Title style new Paragraph({ heading: HeadingLevel.HEADING_1, children: [new TextRun("Heading 1")] }), // Uses overridden Heading1 style new Paragraph({ style: "myStyle", children: [new TextRun("Custom paragraph style")] }), new Paragraph({ children: [ new TextRun("Normal with "), new TextRun({ text: "custom char style", style: "myCharStyle" }) ]}) ] }] }); ``` **Professional Font Combinations:** - **Arial (Headers) + Arial (Body)** - Most universally supported, clean and professional - **Times New Roman (Headers) + Arial (Body)** - Classic serif headers with modern sans-serif body - **Georgia (Headers) + Verdana (Body)** - Optimized for screen reading, elegant contrast **Key Styling Principles:** - **Override built-in styles**: Use exact IDs like "Heading1", "Heading2", "Heading3" to override Word's built-in heading styles - **HeadingLevel constants**: `HeadingLevel.HEADING_1` uses "Heading1" style, `HeadingLevel.HEADING_2` uses "Heading2" style, etc. - **Include outlineLevel**: Set `outlineLevel: 0` for H1, `outlineLevel: 1` for H2, etc. to ensure TOC works correctly - **Use custom styles** instead of inline formatting for consistency - **Set a default font** using `styles.default.document.run.font` - Arial is universally supported - **Establish visual hierarchy** with different font sizes (titles > headers > body) - **Add proper spacing** with `before` and `after` paragraph spacing - **Use colors sparingly**: Default to black (000000) and shades of gray for titles and headings (heading 1, heading 2, etc.) - **Set consistent margins** (1440 = 1 inch is standard) ## Lists (ALWAYS USE PROPER LISTS - NEVER USE UNICODE BULLETS) ```javascript // Bullets - ALWAYS use the numbering config, NOT unicode symbols // CRITICAL: Use LevelFormat.BULLET constant, NOT the string "bullet" const doc = new Document({ numbering: { config: [ { reference: "bullet-list", levels: [{ level: 0, format: LevelFormat.BULLET, text: "•", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 720, hanging: 360 } } } }] }, { reference: "first-numbered-list", levels: [{ level: 0, format: LevelFormat.DECIMAL, text: "%1.", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 720, hanging: 360 } } } }] }, { reference: "second-numbered-list", // Different reference = restarts at 1 levels: [{ level: 0, format: LevelFormat.DECIMAL, text: "%1.", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 720, hanging: 360 } } } }] } ] }, sections: [{ children: [ // Bullet list items new Paragraph({ numbering: { reference: "bullet-list", level: 0 }, children: [new TextRun("First bullet point")] }), new Paragraph({ numbering: { reference: "bullet-list", level: 0 }, children: [new TextRun("Second bullet point")] }), // Numbered list items new Paragraph({ numbering: { reference: "first-numbered-list", level: 0 }, children: [new TextRun("First numbered item")] }), new Paragraph({ numbering: { reference: "first-numbered-list", level: 0 }, children: [new TextRun("Second numbered item")] }), // ⚠️ CRITICAL: Different reference = INDEPENDENT list that restarts at 1 // Same reference = CONTINUES previous numbering new Paragraph({ numbering: { reference: "second-numbered-list", level: 0 }, children: [new TextRun("Starts at 1 again (because different reference)")] }) ] }] }); // ⚠️ CRITICAL NUMBERING RULE: Each reference creates an INDEPENDENT numbered list // - Same reference = continues numbering (1, 2, 3... then 4, 5, 6...) // - Different reference = restarts at 1 (1, 2, 3... then 1, 2, 3...) // Use unique reference names for each separate numbered section! // ⚠️ CRITICAL: NEVER use unicode bullets - they create fake lists that don't work properly // new TextRun("• Item") // WRONG // new SymbolRun({ char: "2022" }) // WRONG // ✅ ALWAYS use numbering config with LevelFormat.BULLET for real Word lists ``` ## Tables ```javascript // Complete table with margins, borders, headers, and bullet points const tableBorder = { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" }; const cellBorders = { top: tableBorder, bottom: tableBorder, left: tableBorder, right: tableBorder }; new Table({ columnWidths: [4680, 4680], // ⚠️ CRITICAL: Set column widths at table level - values in DXA (twentieths of a point) margins: { top: 100, bottom: 100, left: 180, right: 180 }, // Set once for all cells rows: [ new TableRow({ tableHeader: true, children: [ new TableCell({ borders: cellBorders, width: { size: 4680, type: WidthType.DXA }, // ALSO set width on each cell // ⚠️ CRITICAL: Always use ShadingType.CLEAR to prevent black backgrounds in Word. shading: { fill: "D5E8F0", type: ShadingType.CLEAR }, verticalAlign: VerticalAlign.CENTER, children: [new Paragraph({ alignment: AlignmentType.CENTER, children: [new TextRun({ text: "Header", bold: true, size: 22 })] })] }), new TableCell({ borders: cellBorders, width: { size: 4680, type: WidthType.DXA }, // ALSO set width on each cell shading: { fill: "D5E8F0", type: ShadingType.CLEAR }, children: [new Paragraph({ alignment: AlignmentType.CENTER, children: [new TextRun({ text: "Bullet Points", bold: true, size: 22 })] })] }) ] }), new TableRow({ children: [ new TableCell({ borders: cellBorders, width: { size: 4680, type: WidthType.DXA }, // ALSO set width on each cell children: [new Paragraph({ children: [new TextRun("Regular data")] })] }), new TableCell({ borders: cellBorders, width: { size: 4680, type: WidthType.DXA }, // ALSO set width on each cell children: [ new Paragraph({ numbering: { reference: "bullet-list", level: 0 }, children: [new TextRun("First bullet point")] }), new Paragraph({ numbering: { reference: "bullet-list", level: 0 }, children: [new TextRun("Second bullet point")] }) ] }) ] }) ] }) ``` **IMPORTANT: Table Width & Borders** - Use BOTH `columnWidths: [width1, width2, ...]` array AND `width: { size: X, type: WidthType.DXA }` on each cell - Values in DXA (twentieths of a point): 1440 = 1 inch, Letter usable width = 9360 DXA (with 1" margins) - Apply borders to individual `TableCell` elements, NOT the `Table` itself **Precomputed Column Widths (Letter size with 1" margins = 9360 DXA total):** - **2 columns:** `columnWidths: [4680, 4680]` (equal width) - **3 columns:** `columnWidths: [3120, 3120, 3120]` (equal width) ## Links & Navigation ```javascript // TOC (requires headings) - CRITICAL: Use HeadingLevel only, NOT custom styles // ❌ WRONG: new Paragraph({ heading: HeadingLevel.HEADING_1, style: "customHeader", children: [new TextRun("Title")] }) // ✅ CORRECT: new Paragraph({ heading: HeadingLevel.HEADING_1, children: [new TextRun("Title")] }) new TableOfContents("Table of Contents", { hyperlink: true, headingStyleRange: "1-3" }), // External link new Paragraph({ children: [new ExternalHyperlink({ children: [new TextRun({ text: "Google", style: "Hyperlink" })], link: "https://www.google.com" })] }), // Internal link & bookmark new Paragraph({ children: [new InternalHyperlink({ children: [new TextRun({ text: "Go to Section", style: "Hyperlink" })], anchor: "section1" })] }), new Paragraph({ children: [new TextRun("Section Content")], bookmark: { id: "section1", name: "section1" } }), ``` ## Images & Media ```javascript // Basic image with sizing & positioning // CRITICAL: Always specify 'type' parameter - it's REQUIRED for ImageRun new Paragraph({ alignment: AlignmentType.CENTER, children: [new ImageRun({ type: "png", // NEW REQUIREMENT: Must specify image type (png, jpg, jpeg, gif, bmp, svg) data: fs.readFileSync("image.png"), transformation: { width: 200, height: 150, rotation: 0 }, // rotation in degrees altText: { title: "Logo", description: "Company logo", name: "Name" } // IMPORTANT: All three fields are required })] }) ``` ## Page Breaks ```javascript // Manual page break new Paragraph({ children: [new PageBreak()] }), // Page break before paragraph new Paragraph({ pageBreakBefore: true, children: [new TextRun("This starts on a new page")] }) // ⚠️ CRITICAL: NEVER use PageBreak standalone - it will create invalid XML that Word cannot open // ❌ WRONG: new PageBreak() // ✅ CORRECT: new Paragraph({ children: [new PageBreak()] }) ``` ## Headers/Footers & Page Setup ```javascript const doc = new Document({ sections: [{ properties: { page: { margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 }, // 1440 = 1 inch size: { orientation: PageOrientation.LANDSCAPE }, pageNumbers: { start: 1, formatType: "decimal" } // "upperRoman", "lowerRoman", "upperLetter", "lowerLetter" } }, headers: { default: new Header({ children: [new Paragraph({ alignment: AlignmentType.RIGHT, children: [new TextRun("Header Text")] })] }) }, footers: { default: new Footer({ children: [new Paragraph({ alignment: AlignmentType.CENTER, children: [new TextRun("Page "), new TextRun({ children: [PageNumber.CURRENT] }), new TextRun(" of "), new TextRun({ children: [PageNumber.TOTAL_PAGES] })] })] }) }, children: [/* content */] }] }); ``` ## Tabs ```javascript new Paragraph({ tabStops: [ { type: TabStopType.LEFT, position: TabStopPosition.MAX / 4 }, { type: TabStopType.CENTER, position: TabStopPosition.MAX / 2 }, { type: TabStopType.RIGHT, position: TabStopPosition.MAX * 3 / 4 } ], children: [new TextRun("Left\tCenter\tRight")] }) ``` ## Constants & Quick Reference - **Underlines:** `SINGLE`, `DOUBLE`, `WAVY`, `DASH` - **Borders:** `SINGLE`, `DOUBLE`, `DASHED`, `DOTTED` - **Numbering:** `DECIMAL` (1,2,3), `UPPER_ROMAN` (I,II,III), `LOWER_LETTER` (a,b,c) - **Tabs:** `LEFT`, `CENTER`, `RIGHT`, `DECIMAL` - **Symbols:** `"2022"` (•), `"00A9"` (©), `"00AE"` (®), `"2122"` (™), `"00B0"` (°), `"F070"` (✓), `"F0FC"` (✗) ## Critical Issues & Common Mistakes - **CRITICAL: PageBreak must ALWAYS be inside a Paragraph** - standalone PageBreak creates invalid XML that Word cannot open - **ALWAYS use ShadingType.CLEAR for table cell shading** - Never use ShadingType.SOLID (causes black background). - Measurements in DXA (1440 = 1 inch) | Each table cell needs ≥1 Paragraph | TOC requires HeadingLevel styles only - **ALWAYS use custom styles** with Arial font for professional appearance and proper visual hierarchy - **ALWAYS set a default font** using `styles.default.document.run.font` - Arial recommended - **ALWAYS use columnWidths array for tables** + individual cell widths for compatibility - **NEVER use unicode symbols for bullets** - always use proper numbering configuration with `LevelFormat.BULLET` constant (NOT the string "bullet") - **NEVER use \n for line breaks anywhere** - always use separate Paragraph elements for each line - **ALWAYS use TextRun objects within Paragraph children** - never use text property directly on Paragraph - **CRITICAL for images**: ImageRun REQUIRES `type` parameter - always specify "png", "jpg", "jpeg", "gif", "bmp", or "svg" - **CRITICAL for bullets**: Must use `LevelFormat.BULLET` constant, not string "bullet", and include `text: "•"` for the bullet character - **CRITICAL for numbering**: Each numbering reference creates an INDEPENDENT list. Same reference = continues numbering (1,2,3 then 4,5,6). Different reference = restarts at 1 (1,2,3 then 1,2,3). Use unique reference names for each separate numbered section! - **CRITICAL for TOC**: When using TableOfContents, headings must use HeadingLevel ONLY - do NOT add custom styles to heading paragraphs or TOC will break - **Tables**: Set `columnWidths` array + individual cell widths, apply borders to cells not table - **Set table margins at TABLE level** for consistent cell padding (avoids repetition per cell)

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/az9713/claude_skill_hn_mcp_server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server