New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@lpen/docx

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@lpen/docx

A high-performance, type-safe Node.js library for parsing .docx files

latest
npmnpm
Version
1.0.23
Version published
Maintainers
1
Created
Source

@lpen/docx

一个高性能、类型安全的 Node.js 库,用于解析 .docx 文件并将其转换为 HTML,同时也支持将 HTML 转换为 .docx 文档。

特性

  • 🚀 高性能:针对大型文档进行了优化
  • 📦 类型安全:完整的 TypeScript 支持,包含类型定义
  • 🎨 丰富的格式支持:保留样式、图片、表格、列表等
  • 🔧 简单易用:一行代码完成解析和转换
  • 📄 完整支持:处理页眉、页脚、批注(包括回复和解决状态)和复杂布局
  • 🔄 双向转换:支持 DOCX → HTML 和 HTML → DOCX
  • 📊 四向转换:支持 DOCX ↔ JSON ↔ HTML 的完整转换链,JSON 格式为标准化 AST
  • 📊 表格支持:完整的表格功能,包括边框、宽度、合并单元格等
  • 📑 页眉页脚支持:完整支持页眉和页脚的转换,包括 DOCX 转 HTML 和 HTML 转 DOCX
  • 📄 分页符支持:支持 Word 文档中的分页符,HTML 转 DOCX 时可通过样式或类名识别分页符

安装

npm install @lpen/docx
# 或
pnpm add @lpen/docx
# 或
yarn add @lpen/docx

快速开始

DOCX 转 HTML

最简单的方式(推荐)

import { parseDocx } from '@lpen/docx';
import fs from 'fs-extra';

async function main() {
  // 一行代码完成所有操作:加载、解析、转换为 HTML
  const html = await parseDocx('path/to/document.docx');

  // 保存结果
  await fs.writeFile('output.html', html, 'utf-8');
}

main();

自定义选项

import { parseDocx } from '@lpen/docx';

// 指定输出选项
const html = await parseDocx('document.docx', {
  imageMode: 'base64',        // 图片处理方式:'base64'(默认)、'skip' 或 'placeholder'
  includeHeaders: true,       // 包含页眉,默认 true
  includeFooters: true,       // 包含页脚,默认 true
  includeMetadata: false,     // 包含元数据,默认 false
  includeComments: true,      // 包含批注,默认 true
  cssClassPrefix: 'docx-',    // 自定义 CSS 类名前缀
  pageBreakClass: 'docx-page-break', // 分页符的 CSS 类名(默认 "docx-page-break")
});

高级用法:使用 DocxParser 类

如果需要更细粒度的控制,可以使用 DocxParser 类:

import { DocxParser, HtmlConverter } from '@lpen/docx';
import fs from 'fs-extra';

async function main() {
  const parser = new DocxParser();
  
  // 从文件加载
  await parser.loadFromFile('document.docx');
  
  // 或者从 Buffer 加载
  // const buffer = await fs.readFile('document.docx');
  // await parser.load(buffer);
  
  // 获取 AST(抽象语法树)
  const ast = parser.getAST();
  
  // 获取元数据
  const metadata = parser.getMetadata();
  
  // 获取图片
  const images = parser.getImages();
  
  // 获取页眉
  const headers = parser.getHeaders();
  
  // 获取页脚
  const footers = parser.getFooters();
  
  // 使用 HtmlConverter 转换为 HTML
  const converter = new HtmlConverter({
    imageMode: 'base64',
    includeMetadata: true,
  });
  
  const html = converter.convert(
    ast,
    images,
    (id) => parser.getComment(id) // 批注获取函数
  );
  
  await fs.writeFile('output.html', html, 'utf-8');
}

main();

HTML 转 DOCX

最简单的方式(推荐)

import { htmlToWord } from '@lpen/docx';
import fs from 'fs-extra';

async function main() {
  const html = '<h1>标题</h1><p>这是段落内容。</p>';
  
  // 一行代码完成转换
  const docx = await htmlToWord(html);
  
  // 保存文档
  await fs.writeFile('output.docx', docx);
}

