office

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Office Document Generation

Office文档生成

Status: Production Ready Last Updated: 2026-01-12 Dependencies: None (pure JavaScript libraries) Latest Versions: docx@9.5.0, xlsx@0.18.5, pdf-lib@1.17.1, pptxgenjs@4.0.1

状态:已就绪可用于生产环境 最后更新:2026-01-12 依赖项:无(纯JavaScript库) 最新版本:docx@9.5.0、xlsx@0.18.5、pdf-lib@1.17.1、pptxgenjs@4.0.1

Overview

概述

Generate Microsoft Office documents and PDFs programmatically with TypeScript. All libraries are pure JavaScript with zero native dependencies, enabling universal runtime support:
FormatLibraryWorkersBrowserNode.js
DOCX
docx
XLSX
xlsx
(SheetJS)
PDF
pdf-lib
PPTX
pptxgenjs
⚠️*
*PPTX in Workers: Works for local images/data. Remote image fetching needs workaround (uses
https
module).

使用TypeScript以编程方式生成Microsoft Office文档和PDF。所有库均为纯JavaScript实现,无原生依赖,支持全场景运行环境:
格式Workers浏览器Node.js
DOCX
docx
XLSX
xlsx
(SheetJS)
PDF
pdf-lib
PPTX
pptxgenjs
⚠️*
*PPTX在Workers中:支持本地图片/数据。远程图片获取需要替代方案(原实现依赖
https
模块)。

Quick Start

快速开始

Installation

安装

bash
undefined
bash
undefined

Install all four (or pick what you need)

安装全部四个库(或按需选择)

npm install docx xlsx pdf-lib pptxgenjs
undefined
npm install docx xlsx pdf-lib pptxgenjs
undefined

Create a Word Document (30 seconds)

创建Word文档(30秒完成)

typescript
import { Document, Packer, Paragraph, TextRun } from 'docx';
import { writeFileSync } from 'fs';

const doc = new Document({
  sections: [{
    children: [
      new Paragraph({
        children: [
          new TextRun({ text: 'Hello World', bold: true, size: 48 }),
        ],
      }),
    ],
  }],
});

// Node.js: Save to file
const buffer = await Packer.toBuffer(doc);
writeFileSync('hello.docx', buffer);

// Browser/Workers: Get as blob
const blob = await Packer.toBlob(doc);
typescript
import { Document, Packer, Paragraph, TextRun } from 'docx';
import { writeFileSync } from 'fs';

const doc = new Document({
  sections: [{
    children: [
      new Paragraph({
        children: [
          new TextRun({ text: 'Hello World', bold: true, size: 48 }),
        ],
      }),
    ],
  }],
});

// Node.js:保存到文件
const buffer = await Packer.toBuffer(doc);
writeFileSync('hello.docx', buffer);

// 浏览器/Workers:获取Blob对象
const blob = await Packer.toBlob(doc);

Create an Excel Spreadsheet (30 seconds)

创建Excel电子表格(30秒完成)

typescript
import * as XLSX from 'xlsx';

// Create workbook with data
const data = [
  ['Name', 'Amount', 'Date'],
  ['Invoice #1', 1500, '2026-01-12'],
  ['Invoice #2', 2300, '2026-01-13'],
];

const worksheet = XLSX.utils.aoa_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, 'Invoices');

// Node.js: Save to file
XLSX.writeFile(workbook, 'invoices.xlsx');

// Browser/Workers: Get as buffer
const buffer = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' });
typescript
import * as XLSX from 'xlsx';

// 创建包含数据的工作簿
const data = [
  ['Name', 'Amount', 'Date'],
  ['Invoice #1', 1500, '2026-01-12'],
  ['Invoice #2', 2300, '2026-01-13'],
];

const worksheet = XLSX.utils.aoa_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, 'Invoices');

// Node.js:保存到文件
XLSX.writeFile(workbook, 'invoices.xlsx');

// 浏览器/Workers:获取Buffer
const buffer = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' });

Create a PDF (30 seconds)

创建PDF文档(30秒完成)

typescript
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';

const pdfDoc = await PDFDocument.create();
const page = pdfDoc.addPage([612, 792]); // Letter size
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);

page.drawText('Hello World', {
  x: 50,
  y: 700,
  size: 24,
  font,
  color: rgb(0, 0, 0),
});

// Get as bytes (works everywhere)
const pdfBytes = await pdfDoc.save();

// Node.js: Save to file
writeFileSync('hello.pdf', pdfBytes);
typescript
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';

const pdfDoc = await PDFDocument.create();
const page = pdfDoc.addPage([612, 792]); // 信纸尺寸
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);

page.drawText('Hello World', {
  x: 50,
  y: 700,
  size: 24,
  font,
  color: rgb(0, 0, 0),
});

// 获取字节数据(全环境通用)
const pdfBytes = await pdfDoc.save();

// Node.js:保存到文件
writeFileSync('hello.pdf', pdfBytes);

Create a PowerPoint (30 seconds)

创建PowerPoint演示文稿(30秒完成)

typescript
import pptxgen from 'pptxgenjs';

const pptx = new pptxgen();
pptx.author = 'Your Name';
pptx.title = 'Sample Presentation';

// Add a slide
const slide = pptx.addSlide();
slide.addText('Hello World', {
  x: 1, y: 1, w: 8, h: 1.5,
  fontSize: 36, bold: true, color: '363636',
});

// Node.js: Save to file
await pptx.writeFile({ fileName: 'hello.pptx' });

// Browser: Trigger download
await pptx.writeFile({ fileName: 'hello.pptx' });

typescript
import pptxgen from 'pptxgenjs';

const pptx = new pptxgen();
pptx.author = 'Your Name';
pptx.title = 'Sample Presentation';

// 添加幻灯片
const slide = pptx.addSlide();
slide.addText('Hello World', {
  x: 1, y: 1, w: 8, h: 1.5,
  fontSize: 36, bold: true, color: '363636',
});

