排版规范的文档,价值不只在版面本身——段落挂了哪个样式,本身就是一份结构化信息。标题段落用的是 Heading 1 还是 Heading 2,正文用的是什么样式,都能反映文档的层次。反过来,如果要把文档里的标题抽出来生成目录或摘要,最可靠的做法也是「按样式名取」,而不是去猜哪一行字比较大。
Spire.Doc for JavaScript 中每个段落都带一个 StyleName 属性,读取它即可拿到该段落当前使用的样式名。本文的两个示例输出的都是 TXT 文本文件。
本文介绍两个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.Doc for JavaScript 的方法。以下示例默认已安装 Spire.Doc 并完成 WebAssembly 模块初始化。
导出文档的样式名清单
文档的段落按「节 → 段落」两级组织:doc.Sections 是节集合,每个 section 的 Paragraphs 是段落集合。两层都用 Count + get_Item(index) 遍历,逐段读 StyleName 并拼接起来即可。
function App() {
const RetrieveStyle = async () => {
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将示例文件载入虚拟文件系统(VFS)
let inputFileName = "RetrieveStyle.docx";
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}static/data/`);
// 加载文档
let doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 遍历全部节 → 段落,读取每段的 StyleName
let styleName = "";
for (let i = 0; i < doc.Sections.Count; i++) {
let section = doc.Sections.get_Item(i);
for (let j = 0; j < section.Paragraphs.Count; j++) {
let paragraph = section.Paragraphs.get_Item(j);
styleName += paragraph.StyleName + "\r\n";
}
}
// 定义输出文件名
const outputFileName = "RetrieveStyle-result.txt";
// 将内容写入 TXT 文件(直接写虚拟文件系统,不经过 SaveToFile)
window.dotnetRuntime.Module.FS.writeFile(outputFileName, styleName);
doc.Close();
// 读取保存的文件并转换为 Blob 对象
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([modifiedFileArray], { type: "text/plain" });
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>读取文档中每个段落的样式名</h1>
<button onClick={RetrieveStyle}>开始</button>
</div>
);
}
export default App;
示例文档共 14 个段落,导出的 TXT 按段落顺序逐行列出样式名:
Title
Heading1
Normal
Heading2
Normal
Heading1
Normal
Heading2
Normal
Heading1
Normal
ListBullet
ListBullet
ListBullet
可以看到内置样式的名称是去掉空格的形式(Heading1 而非 Heading 1,ListBullet 而非 List Bullet)——这一点在按样式名做判断时很关键。

按样式名提取段落文本
沿用同样的两层遍历,只需要在读到每一段时多一次判断:StyleName 等于目标样式名才把 paragraph.Text 收进结果。示例文档里共有 3 个 Heading1 段落,其余是标题样式、二级标题与正文,用于验证筛选确实只挑中了目标样式。
function App() {
const GetTextByStyleName = async () => {
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将示例文件载入虚拟文件系统(VFS)
let inputFileName = "GetTextByStyleName.docx";
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}static/data/`);
// 加载文档
let doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 收集符合条件的段落文本
let builder = [];
// 遍历全部节 → 段落
for (let i = 0; i < doc.Sections.Count; i++) {
let section = doc.Sections.get_Item(i);
for (let j = 0; j < section.Paragraphs.Count; j++) {
let para = section.Paragraphs.get_Item(j);
// 只收样式名为 Heading1 的段落
if (para.StyleName == "Heading1") {
builder.push(para.Text);
}
}
}
// 定义输出文件名
const outputFileName = "GetTextByStyleName-result.txt";
// 将内容写入 TXT 文件
window.dotnetRuntime.Module.FS.writeFile(outputFileName, builder.join("\n"));
doc.Close();
// 读取保存的文件并转换为 Blob 对象
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([modifiedFileArray], { type: "text/plain" });
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>按样式名提取段落文本</h1>
<button onClick={GetTextByStyleName}>开始</button>
</div>
);
}
export default App;
导出的 TXT 中只有 3 行,正是文档里全部 Heading1 段落:
一、上周事项回顾
二、本周重点
三、待确认事项

常见问题
按样式名筛不出任何段落
原因:内置样式的 StyleName 是不含空格的形式,写 "Heading 1" 会一条都匹配不到。
解决:改用不带空格的名称。示例中 "Heading1" 能命中 3 段,而 "Heading 1" 命中 0 段:
// 正确
if (para.StyleName == "Heading1") { ... }
// 匹配不到
if (para.StyleName == "Heading 1") { ... }
名称是区分大小写的,建议先跑一遍「导出样式名清单」确认文档里的实际名称再写判断条件。
导出的 TXT 每行之间多了一个空行
原因:拼接时同时用了 push(文本 + "\n") 与 join("\n"),每个元素已经自带换行符,join 又补了一次,于是每两条之间多出一个空行。
解决:两者只保留一个——要么元素不带换行符、由 join 统一补,要么元素自带换行符、直接用 join("") 拼接:
// 由 join 统一补换行
builder.push(para.Text);
window.dotnetRuntime.Module.FS.writeFile(outputFileName, builder.join("\n"));
下载得到的文件打开是乱码
原因:Blob 的 MIME 类型写成了 docx,或读取时把文本当成了二进制文档,用 Word 打开自然显示异常。
解决:TXT 输出要声明 text/plain,并用记事本等文本编辑器打开:
const modifiedFile = new Blob([modifiedFileArray], { type: "text/plain" });
遍历 Sections 与 Paragraphs 时漏掉了表格里的段落
原因:section.Paragraphs 只包含正文段落,表格单元格里的段落不在其中。表格内容挂在 section.Tables 下,需要另行遍历每个单元格的 Paragraphs。
解决:若文档含表格且需要一并处理,在正文循环之外单独遍历表格:
for (let t = 0; t < section.Tables.Count; t++) {
let table = section.Tables.get_Item(t);
for (let r = 0; r < table.Rows.Count; r++) {
for (let c = 0; c < table.Rows.get_Item(r).Cells.Count; c++) {
let cell = table.Rows.get_Item(r).Cells.get_Item(c);
// cell.Paragraphs 里是单元格内的段落
}
}
}
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。