main();

自定义选项

import { htmlToWord } from '@lpen/docx';
import fs from 'fs-extra';

const html = `
  <h1>我的文档</h1>
  <p>这是段落内容。</p>
  <table>
    <tr>
      <td>单元格 1</td>
      <td>单元格 2</td>
    </tr>
  </table>
`;

const docx = await htmlToWord(html, {
  title: '我的文档',
  creator: 'docx-parser',
  orientation: 'portrait',  // 'portrait'(默认)或 'landscape'
  margins: {
    top: 1440,    // 1 英寸 = 1440 twips
    right: 1440,
    bottom: 1440,
    left: 1440,
    header: 720,  // 0.5 英寸
    footer: 720,
    gutter: 0,
  },
  font: 'Microsoft YaHei',  // 默认字体
  fontSize: 12,              // 默认字体大小(单位:pt)
  lang: 'zh-CN',             // 文档语言代码
  direction: 'ltr',          // 文本方向:'ltr'(默认)或 'rtl'
  heading: {
    heading1: {
      font: 'Microsoft YaHei',
      fontSize: 24,
      bold: true,
      spacing: {
        before: 240,  // 段前间距(单位:twips)
        after: 40,    // 段后间距(单位:twips)
      },
      keepLines: true,
      keepNext: false,
      outlineLevel: 1,
    },
    heading2: {
      fontSize: 18,
      bold: true,
    },
  },
  imageProcessing: {
    maxRetries: 3,           // 最大重试次数
    downloadTimeout: 5000,   // 下载超时时间(毫秒)
    svgHandling: 'auto',     // SVG 处理方式:'convert'、'native' 或 'auto'
  },
  pageBreak: {
    enabled: true,           // 是否启用分页符识别(默认 true)
    class: 'docx-page-break', // 分页符的 CSS 类名(默认 "docx-page-break")
  },
});

await fs.writeFile('output.docx', docx);

带页眉和页脚

import { htmlToWord } from '@lpen/docx';
import fs from 'fs-extra';

const html = '<h1>标题</h1><p>这是段落内容。</p>';

// 带页眉和页脚
const docx = await htmlToWord(
  html,
  {
    header: true,    // 启用页眉
    footer: true,    // 启用页脚
    skipFirstHeaderFooter: false,  // 是否跳过首页的页眉页脚
  },
  '<p style="text-align: center;">这是页眉内容</p>',  // 页眉 HTML
  '<p style="text-align: center;">第 <span style="page-number">1</span> 页</p>'  // 页脚 HTML
);

await fs.writeFile('output.docx', docx);

API 文档

DOCX 转 HTML

parseDocx(推荐)

最简单的 API,一行代码完成所有操作。

parseDocx(input: string | Buffer, options?: ParseOptions): Promise<string>

参数:

  • input: 文件路径(string)、HTTP/HTTPS URL 或文件 Buffer
  • options: 可选配置项
    • imageMode: 图片处理方式,'base64'(默认)、'skip''placeholder'
    • includeHeaders: 是否包含页眉,默认 true
    • includeFooters: 是否包含页脚,默认 true
    • includeMetadata: 是否包含元数据,默认 false
    • includeComments: 是否包含批注,默认 true
    • cssClassPrefix: 自定义 CSS 类名前缀
    • pageBreakClass: 分页符的 CSS 类名,默认 "docx-page-break"

返回值:

返回 HTML 字符串

示例:

// 从本地文件解析
const html = await parseDocx('document.docx');

// 从 URL 下载并解析
const html = await parseDocx('https://example.com/document.docx');

// 使用 Buffer
const buffer = await fs.readFile('document.docx');
const html = await parseDocx(buffer);

// 自定义选项
const html = await parseDocx('document.docx', {
  imageMode: 'skip',
  includeHeaders: false,
});

DocxParser 类(高级用法)

如果需要更细粒度的控制,可以使用 DocxParser 类:

import { DocxParser, HtmlConverter } from '@lpen/docx';

const parser = new DocxParser();