// Node.js:保存到文件
await pptx.writeFile({ fileName: 'hello.pptx' });

// 浏览器:触发下载
await pptx.writeFile({ fileName: 'hello.pptx' });

DOCX: Word Documents

DOCX:Word文档

Key Concepts

核心概念

The
docx
package uses a builder pattern:
  • Document - The root container
  • Section - Contains pages (most docs have 1 section)
  • Paragraph - A line/block of text
  • TextRun - Formatted text within a paragraph
  • Table - Grid of TableRow → TableCell → Paragraph
docx
包使用构建者模式:
  • Document - 根容器
  • Section - 包含页面(大多数文档只有1个节)
  • Paragraph - 一行/一段文本
  • TextRun - 段落内的格式化文本
  • Table - 表格,由TableRow → TableCell → Paragraph组成

Document with Headings and Paragraphs

包含标题和段落的文档

typescript
import { Document, Packer, Paragraph, TextRun, HeadingLevel } from 'docx';

const doc = new Document({
  sections: [{
    children: [
      new Paragraph({
        text: 'Monthly Report',
        heading: HeadingLevel.HEADING_1,
      }),
      new Paragraph({
        children: [
          new TextRun('This is a '),
          new TextRun({ text: 'bold', bold: true }),
          new TextRun(' and '),
          new TextRun({ text: 'italic', italics: true }),
          new TextRun(' text example.'),
        ],
      }),
    ],
  }],
});
typescript
import { Document, Packer, Paragraph, TextRun, HeadingLevel } from 'docx';

const doc = new Document({
  sections: [{
    children: [
      new Paragraph({
        text: 'Monthly Report',
        heading: HeadingLevel.HEADING_1,
      }),
      new Paragraph({
        children: [
          new TextRun('This is a '),
          new TextRun({ text: 'bold', bold: true }),
          new TextRun(' and '),
          new TextRun({ text: 'italic', italics: true }),
          new TextRun(' text example.'),
        ],
      }),
    ],
  }],
});

Tables

表格

typescript
import { Document, Table, TableRow, TableCell, Paragraph, WidthType } from 'docx';

const table = new Table({
  width: { size: 100, type: WidthType.PERCENTAGE },
  rows: [
    new TableRow({
      children: [
        new TableCell({ children: [new Paragraph('Header 1')] }),
        new TableCell({ children: [new Paragraph('Header 2')] }),
      ],
    }),
    new TableRow({
      children: [
        new TableCell({ children: [new Paragraph('Data 1')] }),
        new TableCell({ children: [new Paragraph('Data 2')] }),
      ],
    }),
  ],
});

const doc = new Document({
  sections: [{ children: [table] }],
});
typescript
import { Document, Table, TableRow, TableCell, Paragraph, WidthType } from 'docx';

const table = new Table({
  width: { size: 100, type: WidthType.PERCENTAGE },
  rows: [
    new TableRow({
      children: [
        new TableCell({ children: [new Paragraph('Header 1')] }),
        new TableCell({ children: [new Paragraph('Header 2')] }),
      ],
    }),
    new TableRow({
      children: [
        new TableCell({ children: [new Paragraph('Data 1')] }),
        new TableCell({ children: [new Paragraph('Data 2')] }),
      ],
    }),
  ],
});

const doc = new Document({
  sections: [{ children: [table] }],
});

Images

图片

typescript
import { Document, Paragraph, ImageRun } from 'docx';
import { readFileSync } from 'fs';

const imageBuffer = readFileSync('logo.png');

const doc = new Document({
  sections: [{
    children: [
      new Paragraph({
        children: [
          new ImageRun({
            data: imageBuffer,
            transformation: { width: 200, height: 100 },
            type: 'png',
          }),
        ],
      }),
    ],
  }],
});
typescript
import { Document, Paragraph, ImageRun } from 'docx';
import { readFileSync } from 'fs';

const imageBuffer = readFileSync('logo.png');

const doc = new Document({
  sections: [{
    children: [
      new Paragraph({
        children: [
          new ImageRun({
            data: imageBuffer,
            transformation: { width: 200, height: 100 },
            type: 'png',
          }),
        ],
      }),
    ],
  }],
});

Export Patterns

导出方式

typescript
// Node.js - Save to file
import { writeFileSync } from 'fs';
const buffer = await Packer.toBuffer(doc);
writeFileSync('document.docx', buffer);

// Browser - Download
const blob = await Packer.toBlob(doc);
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'document.docx';
a.click();

// Cloudflare Workers - Return as Response
const buffer = await Packer.toBuffer(doc);
return new Response(buffer, {
  headers: {
    'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    'Content-Disposition': 'attachment; filename="document.docx"',
  },
});

typescript
// Node.js - 保存到文件
import { writeFileSync } from 'fs';
const buffer = await Packer.toBuffer(doc);
writeFileSync('document.docx', buffer);

// 浏览器 - 触发下载
const blob = await Packer.toBlob(doc);
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'document.docx';
a.click();

// Cloudflare Workers - 作为Response返回
const buffer = await Packer.toBuffer(doc);
return new Response(buffer, {
  headers: {
    'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    'Content-Disposition': 'attachment; filename="document.docx"',
  },
});

XLSX: Excel Spreadsheets

XLSX:Excel电子表格

Key Concepts

核心概念

SheetJS (xlsx) uses utility functions:
  • Workbook - Container for sheets
  • Worksheet - Single sheet (grid of cells)
  • utils.aoa_to_sheet - Array of arrays → worksheet
  • utils.json_to_sheet - JSON array → worksheet
  • utils.book_new/book_append_sheet - Create/add sheets
