HTML 是网页的通用描述方式,但它的下游场景往往不是浏览器:交付给传统办公系统时,RTF 的兼容性最好;内容进入文档站点或代码仓库时,Markdown 才是可维护的形式。两者对源文件的要求并不相同——RTF 需要保留字体、颜色与表格版式,Markdown 只保留标题层级、列表、表格、链接与图片等结构信息。
本文用 Spire.Doc for JavaScript 完成两个功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.Doc for JavaScript。下面的示例假定 Spire.Doc 已安装、WebAssembly 模块已初始化。
将 HTML 转换为 RTF
RTF 以纯文本标记描述字体、段落与表格,几乎被所有文字处理软件识别。将 HTML 转换为 RTF 后,内容可以在未安装 Office 组件的系统中继续编辑,也可以作为邮件正文的富文本来源。
本节使用的源文件是一份发布说明页 Report.html,其中包含深色标题栏、指标卡片、版本对比表、提示框与一张图表,用于覆盖常见的网页版式。转换后,标题栏底色、卡片底纹、表格边框与文字颜色都会写入 RTF。
具体操作步骤如下:
- 获取 Spire.Doc 的 WASM 模块,确认已就绪。
- 把中文字体、
Report.html与图表文件载入虚拟文件系统(VFS)。 - 以 HTML 格式加载文档,使用宽松校验。
- 使用
FileFormat.Rtf把文档保存到 VFS。 - 从 VFS 读出 RTF 字节并触发浏览器下载。
将 HTML 转换为 RTF 的代码如下:
import React from 'react';
function App() {
const htmlToRtf = async () => {
// 获取 Spire.Doc 的 WASM 模块
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 载入字体与源文件
await window.spire.FetchFileToVFS('MSYH.TTC', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
await window.spire.FetchFileToVFS('Report.html', '', `${process.env.PUBLIC_URL}/data/`);
await window.spire.FetchFileToVFS('report-chart.png', '', `${process.env.PUBLIC_URL}/data/`);
// 按 HTML 格式加载文档
const doc = new docModule.Document();
doc.LoadFromFile({
fileName: 'Report.html',
fileFormat: docModule.FileFormat.Html,
validationType: docModule.XHTMLValidationType.None,
});
doc.HtmlBaseUrl = '/';
// 以 RTF 格式保存
const outputFileName = 'Report.rtf';
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Rtf });
// 从 VFS 读取生成的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/rtf' });
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 转 RTF</h1>
<button onClick={htmlToRtf}>生成</button>
</div>
);
}
export default App;
转换后的 RTF 在 Word 中的显示效果如下,标题栏、卡片与表格的底色均得到保留:

哪些样式会保留,哪些会丢失
HTML 中的行内样式与样式表会映射为 RTF 的字符与段落属性,但并非全部 CSS 都有对应表达:
| 样式类别 | 转换结果 |
|---|---|
| 字体、字号、文字颜色 | 保留 |
| 加粗、斜体、下划线、删除线 | 保留 |
| 段落对齐、缩进、行距 | 保留 |
| 表格边框、单元格底色、列宽 | 保留 |
| 图片与超链接 | 保留,图片以数据形式嵌入 |
flex、grid、绝对定位、浮动 |
不保留,内容按文档流重新排列 |
依赖布局实现的多栏页面,转成 RTF 后通常变为单栏;页面中的装饰性元素(阴影、圆角、渐变)会退化为纯色或直接省略。
图片与文件体积
图片会以十六进制数据写入 RTF,文件体积随之增大;同一张图片还会附带一份元文件副本,用于兼容旧版阅读器。示例中约 53 KB 的 PNG 图表,使 RTF 的体积增加到约 2 MB。源文件中的相对路径按 HtmlBaseUrl 指定的目录解析;跨域且不可访问的图片会被跳过,输出中留下空位。图片较多时,可以先在 HTML 中压缩或裁切图片,再执行转换。
将 HTML 转换为 Markdown
Markdown 适合长期维护:纯文本可以 diff、可以审阅、可以托管在版本库中。将 HTML 转换为 Markdown,等于把内容从渲染结果还原为结构描述,标题层级、列表、表格、链接与图片是主要保留对象。
本节使用的源文件是一份升级指南页 Guide.html,由标题、段落、有序与无序列表、验证表格、流程图片和文档链接组成。与 RTF 示例不同,这份源文件不使用版式表格,因此转换后的 Markdown 结构更规整。
具体操作步骤如下:
- 获取 WASM 模块,把字体、
Guide.html与图片载入 VFS。 - 按 HTML 格式加载文档。
- 设置
MarkdownExportOptions.ImagesFolder,指定图片写到哪个目录。 - 使用
FileFormat.Markdown保存文档。 - 从 VFS 读出 Markdown 文本并触发下载。
将 HTML 转换为 Markdown 的代码如下:
import React from 'react';
function App() {
const htmlToMarkdown = async () => {
// 获取 Spire.Doc 的 WASM 模块
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 载入字体与源文件
await window.spire.FetchFileToVFS('MSYH.TTC', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
await window.spire.FetchFileToVFS('Guide.html', '', `${process.env.PUBLIC_URL}/data/`);
await window.spire.FetchFileToVFS('guide-flow.png', '', `${process.env.PUBLIC_URL}/data/`);
// 按 HTML 格式加载文档
const doc = new docModule.Document();
doc.LoadFromFile({
fileName: 'Guide.html',
fileFormat: docModule.FileFormat.Html,
validationType: docModule.XHTMLValidationType.None,
});
doc.HtmlBaseUrl = '/';
// 图片写入 VFS 的 images 目录,Markdown 中以相对路径引用
doc.MarkdownExportOptions.ImagesFolder = 'images';
// 以 Markdown 格式保存
const outputFileName = 'Guide.md';
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Markdown });
// 从 VFS 读取生成的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'text/markdown' });
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 转 Markdown</h1>
<button onClick={htmlToMarkdown}>生成</button>
</div>
);
}
export default App;
转换后的 Markdown 在支持表格的阅读器中的显示效果如下:

图片路径与编码方式
MarkdownExportOptions 提供两种图片处理方式:把图片写成独立文件,或者把图片编码进 Markdown 本身。两者的差别如下:
| 设置 | Markdown 中的引用 | 适用场景 |
|---|---|---|
| 不设置 | ,图片写到 VFS 根目录 |
导出后立即打包 |
ImagesFolder = 'images' |
,图片写入 VFS 的 images 目录 |
提交到代码仓库或文档站点 |
ImagesAsBase64 = true |
,不产生单独的图片文件 |
单文件分发、复制粘贴 |
采用目录方式时,图片按“输出文件名_序号.png”命名,并与 Markdown 一同留在 VFS 中,下载时需要把 Markdown 与 images 目录一起取出:
// 读取导出目录中的图片,与 Markdown 一起打包下载
const imageArray = window.dotnetRuntime.Module.FS.readFile('images/Guide_001.png');
ImagesAsBase64 的代价是文件体积增大约三分之一,图片较多时不宜使用。若选择目录方式而只下载了 Markdown 文件,阅读器中会出现图片缺失的提示。
哪些内容无法转成 Markdown
Markdown 的语法覆盖有限,HTML 中的部分语义在转换后只保留文字,不保留形式:
| HTML 元素 | Markdown 输出 |
|---|---|
标题 h1—h6 |
#—######,标题原有的加粗保留为 **文字** |
| 有序、无序列表 | 1. 与 - 开头的列表项 |
| 表格 | 管道表格,表头单元格加粗 |
引用 blockquote |
普通段落,不输出 > |
| 代码块、行内代码 | 普通文本,不输出围栏与反引号 |
| 颜色、背景色、对齐 | 不输出 |
iframe、脚本、复杂合并单元格 |
忽略或在转换中简化 |
正文型内容(文章、说明、帮助文档)的转换质量最好;交互型页面(仪表盘、表单)不适合作为转换对象。
两个方向的选择建议
| 目标 | 格式 | 理由 |
|---|---|---|
| 交付给办公软件继续编辑 | RTF | 保留字符与段落样式,兼容旧系统 |
| 进入文档站点或代码仓库 | Markdown | 纯文本、可 diff、可托管 |
两种转换都以同一个 Document 为中间模型,加载逻辑完全一致,差别只在保存格式与导出选项。批量处理时,可以在一次加载后先导出 RTF,再导出 Markdown,最后统一释放实例。
常见问题
转换 HTML 时提示找不到字体怎么办
原因:HTML 中的字体信息需要在加载时解析,VFS 中没有任何字体文件时,LoadFromFile 会抛出 Cannot find any fonts in specified font sources.。
解决:在执行加载之前,把项目中的字体文件复制到 VFS 的 /Library/Fonts/ 目录:
await window.spire.FetchFileToVFS('MSYH.TTC', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
字体文件需要自行准备并放在项目的 public/font/ 目录下,页面中未必都有同名文件。
导出的 Markdown 标题为什么带 ** 符号
原因:HTML 中的 h1—h6 默认以加粗显示,转换后的标题段落带有加粗字符格式,导出 Markdown 时该格式被写成 **文字**,于是得到 ## **标题**。
解决:这属于正常的格式保留。若希望标题不带加粗,可以在导出后批量去掉标题行中的 **,或者在源 HTML 中为标题设置 font-weight: normal 后再转换。
Markdown 中的图片文件在哪里
图片默认写到 VFS 根目录,名称由输出文件名与序号组成,例如 Guide_001.png。设置 ImagesFolder 后,图片会写入指定的子目录,Markdown 中的引用也随之改为相对路径。无论采用哪种方式,图片都需要从 VFS 中读出,与 Markdown 一起交付。
为什么导出的 RTF 文件比原 HTML 大很多
RTF 以十六进制文本保存图片,同一张图片占用的字节数约为原文件的两倍;此外,同一张图片还会附带一份元文件副本,用于兼容旧版阅读器。示例中约 53 KB 的图表,最终使 RTF 达到约 2 MB。图片越多、分辨率越高,体积增长越明显。
网页中的颜色和背景在 Markdown 中为什么没有保留
Markdown 是纯结构文本,本身没有描述颜色与底纹的语法。转换时,字体颜色、单元格底色与对齐方式都不会输出,只保留标题、列表、表格、链接与图片等结构。需要保留这些版式时,应改用 RTF 或 PDF 作为输出格式。
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。