// 从文件加载
await parser.loadFromFile('document.docx');

// 或从 Buffer 加载
await parser.load(buffer);

// 获取 AST
const ast = parser.getAST();

// 获取元数据
const metadata = parser.getMetadata();

// 获取图片
const images = parser.getImages();

// 获取页眉
const headers = parser.getHeaders();

// 获取页脚
const footers = parser.getFooters();

// 获取批注
const comment = parser.getComment(commentId);

// 使用 HtmlConverter 转换为 HTML
const converter = new HtmlConverter({
  imageMode: 'base64',
  includeMetadata: true,
});

const html = converter.convert(ast, images, (id) => parser.getComment(id));

HTML 转 DOCX

htmlToWord(推荐)

将 HTML 字符串转换为 Word (.docx) 文档。

htmlToWord(
  htmlString: string,
  options?: HtmlToWordOptions,
  headerHTMLString?: string,
  footerHTMLString?: string
): Promise<Buffer>

参数:

  • htmlString: HTML 字符串
  • options: 可选配置项
    • title: 文档标题
    • creator: 创建者名称
    • orientation: 页面方向,'portrait'(默认)或 'landscape'
    • margins: 页边距(单位:twips,1 twip = 1/20 pt)
      • top, right, bottom, left, header, footer, gutter
    • font: 默认字体,默认 'Calibri'
    • fontSize: 默认字体大小(单位:pt),默认 11
    • lang: 文档语言代码,默认 'en-US'
    • direction: 文本方向,'ltr'(默认)或 'rtl'
    • header: 是否启用页眉,默认 false
    • footer: 是否启用页脚,默认 false
    • skipFirstHeaderFooter: 是否跳过首页的页眉页脚,默认 false
    • headerType: 页眉类型,'default'(默认)、'first'(首页)或 'even'(偶数页)
    • footerType: 页脚类型,'default'(默认)、'first'(首页)或 'even'(偶数页)
    • heading: 标题样式配置(H1-H6)
      • font, fontSize, bold, spacing, keepLines, keepNext, outlineLevel
    • imageProcessing: 图片处理选项
      • maxRetries: 最大重试次数
      • downloadTimeout: 下载超时时间(毫秒)
      • svgHandling: SVG 处理方式,'convert'(转换为 PNG)、'native'(原生支持)或 'auto'(自动)
    • pageBreak: 分页符配置选项
      • enabled: 是否启用分页符识别,默认 true
      • class: 分页符的 CSS 类名,默认 "docx-page-break"
  • headerHTMLString: 页眉 HTML 字符串(可选),当 options.headertrue 时使用
  • footerHTMLString: 页脚 HTML 字符串(可选),当 options.footertrue 时使用

返回值:

返回 .docx 文件的 Buffer

示例:

import { htmlToWord } from '@lpen/docx';
import fs from 'fs-extra';

// 基本使用
const html = '<h1>标题</h1><p>这是段落内容。</p>';
const docx = await htmlToWord(html);
await fs.writeFile('output.docx', docx);

// 带选项
const docx = await htmlToWord(html, {
  title: '我的文档',
  creator: 'docx-parser',
  orientation: 'portrait',
  margins: {
    top: 1440,
    right: 1440,
    bottom: 1440,
    left: 1440,
  },
  font: 'Microsoft YaHei',
  fontSize: 12,
  lang: 'zh-CN',
});

// 处理图片(支持 data URL 和外部 URL)
const htmlWithImage = `
  <h1>带图片的文档</h1>
  <p>内联图片:<img src="data:image/png;base64,..." width="200" height="150" /></p>
  <p>外部图片:<img src="https://example.com/image.jpg" alt="示例图片" /></p>
`;

const docx = await htmlToWord(htmlWithImage, {
  imageProcessing: {
    downloadTimeout: 5000,
    maxRetries: 3,
  },
});

await fs.writeFile('output.docx', docx);