SheetJS(xlsx)使用工具函数模式:
  • Workbook - 工作表容器
  • Worksheet - 单个工作表(单元格网格)
  • utils.aoa_to_sheet - 二维数组 → 工作表
  • utils.json_to_sheet - JSON数组 → 工作表
  • utils.book_new/book_append_sheet - 创建/添加工作表

From Array of Arrays

从二维数组创建

typescript
import * as XLSX from 'xlsx';

const data = [
  ['Product', 'Price', 'Quantity', 'Total'],
  ['Widget A', 10, 5, { f: 'B2*C2' }],  // Formula
  ['Widget B', 15, 3, { f: 'B3*C3' }],
  ['', '', 'Grand Total:', { f: 'SUM(D2:D3)' }],
];

const ws = XLSX.utils.aoa_to_sheet(data);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sales');
typescript
import * as XLSX from 'xlsx';

const data = [
  ['Product', 'Price', 'Quantity', 'Total'],
  ['Widget A', 10, 5, { f: 'B2*C2' }],  // 公式
  ['Widget B', 15, 3, { f: 'B3*C3' }],
  ['', '', 'Grand Total:', { f: 'SUM(D2:D3)' }],
];

const ws = XLSX.utils.aoa_to_sheet(data);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sales');

From JSON

从JSON数组创建

typescript
const invoices = [
  { id: 1, customer: 'Acme Corp', amount: 1500, date: '2026-01-12' },
  { id: 2, customer: 'Globex', amount: 2300, date: '2026-01-13' },
];

const ws = XLSX.utils.json_to_sheet(invoices);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Invoices');
typescript
const invoices = [
  { id: 1, customer: 'Acme Corp', amount: 1500, date: '2026-01-12' },
  { id: 2, customer: 'Globex', amount: 2300, date: '2026-01-13' },
];

const ws = XLSX.utils.json_to_sheet(invoices);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Invoices');

Column Widths

列宽设置

typescript
// Set column widths (in characters)
ws['!cols'] = [
  { wch: 10 },  // Column A
  { wch: 20 },  // Column B
  { wch: 15 },  // Column C
];
typescript
// 设置列宽(单位:字符)
ws['!cols'] = [
  { wch: 10 },  // 列A
  { wch: 20 },  // 列B
  { wch: 15 },  // 列C
];

Multiple Sheets

多工作表

typescript
const wb = XLSX.utils.book_new();

const summaryData = [['Total Sales', 3800]];
const detailData = [['Item', 'Amount'], ['Item 1', 1500], ['Item 2', 2300]];

XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(summaryData), 'Summary');
XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(detailData), 'Details');
typescript
const wb = XLSX.utils.book_new();

const summaryData = [['Total Sales', 3800]];
const detailData = [['Item', 'Amount'], ['Item 1', 1500], ['Item 2', 2300]];

XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(summaryData), 'Summary');
XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(detailData), 'Details');

Export Patterns

导出方式

typescript
// Node.js - Save to file
XLSX.writeFile(workbook, 'report.xlsx');

// Browser/Workers - Get buffer
const buffer = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' });

// Cloudflare Workers - Return as Response
return new Response(buffer, {
  headers: {
    'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    'Content-Disposition': 'attachment; filename="report.xlsx"',
  },
});

typescript
// Node.js - 保存到文件
XLSX.writeFile(workbook, 'report.xlsx');

// 浏览器/Workers - 获取Buffer
const buffer = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' });

// Cloudflare Workers - 作为Response返回
return new Response(buffer, {
  headers: {
    'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    'Content-Disposition': 'attachment; filename="report.xlsx"',
  },
});

PDF: PDF Documents

PDF:PDF文档

Key Concepts

核心概念

pdf-lib uses a page-based approach:
  • PDFDocument - The PDF file
  • PDFPage - Individual page
  • Coordinates - Origin at BOTTOM-LEFT (not top-left!)
  • Units - Points (72 points = 1 inch)
pdf-lib使用基于页面的设计:
  • PDFDocument - PDF文件实例
  • PDFPage - 单个页面
  • 坐标系统 - 原点位于左下角(而非左上角!)
  • 单位 - 点(72点 = 1英寸)

Standard Page Sizes

标准页面尺寸

typescript
// Common sizes in points [width, height]
const LETTER = [612, 792];    // 8.5" x 11"
const A4 = [595.28, 841.89];  // 210mm x 297mm
const LEGAL = [612, 1008];    // 8.5" x 14"
typescript
// 常见尺寸(单位:点)[宽度, 高度]
const LETTER = [612, 792];    // 8.5" x 11"
const A4 = [595.28, 841.89];  // 210mm x 297mm
const LEGAL = [612, 1008];    // 8.5" x 14"

Text and Fonts

文本与字体

typescript
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';

const pdfDoc = await PDFDocument.create();
const page = pdfDoc.addPage([612, 792]);

// Embed standard fonts
const helvetica = await pdfDoc.embedFont(StandardFonts.Helvetica);
const helveticaBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold);

// Draw text (y=0 is BOTTOM of page)
page.drawText('Invoice #12345', {
  x: 50,
  y: 750,  // Near top of page
  size: 24,
  font: helveticaBold,
  color: rgb(0, 0, 0),
});

page.drawText('Thank you for your business!', {
  x: 50,
  y: 50,  // Near bottom of page
  size: 12,
  font: helvetica,
  color: rgb(0.5, 0.5, 0.5),
});
typescript
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';

const pdfDoc = await PDFDocument.create();
const page = pdfDoc.addPage([612, 792]);

// 嵌入标准字体
const helvetica = await pdfDoc.embedFont(StandardFonts.Helvetica);
const helveticaBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold);

// 绘制文本(y=0为页面底部)
page.drawText('Invoice #12345', {
  x: 50,
  y: 750,  // 靠近页面顶部
  size: 24,
  font: helveticaBold,
  color: rgb(0, 0, 0),
});

