使用 JavaScript 在 React 中实现 Word 转 PDF

在 React 项目中实现 Word 转 PDF,是文档管理系统、电子合同签署、报告生成等场景的常见需求。相比于依赖后端服务,在浏览器端(客户端)直接完成转换具有显著优势:文件无需上传至服务器,既降低了带宽消耗和服务器负载,又避免了文档内容在传输过程中泄露的风险,对数据隐私要求较高的业务尤其友好。

本文将基于 Spire.Doc for JavaScript 库,系统性地介绍如何在 React 中实现这一功能。我们会从环境搭建讲起,逐步深入到字体嵌入、权限加密、书签保留等进阶配置,并提供完整的可运行示例。

文章目录

    • [一、技术原理简述:WebAssembly 与虚拟文件系统](#一、技术原理简述:WebAssembly 与虚拟文件系统)
    • 二、项目初始化与环境配置
      • [1. 安装 Node.js 与创建 React 项目](#1. 安装 Node.js 与创建 React 项目)
      • [2. 安装依赖包](#2. 安装依赖包)
      • [3. 迁移核心运行时文件](#3. 迁移核心运行时文件)
      • [4. 准备字体资源(关键步骤)](#4. 准备字体资源(关键步骤))
    • [三、基础转换:Word 转 PDF](#三、基础转换:Word 转 PDF)
    • 四、进阶转换参数详解(`ToPdfParameterList`)
      • [1. 彻底解决字体问题:嵌入所有字体](#1. 彻底解决字体问题:嵌入所有字体)
      • [2. 保护敏感内容:PDF 加密与权限控制](#2. 保护敏感内容:PDF 加密与权限控制)
      • [3. 优化长篇文档体验:保留书签(大纲导航)](#3. 优化长篇文档体验:保留书签(大纲导航))
    • 五、总结

一、技术原理简述:WebAssembly 与虚拟文件系统

在开始编码前,有必要简单了解其底层机制。Spire.Doc for JavaScript 的核心是一个 WASM(WebAssembly) 模块。它将 C++ 编写的文档解析引擎编译为浏览器可执行的二进制格式,因此能够在浏览器中高效地处理 Word 文档。

值得注意的是,WASM 运行在一个独立的沙箱环境中,它无法直接读写您电脑硬盘上的文件。为此,该库实现了一个 虚拟文件系统(VFS,Virtual File System) 。在代码中看到的 FetchFileToVFSFS.readFile 等操作,都是围绕这个虚拟文件系统进行的:

  1. 写入 VFS :通过 FetchFileToVFSpublic 目录下的静态资源(字体文件、模板文档)加载到 WASM 内存中。
  2. 处理文档Document 对象从 VFS 中读取源文件,执行转换操作,并将结果输出到 VFS。
  3. 读取 VFS :转换完成后,通过 FS.readFile 从 VFS 中读取生成的 PDF 二进制数据,再通过浏览器 API 触发下载。

理解这一流程后,后续的代码阅读就会豁然开朗。


二、项目初始化与环境配置

1. 安装 Node.js 与创建 React 项目

首先,从 Node.js 官网 下载并安装 Node.js。安装完成后,在终端中运行以下命令验证安装是否成功:

bash 复制代码
node -v
npm -v

然后创建一个新的 React 项目(以 Create React App 为例):

bash 复制代码
npx create-react-app my-app
cd my-app

2. 安装依赖包

Spire.Office for JavaScript 整合了 Spire.DocSpire.XLSSpire.PDFSpire.Presentation 四个核心组件。在项目根目录执行以下命令安装:

bash 复制代码
npm i spire.office

3. 迁移核心运行时文件

安装完成后,需要将 lib 中的以下五个文件及文件夹复制到 React 项目的 public 文件夹中:

  • spire.doc.js
  • Spire.Doc.Wasm.zip
  • spire.common.js
  • Spire.Common.Wasm.zip
  • _framework 文件夹

为什么放在 public?因为 WASM 加载需要通过 JavaScript 主线程以异步方式请求 .wasm 文件及相关资源。放在 public 目录下可以确保构建工具(如 Webpack)不会错误地处理或重命名它们,且能通过 process.env.PUBLIC_URL 准确访问。

4. 准备字体资源(关键步骤)

浏览器和 WASM 环境默认不包含系统字体。如果待转换的 Word 文档中使用了特殊字体(如 Calibri、Times New Roman 或中文字体),而 VFS 中缺失该字体,转换出的 PDF 可能会出现乱码或文本错位。

建议将项目常用字体(如 CALIBRI.ttfMSYH.TTC 等)一并放入 public 目录。后续代码会通过 FetchFileToVFS 将这些字体预加载到 VFS 中。


三、基础转换:Word 转 PDF

以下是一个完整的转换示例,实现了从加载字体和 Word 文件,到生成 PDF 并触发浏览器下载的完整流程。

javascript 复制代码
import React, { useState, useEffect } from 'react';

function App() {
  const [wasmModule, setWasmModule] = useState(null);

  // 组件挂载时加载 WASM 模块
  useEffect(() => {
    const loadWasm = async () => {
      try {
        const publicUrl = process.env.PUBLIC_URL || '';
        // 动态导入 spire.doc.js(从 public 目录加载)
        const spireModule = await import(
          /* webpackIgnore: true */ `${publicUrl}/spire.doc.js`
        );
        const rawModule = spireModule.default || spireModule;
        // 初始化 WASM 模块,locateFile 用于指定 .wasm 文件位置
        const module = await rawModule({
          locateFile: (p) =>
            p.endsWith('.wasm') ? `${publicUrl}/${p}` : p,
        });
        // 模块初始化完成后暴露在 window.spire 下
        setWasmModule(window.spire);
      } catch (error) {
        console.error('Failed to load WASM module:', error);
      }
    };

    loadWasm();
  }, []);

  const convertWordToPdf = async () => {
    if (!wasmModule) {
      alert('文档处理引擎尚未加载完成,请稍候');
      return;
    }

    // 1. 加载字体到虚拟文件系统(VFS)
    await window.spire.FetchFileToVFS(
      'CALIBRI.ttf',
      '/Library/Fonts/',
      `${process.env.PUBLIC_URL}/`
    );

    // 2. 加载待转换的 Word 文件到 VFS
    const inputFileName = 'Roche.docx';
    await window.spire.FetchFileToVFS(
      inputFileName,
      '',
      `${process.env.PUBLIC_URL}/`
    );

    // 3. 创建文档对象并加载 Word 文件
    const doc = new wasmModule.Document();
    doc.LoadFromFile(inputFileName);

    // 4. 转换为 PDF
    const outputFileName = 'ToPDF.pdf';
    doc.SaveToFile({
      fileName: outputFileName,
      fileFormat: wasmModule.FileFormat.PDF,
    });

    // 5. 从 VFS 读取生成的 PDF 并触发下载
    const fileArray = wasmModule.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/pdf' });
    const url = URL.createObjectURL(blob);

    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);

    // 6. 释放资源
    doc.Dispose();
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Word 转 PDF 示例</h1>
      <button onClick={convertWordToPdf} disabled={!wasmModule}>
        转换
      </button>
    </div>
  );
}

export default App;

代码要点说明

  • 加载方式 :使用 import() 动态加载 spire.doc.js,配合 locateFile 指定 .wasm 文件位置。/* webpackIgnore: true */ 注释用于防止 Webpack 处理该动态导入路径,确保从 public 目录加载。
  • 模块引用 :初始化完成后,通过 window.spire 访问 API。
  • 文档创建 :使用 new wasmModule.Document() 创建文档实例。
  • FetchFileToVFS:将字体和 Word 文件加载到虚拟文件系统中。
  • FS.readFile:从 VFS 中读取生成的 PDF 文件。
  • 资源释放 :转换完成后调用 doc.Dispose() 释放内存。

四、进阶转换参数详解(ToPdfParameterList

基础转换仅能满足简单的需求。为了应对复杂的业务场景,Spire.Doc 提供了 ToPdfParameterList 参数类,允许我们对转换过程进行精细控制。

1. 彻底解决字体问题:嵌入所有字体

默认情况下,PDF 会引用系统字体。如果目标阅读设备缺少对应字体,显示会回退。将 IsEmbeddedAllFonts 设为 true 可以将文档中用到的所有字符轮廓嵌入 PDF,实现"所见即所得"。

javascript 复制代码
const convertWithFonts = async () => {
  if (!wasmModule) return;

  // 加载字体和文件到 VFS
  await window.spire.FetchFileToVFS('CALIBRI.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/`);
  await window.spire.FetchFileToVFS('Roche.docx', '', `${process.env.PUBLIC_URL}/`);

  const doc = new wasmModule.Document();
  doc.LoadFromFile('Roche.docx');

  // 创建转换参数对象
  let parameters = new wasmModule.ToPdfParameterList();

  // 嵌入所有字体
  parameters.IsEmbeddedAllFonts = true;

  // 针对某些特殊私有字体,还可以单独指定路径
  const privateFont = wasmModule.PrivateFontPath.Create('MyCustomFont', 'custom.ttf');
  parameters.PrivateFontPaths = [privateFont];

  const outputFileName = 'ToPDF_with_Fonts.pdf';
  doc.SaveToFile({ fileName: outputFileName, paramList: parameters });

  // ... 读取并下载逻辑同上
  doc.Dispose();
};

2. 保护敏感内容:PDF 加密与权限控制

对于合同、标书等保密文档,加密至关重要。PdfSecurity.Encrypt 方法接受四个核心参数:

  • 打开密码:用户打开 PDF 时必须输入的密码。
  • 权限密码:用于修改权限设置的管理员密码(与打开密码不同)。
  • 权限标志:控制是否允许打印、复制内容、修改文档等。
  • 加密强度 :一般选用 128 位密钥(Key128Bit)。
javascript 复制代码
const convertWithEncryption = async () => {
  // ... 加载字体和文件 ...

  let parameters = new wasmModule.ToPdfParameterList();

  // 加密 PDF
  parameters.PdfSecurity.Encrypt(
    'user123',                                    // 用户打开密码
    'admin456',                                   // 所有者权限密码
    wasmModule.PdfPermissionsFlags.Print | wasmModule.PdfPermissionsFlags.Copy, // 仅允许打印和复制
    wasmModule.PdfEncryptionKeySize.Key128Bit     // 128 位加密强度
  );

  const outputFileName = 'ToPDF_Encrypted.pdf';
  doc.SaveToFile({ fileName: outputFileName, paramList: parameters });

  // ... 下载及资源释放
};

3. 优化长篇文档体验:保留书签(大纲导航)

如果 Word 文档中已经通过标题样式(Heading 1/2/3)添加了书签,设置 CreateWordBookmarks = true 可以在生成的 PDF 左侧生成导航目录树,极大提升长文档的浏览效率。

javascript 复制代码
const convertWithBookmarks = async () => {
  // ... 加载字体和文件 ...

  let parameters = new wasmModule.ToPdfParameterList();

  // 从 Word 标题创建 PDF 书签
  parameters.CreateWordBookmarks = true;

  const outputFileName = 'ToPDF_with_Bookmarks.pdf';
  doc.SaveToFile({ fileName: outputFileName, paramList: parameters });

  // ... 下载及资源释放
};

五、总结

本文详细介绍了在 React 生态中利用 WebAssembly 技术实现客户端 Word 转 PDF 的完整路径。通过 Spire.Office for JavaScript,我们不仅绕开了后端文件处理的瓶颈,还利用虚拟文件系统和精细的参数配置,实现了字体嵌入、文档加密、书签保留等企业级特性。

------使用中遇到问题,可以在论坛上交流讨论-----------

相关推荐
HUMHSX2 小时前
Vue 项目启动全流程解析:从入口文件到全局指令注册与页面渲染
前端·javascript·vue.js
谢尔登3 小时前
【React】 状态管理方案
前端·react.js·前端框架
用户938515635074 小时前
手把手教你实现一个 MCP 文件读取服务器:从协议到代码的深度解析
javascript·人工智能
用户2136610035724 小时前
Vue商品详情与放大镜组件
前端·javascript
半个落月4 小时前
从Tapas小Demo理清localStorage、事件与this
前端·javascript
用户938515635074 小时前
RAG 实战:从零搭建语义搜索系统,彻底告别关键词匹配的尴尬
javascript·人工智能
李明卫杭州4 小时前
Vue2 中 v-model 处理不同数据结构的技巧
前端·javascript·vue.js
李明卫杭州4 小时前
使用 computed 处理 v-model 复杂数据结构
前端·javascript·vue.js
天才熊猫君7 小时前
配置与数据分离:一种可视化搭建的属性编辑方案
前端·javascript
林希_Rachel_傻希希8 小时前
web性能之相关路径——AI总结
前端·javascript·面试