// 带页眉和页脚
const html = '<h1>标题</h1><p>这是段落内容。</p>';
const docxWithHeaderFooter = await htmlToWord(
  html,
  {
    header: true,
    footer: true,
    skipFirstHeaderFooter: false,
  },
  '<p style="text-align: center;">这是页眉内容</p>',
  '<p style="text-align: center;">第 <span style="page-number">1</span> 页</p>'
);

await fs.writeFile('output-with-header-footer.docx', docxWithHeaderFooter);

分页符

在 HTML 中使用分页符,可以通过以下方式:

方式 1:使用内联样式

const html = `
  <h1>第一页标题</h1>
  <p>这是第一页的内容。</p>
  <div style="page-break-after: always"></div>
  <h2>第二页标题</h2>
  <p>这是第二页的内容。</p>
`;

const docx = await htmlToWord(html);
await fs.writeFile('output.docx', docx);

方式 2:使用 CSS 类名

const html = `
  <h1>第一页标题</h1>
  <p>这是第一页的内容。</p>
  <div class="docx-page-break"></div>
  <h2>第二页标题</h2>
  <p>这是第二页的内容。</p>
`;

const docx = await htmlToWord(html);
await fs.writeFile('output.docx', docx);

方式 3:使用简化的类名

const html = `
  <h1>第一页标题</h1>
  <p>这是第一页的内容。</p>
  <div class="page-break"></div>
  <h2>第二页标题</h2>
  <p>这是第二页的内容。</p>
`;

const docx = await htmlToWord(html);
await fs.writeFile('output.docx', docx);

自定义分页符类名

const html = `
  <h1>第一页标题</h1>
  <p>这是第一页的内容。</p>
  <div class="my-custom-page-break"></div>
  <h2>第二页标题</h2>
  <p>这是第二页的内容。</p>
`;

const docx = await htmlToWord(html, {
  pageBreak: {
    enabled: true,
    class: 'my-custom-page-break', // 自定义类名
  },
});
await fs.writeFile('output.docx', docx);

禁用分页符识别

const docx = await htmlToWord(html, {
  pageBreak: {
    enabled: false, // 禁用分页符识别
  },
});

四向转换(DOCX ↔ JSON ↔ HTML)

本库支持完整的四向转换,JSON 格式为标准化 AST(抽象语法树),可以作为中间格式进行数据操作和转换。

转换流程图

DOCX ──docxToJson──> JSON (AST) ──jsonToHtml──> HTML
  ↑                                    ↓
  │                              htmlToJson
  │                                    ↓
jsonToDocx                      JSON (AST)

1. DOCX → JSON (AST)

将 DOCX 文件转换为 JSON 格式的 AST。

import { docxToJson } from '@lpen/docx';
import fs from 'fs-extra';

async function main() {
  // 从本地文件解析
  const ast = await docxToJson('document.docx');
  
  // 从 URL 下载并解析
  const ast = await docxToJson('https://example.com/document.docx');
  
  // 从 Buffer 解析
  const buffer = await fs.readFile('document.docx');
  const ast = await docxToJson(buffer);
  
  // AST 就是 JSON 格式,可以直接序列化
  const jsonString = JSON.stringify(ast, null, 2);
  await fs.writeFile('document.json', jsonString, 'utf-8');
  
  // 或者直接使用 AST 对象进行操作
  console.log('文档类型:', ast.type);
  console.log('子节点数量:', ast.children?.length || 0);
}

main();

2. HTML → JSON (AST)

将 HTML 字符串转换为 JSON 格式的 AST。

import { htmlToJson } from '@lpen/docx';
import fs from 'fs-extra';

const html = '<h1>标题</h1><p>这是段落内容。</p>';

// 基本使用
const ast = htmlToJson(html);

// AST 就是 JSON 格式,可以直接序列化
const jsonString = JSON.stringify(ast, null, 2);
await fs.writeFile('document.json', jsonString, 'utf-8');

// 带选项
const ast = htmlToJson(html, {
  skipHTMLMinify: false,
  decodeUnicode: true,
  preserveWhitespace: false,
});

3. JSON (AST) → HTML

将 JSON 格式的 AST 转换为 HTML 字符串。