page.drawText('Thank you for your business!', {
  x: 50,
  y: 50,  // 靠近页面底部
  size: 12,
  font: helvetica,
  color: rgb(0.5, 0.5, 0.5),
});

Drawing Shapes

绘制图形

typescript
// Rectangle
page.drawRectangle({
  x: 50,
  y: 600,
  width: 200,
  height: 100,
  borderColor: rgb(0, 0, 0),
  borderWidth: 1,
  color: rgb(0.95, 0.95, 0.95),  // Fill color
});

// Line
page.drawLine({
  start: { x: 50, y: 500 },
  end: { x: 550, y: 500 },
  thickness: 1,
  color: rgb(0, 0, 0),
});
typescript
// 矩形
page.drawRectangle({
  x: 50,
  y: 600,
  width: 200,
  height: 100,
  borderColor: rgb(0, 0, 0),
  borderWidth: 1,
  color: rgb(0.95, 0.95, 0.95),  // 填充色
});

// 直线
page.drawLine({
  start: { x: 50, y: 500 },
  end: { x: 550, y: 500 },
  thickness: 1,
  color: rgb(0, 0, 0),
});

Images

图片

typescript
// From URL/fetch
const imageBytes = await fetch('https://example.com/logo.png').then(r => r.arrayBuffer());
const image = await pdfDoc.embedPng(imageBytes);

// Or embedJpg for JPEG
// const image = await pdfDoc.embedJpg(imageBytes);

page.drawImage(image, {
  x: 50,
  y: 700,
  width: 100,
  height: 50,
});
typescript
// 从URL/fetch获取
const imageBytes = await fetch('https://example.com/logo.png').then(r => r.arrayBuffer());
const image = await pdfDoc.embedPng(imageBytes);

// 嵌入JPG图片使用embedJpg
// const image = await pdfDoc.embedJpg(imageBytes);

page.drawImage(image, {
  x: 50,
  y: 700,
  width: 100,
  height: 50,
});

Fill PDF Forms

填充PDF表单

typescript
import { PDFDocument } from 'pdf-lib';

// Load existing PDF with form
const existingPdfBytes = await fetch('form.pdf').then(r => r.arrayBuffer());
const pdfDoc = await PDFDocument.load(existingPdfBytes);

// Get form and fields
const form = pdfDoc.getForm();
const nameField = form.getTextField('customer_name');
const dateField = form.getTextField('date');

// Fill fields
nameField.setText('John Doe');
dateField.setText('2026-01-12');

// Flatten form (make fields non-editable)
form.flatten();

const pdfBytes = await pdfDoc.save();
typescript
import { PDFDocument } from 'pdf-lib';

// 加载包含表单的现有PDF
const existingPdfBytes = await fetch('form.pdf').then(r => r.arrayBuffer());
const pdfDoc = await PDFDocument.load(existingPdfBytes);

// 获取表单和字段
const form = pdfDoc.getForm();
const nameField = form.getTextField('customer_name');
const dateField = form.getTextField('date');

// 填充字段
nameField.setText('John Doe');
dateField.setText('2026-01-12');

// 扁平化表单(使字段不可编辑)
form.flatten();

const pdfBytes = await pdfDoc.save();

Merge PDFs

合并PDF

typescript
import { PDFDocument } from 'pdf-lib';

const pdf1Bytes = await fetch('doc1.pdf').then(r => r.arrayBuffer());
const pdf2Bytes = await fetch('doc2.pdf').then(r => r.arrayBuffer());

const mergedPdf = await PDFDocument.create();
const pdf1 = await PDFDocument.load(pdf1Bytes);
const pdf2 = await PDFDocument.load(pdf2Bytes);

// Copy all pages from both documents
const pages1 = await mergedPdf.copyPages(pdf1, pdf1.getPageIndices());
const pages2 = await mergedPdf.copyPages(pdf2, pdf2.getPageIndices());

pages1.forEach(page => mergedPdf.addPage(page));
pages2.forEach(page => mergedPdf.addPage(page));

const mergedBytes = await mergedPdf.save();

typescript
import { PDFDocument } from 'pdf-lib';

const pdf1Bytes = await fetch('doc1.pdf').then(r => r.arrayBuffer());
const pdf2Bytes = await fetch('doc2.pdf').then(r => r.arrayBuffer());

const mergedPdf = await PDFDocument.create();
const pdf1 = await PDFDocument.load(pdf1Bytes);
const pdf2 = await PDFDocument.load(pdf2Bytes);

// 复制两个文档的所有页面
const pages1 = await mergedPdf.copyPages(pdf1, pdf1.getPageIndices());
const pages2 = await mergedPdf.copyPages(pdf2, pdf2.getPageIndices());

pages1.forEach(page => mergedPdf.addPage(page));
pages2.forEach(page => mergedPdf.addPage(page));

const mergedBytes = await mergedPdf.save();

PPTX: PowerPoint Presentations

PPTX:PowerPoint演示文稿

Key Concepts

核心概念

pptxgenjs uses a slide-based approach:
  • Presentation - The PPTX file (new pptxgen())
  • Slide - Individual slide (addSlide())
  • Elements - Text, images, shapes, tables, charts
  • Units - Inches by default (can change to cm/points)
pptxgenjs使用基于幻灯片的设计:
  • Presentation - PPTX文件实例(new pptxgen())
  • Slide - 单个幻灯片(addSlide())
  • 元素 - 文本、图片、图形、表格、图表
  • 单位 - 默认英寸(可切换为厘米/点)

Basic Presentation

基础演示文稿

typescript
import pptxgen from 'pptxgenjs';

const pptx = new pptxgen();
pptx.author = 'Author Name';
pptx.title = 'Presentation Title';
pptx.subject = 'Subject';
pptx.company = 'Company';

