以 HTML 保存的合同、报表与帮助文档,在归档、分发和检索三个环节往往需要换成不同的格式:对外发送要求 PDF,列表预览只需要一张缩略图,全文检索只关心纯文字。若每次都在服务端排队转换,既增加部署成本,也意味着文件内容要先离开用户的设备。
本文用 Spire.Doc for JavaScript 在浏览器中完成上述三种输出:把 HTML 转换为 PDF、把 HTML 转换为图片、把 HTML 转换为文本。三种输出共用同一套加载流程,区别只在于导出方式。示例使用同一份《星澜科技 2026 年第三季度运营报告》的 HTML 文件 Sample.html:该文件以 CSS 排版,包含背景色信息带、统计卡片、有序列表与无序列表、多张数据表格、一张内嵌的柱状图,并为经营指标、风险与应对两个章节设置了强制分页,转换后得到三页 A4 文档。
本文介绍三个功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.Doc for JavaScript。下面的示例假定 Spire.Doc 已安装、WebAssembly 模块已初始化。
将 HTML 转换为 PDF
将 HTML 转换为 PDF,由 Document.LoadFromFile() 与 SaveToFile() 两步完成:前者按 HTML 规则解析页面结构,后者把解析得到的文档按 PDF 输出。页面布局在转换后固定下来,不随阅读环境变化,适合归档与对外分发。
LoadFromFile() 以对象形式接收参数:
| 参数 | 取值 | 说明 |
|---|---|---|
fileName |
'Sample.html' |
虚拟文件系统(VFS)中的文件路径 |
fileFormat |
FileFormat.Html |
按 HTML 解析,不依赖文件扩展名 |
validationType |
XHTMLValidationType.None |
宽松校验,容忍不规范的标签与属性 |
XHTMLValidationType 另有 Strict 与 Transitional 两种取值。业务系统导出的页面通常达不到严格标准,选择 None 可以减少解析中断。
具体操作步骤如下:
- 获取 Spire.Doc 的 WASM 模块,确认已就绪。
- 把中文字体与
Sample.html载入 VFS。 - 创建
Document实例,按 HTML 格式加载文件。 - 以
FileFormat.PDF保存到 VFS。 - 从 VFS 读出 PDF 字节,生成下载链接。
将 HTML 转换为 PDF 的代码如下:
function App() {
const htmlToPdf = async () => {
// 获取 Spire.Doc 的 WASM 模块
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 载入中文字体与待转换的 HTML 文件
await window.spire.FetchFileToVFS('MSYH.TTC', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
await window.spire.FetchFileToVFS('Sample.html', '', `${process.env.PUBLIC_URL}/data/`);
const doc = new docModule.Document();
// 按 HTML 格式加载,使用宽松校验
doc.LoadFromFile({
fileName: 'Sample.html',
fileFormat: docModule.FileFormat.Html,
validationType: docModule.XHTMLValidationType.None,
});
// 相对路径资源以 VFS 根目录为基准
doc.HtmlBaseUrl = '/';
const outputFileName = 'HtmlToPdf.pdf';
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.PDF });
// 从 VFS 读取生成的文件并触发下载
const fileArray = window.dotnetRuntime.Module.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;
a.click();
URL.revokeObjectURL(url);
doc.Dispose();
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>HTML 转 PDF</h1>
<button onClick={htmlToPdf}>生成</button>
</div>
);
}
export default App;
示例 HTML 转换后得到三页 A4 文档,结果如下:

第一页为封面信息与季度概览,第二页为经营指标与收入构成,第三页为风险与应对、下一阶段计划及附录。标题层级、段落间距、有序列表的编号、表头的深蓝底色与表格的斑马纹都按 CSS 保留下来;内嵌的 chart.png 按 HTML 中设定的宽度嵌入,page-break-before: always 同样如实生效,因此第四、第七两个章节分别从第二页与第三页的顶部开始。
怎样控制 PDF 的分页位置
默认情况下,分页由内容高度自动决定,正文排满一页后在下一个自然位置断开。若需要按章节强制另起一页,可以在样式表中对相应元素设置 page-break-before: always,HTML 中的该属性在转换后仍然有效:
<!-- 该标题及其后的内容从新的一页开始 -->
<h2 style="page-break-before: always;">四、经营指标</h2>
示例对「四、经营指标」与「七、风险与应对」两个标题设置了该属性,转换结果中这两节各自从页面顶部开始,分页位置与在浏览器中打印时的预期一致。
将 HTML 转换为图片
将 HTML 转换为图片,使用 Document.SaveImageToStreams()。缩略图、消息卡片与文档预览只需要一张图片,该方法把指定页面渲染为图片对象,再通过该对象的 Save() 方法写入 VFS。参数与相关成员如下:
| 参数或成员 | 取值 | 说明 |
|---|---|---|
pageIndex |
从 0 开始的页序号 |
0 表示第一页 |
type |
ImageType.Bitmap |
位图;改为 ImageType.Metafile 则输出矢量图元文件 |
Save(path) |
VFS 中的文件路径 | 按扩展名写出文件,示例写 HtmlToImage.png |
具体操作步骤如下:
- 获取 Spire.Doc 的 WASM 模块,确认已就绪。
- 把中文字体与
Sample.html载入 VFS。 - 创建
Document实例,按 HTML 格式加载文件。 - 调用
SaveImageToStreams()渲染第一页,通过Save()写入 VFS。 - 从 VFS 读出 PNG 字节,生成下载链接。
将 HTML 转换为图片的代码如下:
function App() {
const htmlToImage = async () => {
// 获取 Spire.Doc 的 WASM 模块
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 载入中文字体与待转换的 HTML 文件
await window.spire.FetchFileToVFS('MSYH.TTC', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
await window.spire.FetchFileToVFS('Sample.html', '', `${process.env.PUBLIC_URL}/data/`);
const doc = new docModule.Document();
doc.LoadFromFile({
fileName: 'Sample.html',
fileFormat: docModule.FileFormat.Html,
validationType: docModule.XHTMLValidationType.None,
});
doc.HtmlBaseUrl = '/';
const outputFileName = 'HtmlToImage.png';
// pageIndex 从 0 开始,渲染第一页
const image = doc.SaveImageToStreams({
pageIndex: 0,
type: docModule.ImageType.Bitmap,
});
image.Save(outputFileName);
doc.Dispose();
// 从 VFS 读取图片并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'image/png' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>HTML 转图片</h1>
<button onClick={htmlToImage}>生成</button>
</div>
);
}
export default App;
示例 HTML 的第一页渲染结果如下:

输出的 PNG 为 794 × 1123 像素,对应 A4 页面在 96 dpi 下的尺寸;内容与 PDF 第一页保持一致,信息带、统计卡片、列表与时间线表格的相对位置都没有变化。图片的宽高比取决于页面设置,而非浏览器的窗口宽度。
如何批量导出全部页面
HTML 较长时会被分成多页,Document.PageCount 给出总页数,按页循环即可导出全部页面:
// 与 PDF 页数一致,示例文档为 3 页
const pageCount = doc.PageCount;
for (let i = 0; i < pageCount; i++) {
const pageImage = doc.SaveImageToStreams({
pageIndex: i,
type: docModule.ImageType.Bitmap,
});
pageImage.Save(`HtmlPage-${i + 1}.png`);
}
// 循环结束后统一释放文档实例
doc.Dispose();
示例文档共三页,循环得到 HtmlPage-1.png 至 HtmlPage-3.png 三个文件,PageCount 与转换出的 PDF 页数一致。只渲染一次文档、连续导出多页,比逐页重新加载更快;文档实例在循环结束后统一释放即可,不必每页创建一次。
将 HTML 转换为文本
将 HTML 转换为文本,以 FileFormat.Txt 保存即可。全文检索、内容审核与语料入库只关心文字,导出时 Spire.Doc 按文档顺序抽取文字,样式、表格边框、图片与超链接地址都不保留,导出的内容适于作为纯文本语料的来源。导出规则如下:
| 内容 | 导出结果 |
|---|---|
| 标题与正文 | 按段落顺序逐行输出,只保留文字,不带字号与样式 |
| 有序列表 | 行首保留 1. 加两个空格的编号前缀,序号按实际顺序递增 |
| 无序列表 | 行首保留 * 加一个空格作为项目符号 |
| 表格 | 每个单元格单独成行,按行优先顺序展开,单元格之间没有分隔符 |
| 图片 | 不导出,原位置留空 |
| 分页 | 不分页,全部内容写入同一个文件 |
| 编码 | UTF-8,文件开头带 BOM |
具体操作步骤如下:
- 获取 Spire.Doc 的 WASM 模块,确认已就绪。
- 把中文字体与
Sample.html载入 VFS,保持与前两个示例一致的加载流程。 - 创建
Document实例,按 HTML 格式加载文件。 - 以
FileFormat.Txt保存到 VFS。 - 从 VFS 读出文本文件,生成下载链接。
将 HTML 转换为文本的代码如下:
function App() {
const htmlToText = async () => {
// 获取 Spire.Doc 的 WASM 模块
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 载入中文字体与待转换的 HTML 文件
await window.spire.FetchFileToVFS('MSYH.TTC', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
await window.spire.FetchFileToVFS('Sample.html', '', `${process.env.PUBLIC_URL}/data/`);
const doc = new docModule.Document();
doc.LoadFromFile({
fileName: 'Sample.html',
fileFormat: docModule.FileFormat.Html,
validationType: docModule.XHTMLValidationType.None,
});
const outputFileName = 'HtmlToText.txt';
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Txt });
doc.Dispose();
// 从 VFS 读取文本并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>HTML 转文本</h1>
<button onClick={htmlToText}>生成</button>
</div>
);
}
export default App;
三页内容全部写入同一个文本文件,开头部分如下:

信息带中的英文标题、公司名称与副标题各占一行;「一、季度概览」等标题只保留文字,不带任何标记;有序列表的每一行以 1.、2. 这样的序号加两个空格开头;统计卡片在导出后表现为「1.86 亿元」与「季度营业收入」两行;时间线表格的每个单元格各自成行。图片 chart.png 在文本中没有对应内容,图注「图 1 2026 年第三季度各业务线收入构成」则作为正文保留。
三种输出如何选择
| 输出格式 | 适用场景 | 主要代价 |
|---|---|---|
| 归档、对外发送、打印 | 需要嵌入字体,文件体积较大 | |
| 图片 | 缩略图、预览、消息卡片 | 不可检索,清晰度受渲染分辨率限制 |
| 文本 | 全文检索、语料、内容审核 | 丢失全部排版信息 |
三种转换都只在浏览器内完成,文件不会离开用户的设备。需要批量处理时,把上述函数放进同一个循环中,按顺序加载、导出并释放实例即可。
常见问题
转换过程需要把文件上传到服务器吗
不需要。Spire.Doc for JavaScript 基于 WebAssembly 在浏览器端运行,HTML 文件通过虚拟文件系统(VFS)在本地读写,加载、转换、保存都在本地完成,库本身不会把文件发往服务器。文件最终是否会离开设备,取决于你在页面上另外接入的代码。
转换结果中的中文为什么显示为方框
原因:PDF 与图片在渲染文字时需要从 VFS 中读取字体。VFS 中一个字体都没有时,保存会直接抛出 Cannot find any fonts in specified font sources.;只载入了西文字体时,转换仍会完成,但中文字符因缺少字形而显示为方框,西文与数字则正常显示。
解决:把中文字体文件与 HTML 一同载入 VFS,并把字体放到 /Library/Fonts/ 目录:
// 中文字体写入 VFS 的字体目录
await window.spire.FetchFileToVFS('MSYH.TTC', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
FetchFileToVFS 在请求失败(例如返回 404)时不会中断执行,错误页面同样会被写入 VFS,后续才会以字体异常的形式暴露出来。载入前应确认 public/font 下的文件真实存在。
HTML 中引用的图片为什么没有出现在结果里
原因:LoadFromFile 只解析 HTML 文件本身,引用的图片、样式表须另行载入 VFS。图片缺失时转换不会报错,图片位置会渲染为一个占位图标,其后的内容随之移动;示例中缺少 chart.png 时,文档由三页变为四页。
解决:把 HTML 引用的资源一并载入 VFS。相对路径按加载文件所在的 VFS 目录解析,因此把 Sample.html 与 chart.png 放在同一目录即可;资源位于其他目录时,用 HtmlBaseUrl 指定基准目录:
// 图片与 HTML 放在同一目录,一起载入 VFS
await window.spire.FetchFileToVFS('Sample.html', '', `${process.env.PUBLIC_URL}/data/`);
await window.spire.FetchFileToVFS('chart.png', '', `${process.env.PUBLIC_URL}/data/`);
导出的文本中,表格为什么一行只有一个单元格
原因:文本导出只按顺序写出文字,不保留表格的行列结构。示例中的「四、经营指标」表格在文本里表现为「指标」「本季度」「上季度」「环比变化」四行接着「营业收入(万元)」「18,600」「16,240」「+14.5%」四行,单元格之间既没有制表符也没有竖线,因此无法直接看出哪几个值属于同一行。
解决:需要保留行列关系时,按表格的固定列数分组解析,或改用 PDF、图片保留版式:
// 表格固定四列,先截取表格所在的起止行号,再按每四行一组还原
const cells = text.split('\n').slice(tableStart, tableEnd);
const rows = [];
for (let i = 0; i + 4 <= cells.length; i += 4) {
rows.push(cells.slice(i, i + 4));
}
若表格列数随内容变化,建议以 PDF 作为版式基准,文本仅用于检索。
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。