import { jsonToHtml } from '@lpen/docx';
import fs from 'fs-extra';

// 从 JSON 文件读取 AST
const jsonString = await fs.readFile('document.json', 'utf-8');
const ast = JSON.parse(jsonString);

// 基本使用
const html = jsonToHtml(ast);

// 带选项和图片
const html = jsonToHtml(ast, {
  imageMode: 'base64',
  includeMetadata: false,
  cssClassPrefix: 'docx-',
}, images);

// 带批注支持
const html = jsonToHtml(ast, {}, images, (id) => {
  // 返回批注定义
  return commentMap.get(id);
});

await fs.writeFile('output.html', html, 'utf-8');

4. JSON (AST) → DOCX

将 JSON 格式的 AST 转换为 DOCX 文档。

import { jsonToDocx } from '@lpen/docx';
import fs from 'fs-extra';

// 从 JSON 文件读取 AST
const jsonString = await fs.readFile('document.json', 'utf-8');
const ast = JSON.parse(jsonString);

// 基本使用
const docx = await jsonToDocx(ast);

// 带选项
const docx = await jsonToDocx(ast, {
  title: '我的文档',
  creator: 'docx-parser',
  orientation: 'portrait',
  margins: { top: 1440, bottom: 1440 },
  font: 'Microsoft YaHei',
  fontSize: 12,
});

await fs.writeFile('output.docx', docx);

带批注的 JSON (AST) → DOCX 示例

以下示例展示了如何创建包含批注和回复的文档:

import { jsonToDocx } from '@lpen/docx';
import fs from 'fs-extra';

// 批注类型定义(如果需要类型提示)
// 注意:实际使用时,CommentDefinition 和 CommentReply 类型可以从 '@lpen/docx/types' 导入
interface CommentDefinition {
  id: number;
  author: string;
  date: Date;
  initials?: string;
  done?: boolean; // true 表示已解决,false 或 undefined 表示未解决
  content?: any; // DocxNode 类型,批注内容
  replies?: CommentReply[]; // 批注的回复列表
}

interface CommentReply {
  id?: number; // 可选,如果不提供则自动生成
  author: string;
  date: Date;
  initials?: string;
  content: any; // DocxNode 类型,回复内容
}

// 1. 创建包含批注的 JSON AST
const ast = {
  type: "document",
  children: [
    // 第一段:包含主批注和两个回复批注
    {
      type: "paragraph",
      children: [
        {
          type: "commentRangeStart",
          commentId: 0,
          properties: {
            commentId: 0,
            commentType: "commentRangeStart",
          },
        },
        {
          type: "commentRangeStart",
          commentId: 1,
          properties: {
            commentId: 1,
            commentType: "commentRangeStart",
          },
        },
        {
          type: "commentRangeStart",
          commentId: 2,
          properties: {
            commentId: 2,
            commentType: "commentRangeStart",
          },
        },
        {
          type: "run",
          text: "本项目计划于2025年第一季度完成,涉及多个模块的开发与集成工作。",
          properties: {},
        },
        {
          type: "commentRangeEnd",
          commentId: 0,
          properties: {
            commentId: 0,
            commentType: "commentRangeEnd",
          },
        },
        {
          type: "commentReference",
          commentId: 0,
          properties: {
            commentId: 0,
            commentType: "commentReference",
          },
          children: [],
        },
        {
          type: "commentRangeEnd",
          commentId: 1,
          properties: {
            commentId: 1,
            commentType: "commentRangeEnd",
          },
        },
        {
          type: "commentReference",
          commentId: 1,
          properties: {
            commentId: 1,
            commentType: "commentReference",
          },
          children: [],
        },
        {
          type: "commentRangeEnd",
          commentId: 2,
          properties: {
            commentId: 2,
            commentType: "commentRangeEnd",
          },
        },
        {
          type: "commentReference",
          commentId: 2,
          properties: {
            commentId: 2,
            commentType: "commentReference",
          },
          children: [],
        },
      ],
      properties: {
        fontFamily: "微软雅黑",
      },
    },
    // 第二段:包含主批注和一个回复批注
    {
      type: "paragraph",
      children: [
        {
          type: "commentRangeStart",
          commentId: 3,
          properties: {
            commentId: 3,
            commentType: "commentRangeStart",
          },
        },
        {
          type: "commentRangeStart",
          commentId: 4,
          properties: {
            commentId: 4,
            commentType: "commentRangeStart",
          },
        },
        {
          type: "run",
          text: "技术方案需要进一步优化,建议增加性能测试环节以确保系统稳定性。",
          properties: {},
        },
        {
          type: "commentRangeEnd",
          commentId: 3,
          properties: {
            commentId: 3,
            commentType: "commentRangeEnd",
          },
        },
        {
          type: "commentReference",
          commentId: 3,
          properties: {
            commentId: 3,
            commentType: "commentReference",
          },
          children: [],
        },
        {
          type: "commentRangeEnd",
          commentId: 4,
          properties: {
            commentId: 4,
            commentType: "commentRangeEnd",
          },
        },
        {
          type: "commentReference",
          commentId: 4,
          properties: {
            commentId: 4,
            commentType: "commentReference",
          },
          children: [],
        },
      ],
      properties: {
        fontFamily: "微软雅黑",
      },
    },
  ],
};