// Title slide
const titleSlide = pptx.addSlide();
titleSlide.addText('Quarterly Report', {
  x: 0.5, y: 2, w: 9, h: 1,
  fontSize: 44, bold: true, color: '0066CC', align: 'center',
});
titleSlide.addText('Q1 2026', {
  x: 0.5, y: 3.5, w: 9, h: 0.5,
  fontSize: 24, color: '666666', align: 'center',
});
typescript
import pptxgen from 'pptxgenjs';

const pptx = new pptxgen();
pptx.author = 'Author Name';
pptx.title = 'Presentation Title';
pptx.subject = 'Subject';
pptx.company = 'Company';

// 标题幻灯片
const titleSlide = pptx.addSlide();
titleSlide.addText('Quarterly Report', {
  x: 0.5, y: 2, w: 9, h: 1,
  fontSize: 44, bold: true, color: '0066CC', align: 'center',
});
titleSlide.addText('Q1 2026', {
  x: 0.5, y: 3.5, w: 9, h: 0.5,
  fontSize: 24, color: '666666', align: 'center',
});

Slide with Bullet Points

包含项目符号的幻灯片

typescript
const contentSlide = pptx.addSlide();

// Title
contentSlide.addText('Key Highlights', {
  x: 0.5, y: 0.5, w: 9, h: 0.8,
  fontSize: 32, bold: true, color: '333333',
});

// Bullet points
contentSlide.addText([
  { text: 'Revenue up 25% YoY', options: { bullet: true, fontSize: 20 } },
  { text: 'Customer base grew to 10,000', options: { bullet: true, fontSize: 20 } },
  { text: 'New product launch successful', options: { bullet: true, fontSize: 20 } },
  { text: 'Expanded to 5 new markets', options: { bullet: true, fontSize: 20 } },
], { x: 0.5, y: 1.5, w: 8, h: 4, valign: 'top' });
typescript
const contentSlide = pptx.addSlide();

// 标题
contentSlide.addText('Key Highlights', {
  x: 0.5, y: 0.5, w: 9, h: 0.8,
  fontSize: 32, bold: true, color: '333333',
});

// 项目符号
contentSlide.addText([
  { text: 'Revenue up 25% YoY', options: { bullet: true, fontSize: 20 } },
  { text: 'Customer base grew to 10,000', options: { bullet: true, fontSize: 20 } },
  { text: 'New product launch successful', options: { bullet: true, fontSize: 20 } },
  { text: 'Expanded to 5 new markets', options: { bullet: true, fontSize: 20 } },
], { x: 0.5, y: 1.5, w: 8, h: 4, valign: 'top' });

Tables

表格

typescript
const tableSlide = pptx.addSlide();

tableSlide.addText('Sales Summary', {
  x: 0.5, y: 0.5, w: 9, h: 0.8,
  fontSize: 28, bold: true,
});

const tableData = [
  [{ text: 'Region', options: { bold: true, fill: '0066CC', color: 'FFFFFF' } },
   { text: 'Q1', options: { bold: true, fill: '0066CC', color: 'FFFFFF' } },
   { text: 'Q2', options: { bold: true, fill: '0066CC', color: 'FFFFFF' } }],
  ['North America', '$2.5M', '$2.8M'],
  ['Europe', '$1.8M', '$2.1M'],
  ['Asia Pacific', '$1.2M', '$1.5M'],
];

tableSlide.addTable(tableData, {
  x: 0.5, y: 1.5, w: 9,
  border: { pt: 1, color: 'CCCCCC' },
  fontFace: 'Arial',
  fontSize: 14,
  align: 'center',
  valign: 'middle',
});
typescript
const tableSlide = pptx.addSlide();

tableSlide.addText('Sales Summary', {
  x: 0.5, y: 0.5, w: 9, h: 0.8,
  fontSize: 28, bold: true,
});

const tableData = [
  [{ text: 'Region', options: { bold: true, fill: '0066CC', color: 'FFFFFF' } },
   { text: 'Q1', options: { bold: true, fill: '0066CC', color: 'FFFFFF' } },
   { text: 'Q2', options: { bold: true, fill: '0066CC', color: 'FFFFFF' } }],
  ['North America', '$2.5M', '$2.8M'],
  ['Europe', '$1.8M', '$2.1M'],
  ['Asia Pacific', '$1.2M', '$1.5M'],
];

tableSlide.addTable(tableData, {
  x: 0.5, y: 1.5, w: 9,
  border: { pt: 1, color: 'CCCCCC' },
  fontFace: 'Arial',
  fontSize: 14,
  align: 'center',
  valign: 'middle',
});

Charts

图表

typescript
const chartSlide = pptx.addSlide();

chartSlide.addText('Revenue Trend', {
  x: 0.5, y: 0.5, w: 9, h: 0.6,
  fontSize: 28, bold: true,
});

chartSlide.addChart(pptx.ChartType.line, [
  { name: 'Revenue', labels: ['Jan', 'Feb', 'Mar', 'Apr'], values: [100, 120, 150, 180] },
  { name: 'Expenses', labels: ['Jan', 'Feb', 'Mar', 'Apr'], values: [80, 85, 90, 95] },
], {
  x: 0.5, y: 1.2, w: 9, h: 4,
  showLegend: true,
  legendPos: 'b',
  showTitle: false,
});
typescript
const chartSlide = pptx.addSlide();

chartSlide.addText('Revenue Trend', {
  x: 0.5, y: 0.5, w: 9, h: 0.6,
  fontSize: 28, bold: true,
});

chartSlide.addChart(pptx.ChartType.line, [
  { name: 'Revenue', labels: ['Jan', 'Feb', 'Mar', 'Apr'], values: [100, 120, 150, 180] },
  { name: 'Expenses', labels: ['Jan', 'Feb', 'Mar', 'Apr'], values: [80, 85, 90, 95] },
], {
  x: 0.5, y: 1.2, w: 9, h: 4,
  showLegend: true,
  legendPos: 'b',
  showTitle: false,
});

Images

图片

typescript
import { readFileSync } from 'fs';

const imageSlide = pptx.addSlide();

// From file (Node.js)
imageSlide.addImage({
  path: 'logo.png',
  x: 0.5, y: 0.5, w: 2, h: 1,
});

// From base64
const imageBase64 = readFileSync('chart.png').toString('base64');
imageSlide.addImage({
  data: `image/png;base64,${imageBase64}`,
  x: 0.5, y: 2, w: 4, h: 3,
});

// From URL (Node.js only - uses https module)
imageSlide.addImage({
  path: 'https://example.com/image.png',
  x: 5, y: 2, w: 4, h: 3,
});
typescript
import { readFileSync } from 'fs';

const imageSlide = pptx.addSlide();

// 从文件加载(Node.js)
imageSlide.addImage({
  path: 'logo.png',
  x: 0.5, y: 0.5, w: 2, h: 1,
});

// 从Base64加载
const imageBase64 = readFileSync('chart.png').toString('base64');
imageSlide.addImage({
  data: `image/png;base64,${imageBase64}`,
  x: 0.5, y: 2, w: 4, h: 3,
});

// 从URL加载(仅Node.js - 依赖https模块)
imageSlide.addImage({
  path: 'https://example.com/image.png',
  x: 5, y: 2, w: 4, h: 3,
});

Shapes

图形

typescript
const shapeSlide = pptx.addSlide();

// Rectangle
shapeSlide.addShape(pptx.ShapeType.rect, {
  x: 0.5, y: 0.5, w: 3, h: 2,
  fill: { color: '0066CC' },
  line: { color: '004499', pt: 2 },
});

// Circle/Oval
shapeSlide.addShape(pptx.ShapeType.ellipse, {
  x: 4, y: 0.5, w: 2, h: 2,
  fill: { color: '00AA00' },
});

// Arrow
shapeSlide.addShape(pptx.ShapeType.rightArrow, {
  x: 1, y: 3, w: 3, h: 1,
  fill: { color: 'FF6600' },
});
typescript
const shapeSlide = pptx.addSlide();

// 矩形
shapeSlide.addShape(pptx.ShapeType.rect, {
  x: 0.5, y: 0.5, w: 3, h: 2,
  fill: { color: '0066CC' },
  line: { color: '004499', pt: 2 },
});

// 圆形/椭圆形
shapeSlide.addShape(pptx.ShapeType.ellipse, {
  x: 4, y: 0.5, w: 2, h: 2,
  fill: { color: '00AA00' },
});

// 箭头
shapeSlide.addShape(pptx.ShapeType.rightArrow, {
  x: 1, y: 3, w: 3, h: 1,
  fill: { color: 'FF6600' },
});

Slide Layouts and Masters

幻灯片布局与母版

typescript
// Define a master slide
pptx.defineSlideMaster({
  title: 'COMPANY_MASTER',
  background: { color: 'FFFFFF' },
  objects: [
    { text: { text: 'Company Name', options: { x: 0.5, y: 0.2, w: 4, h: 0.3, fontSize: 10, color: '999999' } } },
    { line: { x: 0.5, y: 0.6, w: 9, h: 0, line: { color: '0066CC', pt: 2 } } },
  ],
});

// Use the master
const slide = pptx.addSlide({ masterName: 'COMPANY_MASTER' });
typescript
// 定义母版幻灯片
pptx.defineSlideMaster({
  title: 'COMPANY_MASTER',
  background: { color: 'FFFFFF' },
  objects: [
    { text: { text: 'Company Name', options: { x: 0.5, y: 0.2, w: 4, h: 0.3, fontSize: 10, color: '999999' } } },
    { line: { x: 0.5, y: 0.6, w: 9, h: 0, line: { color: '0066CC', pt: 2 } } },
  ],
});

// 使用母版
const slide = pptx.addSlide({ masterName: 'COMPANY_MASTER' });

Export Patterns

导出方式

typescript
// Node.js - Save to file
await pptx.writeFile({ fileName: 'presentation.pptx' });

// Browser - Trigger download
await pptx.writeFile({ fileName: 'presentation.pptx' });

// Get as base64 (for email, API, etc.)
const base64 = await pptx.write({ outputType: 'base64' });

// Get as Blob (browser)
const blob = await pptx.write({ outputType: 'blob' });

// Get as ArrayBuffer
const arrayBuffer = await pptx.write({ outputType: 'arraybuffer' });

// Cloudflare Workers - Return as Response
const arrayBuffer = await pptx.write({ outputType: 'arraybuffer' });
return new Response(arrayBuffer, {
  headers: {
    'Content-Type': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
    'Content-Disposition': 'attachment; filename="presentation.pptx"',
  },
});

typescript
// Node.js - 保存到文件
await pptx.writeFile({ fileName: 'presentation.pptx' });

// 浏览器 - 触发下载
await pptx.writeFile({ fileName: 'presentation.pptx' });

// 获取Base64编码(用于邮件、API等场景)
const base64 = await pptx.write({ outputType: 'base64' });

// 获取Blob对象(浏览器)
const blob = await pptx.write({ outputType: 'blob' });

// 获取ArrayBuffer
const arrayBuffer = await pptx.write({ outputType: 'arraybuffer' });

// Cloudflare Workers - 作为Response返回
const arrayBuffer = await pptx.write({ outputType: 'arraybuffer' });
return new Response(arrayBuffer, {
  headers: {
    'Content-Type': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
    'Content-Disposition': 'attachment; filename="presentation.pptx"',
  },
});

Cloudflare Workers Integration

Cloudflare Workers集成

All four libraries work in Cloudflare Workers (PPTX with caveats for remote images).
所有四个库均可在Cloudflare Workers中运行(PPTX的远程图片加载存在限制)。

DOCX Generation Endpoint

DOCX生成接口

typescript
import { Hono } from 'hono';
import { Document, Packer, Paragraph, TextRun } from 'docx';

const app = new Hono();

app.get('/api/invoice/:id', async (c) => {
  const invoiceId = c.req.param('id');

  const doc = new Document({
    sections: [{
      children: [
        new Paragraph({
          children: [new TextRun({ text: `Invoice #${invoiceId}`, bold: true, size: 48 })],
        }),
      ],
    }],
  });

  const buffer = await Packer.toBuffer(doc);

  return new Response(buffer, {
    headers: {
      'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      'Content-Disposition': `attachment; filename="invoice-${invoiceId}.docx"`,
    },
  });
});

export default app;
typescript
import { Hono } from 'hono';
import { Document, Packer, Paragraph, TextRun } from 'docx';

const app = new Hono();

app.get('/api/invoice/:id', async (c) => {
  const invoiceId = c.req.param('id');

  const doc = new Document({
    sections: [{
      children: [
        new Paragraph({
          children: [new TextRun({ text: `Invoice #${invoiceId}`, bold: true, size: 48 })],
        }),
      ],
    }],
  });

  const buffer = await Packer.toBuffer(doc);

  return new Response(buffer, {
    headers: {
      'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      'Content-Disposition': `attachment; filename="invoice-${invoiceId}.docx"`,
    },
  });
});

export default app;

HTML to PDF with Browser Rendering

使用浏览器渲染将HTML转为PDF

For complex layouts with CSS, use Cloudflare Browser Rendering (paid feature):
typescript
import puppeteer from '@cloudflare/puppeteer';

export default {
  async fetch(request, env) {
    const browser = await puppeteer.launch(env.BROWSER);
    const page = await browser.newPage();

    // Set HTML content
    await page.setContent(`
      <html>
        <head>
          <style>
            body { font-family: Arial, sans-serif; padding: 40px; }
            h1 { color: #333; }
            table { width: 100%; border-collapse: collapse; }
            td, th { border: 1px solid #ddd; padding: 8px; }
          </style>
        </head>
        <body>
          <h1>Invoice #12345</h1>
          <table>
            <tr><th>Item</th><th>Amount</th></tr>
            <tr><td>Service</td><td>$500</td></tr>
          </table>
        </body>
      </html>
    `);

    const pdf = await page.pdf({ format: 'A4' });
    await browser.close();

    return new Response(pdf, {
      headers: {
        'Content-Type': 'application/pdf',
        'Content-Disposition': 'attachment; filename="invoice.pdf"',
      },
    });
  },
};
wrangler.jsonc binding:
jsonc
{
  "browser": {
    "binding": "BROWSER"
  }
}

对于包含复杂CSS布局的场景,可使用Cloudflare浏览器渲染功能(付费特性):
typescript
import puppeteer from '@cloudflare/puppeteer';

export default {
  async fetch(request, env) {
    const browser = await puppeteer.launch(env.BROWSER);
    const page = await browser.newPage();

    // 设置HTML内容
    await page.setContent(`
      <html>
        <head>
          <style>
            body { font-family: Arial, sans-serif; padding: 40px; }
            h1 { color: #333; }
            table { width: 100%; border-collapse: collapse; }
            td, th { border: 1px solid #ddd; padding: 8px; }
          </style>
        </head>
        <body>
          <h1>Invoice #12345</h1>
          <table>
            <tr><th>Item</th><th>Amount</th></tr>
            <tr><td>Service</td><td>$500</td></tr>
          </table>
        </body>
      </html>
    `);

    const pdf = await page.pdf({ format: 'A4' });
    await browser.close();

    return new Response(pdf, {
      headers: {
        'Content-Type': 'application/pdf',
        'Content-Disposition': 'attachment; filename="invoice.pdf"',
      },
    });
  },
};
wrangler.jsonc绑定配置:
jsonc
{
  "browser": {
    "binding": "BROWSER"
  }
}

Critical Rules

关键规则

Always Do

必须遵循

✅ Use
await Packer.toBuffer()
for DOCX (it's async) ✅ Remember PDF coordinates start at BOTTOM-LEFT ✅ Use
type: 'buffer'
for XLSX in Workers/browser ✅ Embed fonts in PDF before using them ✅ Set proper Content-Type headers for downloads ✅ Use
await pptx.writeFile()
or
await pptx.write()
for PPTX ✅ Use base64 images in PPTX for Workers (avoid remote URLs)
✅ 对DOCX使用
await Packer.toBuffer()
(该方法为异步) ✅ 记住PDF坐标原点位于左下角 ✅ 在Workers/浏览器中对XLSX使用
type: 'buffer'
✅ 在PDF中使用字体前先嵌入 ✅ 为下载操作设置正确的Content-Type头 ✅ 对PPTX使用
await pptx.writeFile()
await pptx.write()
✅ 在Workers中使用Base64格式的图片(避免远程URL)

Never Do

禁止操作

❌ Use
Packer.toBuffer()
without await (returns Promise) ❌ Assume PDF y=0 is at top (it's at bottom) ❌ Use
writeFile
in Workers (use Response instead) ❌ Forget to set Content-Disposition for downloads ❌ Use Node.js fs module in browser/Workers ❌ Use PPTX path URLs in Workers (https module not available)

❌ 不使用await直接调用
Packer.toBuffer()
(会返回Promise) ❌ 假设PDF的y=0位于顶部(实际为底部) ❌ 在Workers中使用
writeFile
(改用Response返回) ❌ 下载时忘记设置Content-Disposition头 ❌ 在浏览器/Workers中使用Node.js的fs模块 ❌ 在Workers中使用PPTX的远程URL图片(https模块不可用)

Known Issues Prevention

已知问题预防

Issue #1: DOCX Packer Returns Promise

问题1:DOCX Packer返回Promise

Error:
[object Promise]
in file or empty document Why:
Packer.toBuffer()
is async but called without await Prevention: Always
await Packer.toBuffer(doc)
错误表现:文件内容为
[object Promise]
或文档为空 原因
Packer.toBuffer()
是异步方法但未使用await调用 预防方案:始终使用
await Packer.toBuffer(doc)

Issue #2: PDF Text at Wrong Position

问题2:PDF文本位置错误

Error: Text appears at bottom of page or off-page Why: PDF coordinates have origin at bottom-left, not top-left Prevention: For text near top, use high y values (e.g., y=750 for letter size)
错误表现:文本出现在页面底部或页面外 原因:PDF坐标原点位于左下角,而非左上角 预防方案:要在页面顶部附近显示文本,使用较大的y值(例如信纸尺寸使用y=750)

Issue #3: XLSX Formula Not Calculating

问题3:XLSX公式未计算

Error: Cell shows formula text instead of result Why: SheetJS doesn't execute formulas, Excel does on open Prevention: This is expected - formulas calculate when opened in Excel
错误表现:单元格显示公式文本而非计算结果 原因:SheetJS不执行公式,由Excel在打开时计算 预防方案:此为预期行为 - 公式会在Excel中打开时自动计算

Issue #4: Workers DOCX/PDF Returns Empty

问题4:Workers中DOCX/PDF返回空内容

Error: Downloaded file is empty or corrupted Why: Returning wrong type or missing Content-Type header Prevention: Return buffer directly with proper headers
错误表现:下载的文件为空或损坏 原因:返回类型错误或缺少Content-Type头 预防方案:直接返回Buffer并设置正确的响应头

Issue #5: PPTX Remote Images Fail in Workers

问题5:Workers中PPTX远程图片加载失败

Error:
https is not defined
or image not appearing Why: pptxgenjs uses Node.js
https
module for remote images Prevention: Use base64 data URIs or local images in Workers environment

错误表现
https is not defined
或图片不显示 原因:pptxgenjs使用Node.js的
https
模块加载远程图片 预防方案:在Workers环境中使用Base64数据URI或本地图片

Using Bundled Resources

使用捆绑资源

Templates (templates/)

模板(templates/)

  • docx-basic.ts
    - Complete Word document with headings, tables, images
  • xlsx-basic.ts
    - Excel workbook with formulas and formatting
  • pdf-basic.ts
    - PDF with text, images, shapes
  • pptx-basic.ts
    - PowerPoint with slides, charts, tables
  • workers-pdf.ts
    - Cloudflare Workers PDF generation example
  • docx-basic.ts
    - 完整的Word文档示例,包含标题、表格、图片
  • xlsx-basic.ts
    - 包含公式和格式的Excel工作簿示例
  • pdf-basic.ts
    - 包含文本、图片、图形的PDF示例
  • pptx-basic.ts
    - 包含幻灯片、图表、表格的PowerPoint示例
  • workers-pdf.ts
    - Cloudflare Workers PDF生成示例

References (references/)

参考文档(references/)

  • docx-api.md
    - Quick reference for docx npm package
  • xlsx-api.md
    - Quick reference for SheetJS functions
  • pdf-lib-api.md
    - Quick reference for pdf-lib methods
  • pptxgenjs-api.md
    - Quick reference for pptxgenjs
  • docx-api.md
    - docx npm包快速参考
  • xlsx-api.md
    - SheetJS函数快速参考
  • pdf-lib-api.md
    - pdf-lib方法快速参考
  • pptxgenjs-api.md
    - pptxgenjs快速参考

Scripts (scripts/)

脚本(scripts/)

  • verify-deps.sh
    - Check library versions are current

  • verify-deps.sh
    - 检查库版本是否为最新

Limitations vs Anthropic's Official Skills

与Anthropic官方技能的对比

This skill focuses on document creation with portable TypeScript libraries:
FeatureThis SkillAnthropic's Skills
Create DOCX/XLSX/PDF/PPTX
Works in Cloudflare Workers
Plugin installable
TypeScript-first❌ (Python)
Edit existing DOCX with tracked changes
Excel formula validation
OCR scanned PDFs
For advanced editing scenarios (tracked changes, formula validation), consider Anthropic's official skills with Python tooling.

本技能专注于使用可移植的TypeScript库进行文档创建
特性本技能Anthropic官方技能
创建DOCX/XLSX/PDF/PPTX
支持Cloudflare Workers
可作为插件安装
优先支持TypeScript❌(基于Python)
编辑带修订记录的现有DOCX
Excel公式验证
扫描PDF的OCR识别
对于高级编辑场景(如修订记录、公式验证),建议使用Anthropic基于Python工具链的官方技能。

Package Versions (Verified 2026-01-12)

包版本(2026-01-12验证)

json
{
  "dependencies": {
    "docx": "^9.5.0",
    "xlsx": "^0.18.5",
    "pdf-lib": "^1.17.1",
    "pptxgenjs": "^4.0.1"
  }
}

json
{
  "dependencies": {
    "docx": "^9.5.0",
    "xlsx": "^0.18.5",
    "pdf-lib": "^1.17.1",
    "pptxgenjs": "^4.0.1"
  }
}

Official Documentation

官方文档

Complete Setup Checklist

完整设置检查清单

  • Installed required packages (
    npm install docx xlsx pdf-lib
    )
  • Verified import statements match runtime (Node.js vs browser)
  • Used proper export method for target environment
  • Set Content-Type headers for HTTP responses
  • Tested document opens correctly in target application
  • Confirmed Workers compatibility (if deploying to edge)
  • 已安装所需包(
    npm install docx xlsx pdf-lib
  • 验证导入语句与运行环境匹配(Node.js vs 浏览器)
  • 针对目标环境使用了正确的导出方法
  • 为HTTP响应设置了正确的Content-Type头
  • 测试文档可在目标应用中正常打开
  • 确认Workers兼容性(如果部署到边缘环境)