// 2. 创建批注定义 Map
const commentMap = new Map<number, CommentDefinition>();

// 批注0:第一段的主批注(已解决,有2个回复)
commentMap.set(0, {
  id: 0,
  author: "张明",
  date: new Date("2025-01-15T10:00:00Z"),
  initials: "ZM",
  done: true, // 已解决
  content: {
    type: "document",
    children: [
      {
        type: "paragraph",
        children: [
          {
            type: "run",
            text: "该计划时间安排合理,建议按此执行。",
            properties: {},
          },
        ],
        properties: {},
      },
    ],
  },
  replies: [
    {
      id: 1, // 批注1是批注0的回复
      author: "李华",
      date: new Date("2025-01-15T10:30:00Z"),
      initials: "LH",
      content: {
        type: "document",
        children: [
          {
            type: "paragraph",
            children: [
              {
                type: "run",
                text: "同意,我会协调各部门配合完成。",
                properties: {},
              },
            ],
            properties: {},
          },
        ],
      },
    },
    {
      id: 2, // 批注2是批注0的回复
      author: "王芳",
      date: new Date("2025-01-15T11:00:00Z"),
      initials: "WF",
      content: {
        type: "document",
        children: [
          {
            type: "paragraph",
            children: [
              {
                type: "run",
                text: "已收到,我会跟进项目进度。",
                properties: {},
              },
            ],
            properties: {},
          },
        ],
      },
    },
  ],
});

// 批注3:第二段的主批注(未解决,有1个回复)
commentMap.set(3, {
  id: 3,
  author: "陈强",
  date: new Date("2025-01-15T14:00:00Z"),
  initials: "CQ",
  done: false, // 未解决
  content: {
    type: "document",
    children: [
      {
        type: "paragraph",
        children: [
          {
            type: "run",
            text: "性能测试环节确实很重要,需要尽快落实。",
            properties: {},
          },
        ],
        properties: {},
      },
    ],
  },
  replies: [
    {
      id: 4, // 批注4是批注3的回复
      author: "刘静",
      date: new Date("2025-01-15T14:30:00Z"),
      initials: "LJ",
      content: {
        type: "document",
        children: [
          {
            type: "paragraph",
            children: [
              {
                type: "run",
                text: "我会在本周内完成测试方案的制定。",
                properties: {},
              },
            ],
            properties: {},
          },
        ],
      },
    },
  ],
});

// 3. 转换为 DOCX(传入批注获取函数)
const getComment = (id: number) => commentMap.get(id);
const docx = await jsonToDocx(ast, {
  title: '项目计划文档',
  creator: 'docx-parser',
  font: 'Microsoft YaHei',
  fontSize: 12,
}, getComment);

await fs.writeFile('output-with-comments.docx', docx);

批注结构说明:

  • 主批注:在 AST 中使用 commentRangeStartcommentRangeEndcommentReference 标记批注范围
  • 批注定义:通过 CommentDefinition 接口定义,包含:
    • id: 批注ID
    • author: 作者名称
    • date: 批注日期
    • initials: 作者缩写
    • done: 是否已解决(true 表示已解决,falseundefined 表示未解决)
    • content: 批注内容(AST节点)
    • replies: 批注的回复列表(可选)
  • 回复批注:在 replies 数组中定义,每个回复也是一个 CommentReply 对象,包含 idauthordateinitialscontent
  • 批注获取函数jsonToDocx 的第三个参数是一个函数,用于根据批注ID获取批注定义

完整转换示例

以下示例展示了如何使用 JSON (AST) 作为中间格式进行复杂的转换操作:

import { docxToJson, jsonToHtml, htmlToJson, jsonToDocx } from '@lpen/docx';
import fs from 'fs-extra';

async function complexConversion() {
  // 1. DOCX → JSON
  const ast1 = await docxToJson('input.docx');
  
  // 2. 对 AST 进行操作(例如:修改内容、添加节点等)
  // ast1.children?.push({ ... });
  
  // 3. JSON → HTML
  const html = jsonToHtml(ast1);
  
  // 4. 修改 HTML
  const modifiedHtml = html.replace('旧文本', '新文本');
  
  // 5. HTML → JSON
  const ast2 = htmlToJson(modifiedHtml);
  
  // 6. JSON → DOCX
  const docx = await jsonToDocx(ast2);
  
  await fs.writeFile('output.docx', docx);
}

complexConversion();

AST 结构说明

AST(抽象语法树)是一个标准化的 JSON 格式,用于表示文档结构。主要节点类型包括:

  • document: 文档根节点
  • paragraph: 段落节点
  • run: 文本运行节点
  • hyperlink: 超链接节点
  • image: 图片节点
  • list: 列表节点
  • list_item: 列表项节点
  • table: 表格节点
  • table_row: 表格行节点
  • table_cell: 表格单元格节点
  • commentRangeStart: 批注范围开始节点
  • commentRangeEnd: 批注范围结束节点
  • commentReference: 批注引用节点
  • pageBreak: 分页符节点

每个节点包含:

  • type: 节点类型
  • children: 子节点数组(可选)
  • text: 文本内容(可选)
  • properties: 节点属性(样式、格式等)
  • 其他特定于节点类型的属性

更多详细信息请参考 DocxNode 类型定义。

支持的 HTML 标签和 CSS 样式

支持的 HTML 标签

  • 标题: <h1> - <h6>
  • 段落: <p>
  • 文本格式: <strong>, <b>, <em>, <i>, <u>, <s>, <del>
  • 列表: <ul>, <ol>, <li>
  • 表格: <table>, <tr>, <td>, <th>
    • 支持 colspanrowspan 属性
    • 支持 width 样式(表格和单元格)
  • 链接: <a href="...">
  • 图片: <img src="...">(支持 data URL 和外部 URL)
  • 换行: <br>, <br/>
  • 水平线: <hr>
  • 引用: <blockquote>
  • 代码: <code>, <pre>
  • 分页符: <div style="page-break-after: always"></div><div class="docx-page-break"></div>

支持的 CSS 样式

  • color: 文本颜色
  • background-color: 背景颜色
  • font-size: 字体大小
  • font-family: 字体族
  • font-weight: 字体粗细(bold, normal
  • font-style: 字体样式(italic, normal
  • text-align: 文本对齐(left, center, right, justify
  • text-decoration: 文本装饰(underline, line-through
  • margin: 外边距
  • padding: 内边距
  • border: 边框(支持 border-top, border-right, border-bottom, border-left
  • width, height: 尺寸(用于图片、表格和单元格)
  • vertical-align: 垂直对齐(用于表格单元格)
  • page-break-after: 分页符样式(always 表示在元素后插入分页符)

更多示例

DOCX 转 HTML 示例

从文件路径解析

import { parseDocx } from '@lpen/docx';

const html = await parseDocx('./document.docx');
console.log(html);

从 URL 下载并解析

import { parseDocx } from '@lpen/docx';

// 从 HTTPS URL 下载并解析
const html = await parseDocx('https://example.com/document.docx');

// 从 HTTP URL 下载并解析
const html = await parseDocx('http://example.com/document.docx');

从 Buffer 解析

import { parseDocx } from '@lpen/docx';
import fs from 'fs-extra';

const buffer = await fs.readFile('./document.docx');
const html = await parseDocx(buffer);

自定义图片处理

// 跳过图片
const html = await parseDocx('document.docx', {
  imageMode: 'skip'
});

// 使用占位符
const html = await parseDocx('document.docx', {
  imageMode: 'placeholder'
});

不包含页眉页脚

const html = await parseDocx('document.docx', {
  includeHeaders: false,
  includeFooters: false,
});

不包含批注

const html = await parseDocx('document.docx', {
  includeComments: false,
});

HTML 转 DOCX 示例

基本表格

const html = `
  <table>
    <tr>
      <th>姓名</th>
      <th>年龄</th>
    </tr>
    <tr>
      <td>张三</td>
      <td>25</td>
    </tr>
  </table>
`;

const docx = await htmlToWord(html);
await fs.writeFile('output.docx', docx);

带样式的表格

const html = `
  <table style="width: 432pt; border-collapse: collapse;">
    <tr>
      <td style="width: 186.75pt; border: 1pt solid #000000;">单元格 1</td>
      <td style="width: 186.75pt; border: 1pt solid #000000;">单元格 2</td>
    </tr>
  </table>
`;

const docx = await htmlToWord(html);
await fs.writeFile('output.docx', docx);

合并单元格

const html = `
  <table>
    <tr>
      <td rowspan="2">合并行</td>
      <td>单元格 1</td>
    </tr>
    <tr>
      <td>单元格 2</td>
    </tr>
  </table>
`;

const docx = await htmlToWord(html);
await fs.writeFile('output.docx', docx);

列表

const html = `
  <ul>
    <li>项目 1</li>
    <li>项目 2</li>
  </ul>
  <ol>
    <li>有序项目 1</li>
    <li>有序项目 2</li>
  </ol>
`;

const docx = await htmlToWord(html);
await fs.writeFile('output.docx', docx);

许可证

MIT

联系方式

如有问题或建议,欢迎联系:

  • 📧 邮箱:2736661400@qq.com

问题报告格式

为了更好地帮助您解决问题,请在邮件中包含以下信息:

邮件主题格式:

[@lpen/docx] 问题描述

邮件内容模板:

问题描述:
[请简要描述您遇到的问题]

复现步骤:
1. [步骤 1]
2. [步骤 2]
3. [步骤 3]

预期行为:
[描述您期望的行为]

实际行为:
[描述实际发生的行为]

环境信息:
- Node.js 版本:[例如:v18.0.0]
- 操作系统:[例如:Windows 10 / macOS 13 / Linux Ubuntu 22.04]
- 库版本:[例如:@lpen/docx@1.0.8]

代码示例:
[请提供相关的代码示例,如果可能的话]

错误信息:
[如果有错误信息,请完整粘贴]

附加信息:
[任何其他可能有助于解决问题的信息]

示例:

问题描述:
表格宽度在 HTML 转 DOCX 时没有正确保留

复现步骤:
1. 创建一个包含表格的 HTML 文件
2. 使用 htmlToWord 函数转换
3. 打开生成的 DOCX 文件

预期行为:
表格宽度应该与 HTML 中的 width 样式一致

实际行为:
表格宽度变成了默认值,没有保留原始宽度

环境信息:
- Node.js 版本:v18.17.0
- 操作系统:Windows 10
- 库版本:@lpen/docx@1.0.8

代码示例:
const html = '<table style="width: 432pt;"><tr><td>内容</td></tr></table>';
const docx = await htmlToWord(html);

Keywords

docx

FAQs

Package last updated on 18 Nov 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts