如何在 TypeScript 中处理 PDF 结构
本指南展示了如何使用 Aspose.PDF FOSS 为 TypeScript 构建 PDF 文件的文档导航和逻辑结构:页面上的可点击目录、文档级书签大纲、命名目标以及用于无障碍的标记结构树。它要求 Node.js 22 或更高版本。
分步指南
步骤 1:安装包
git clone https://github.com/aspose-pdf-foss/Aspose.PDF-FOSS-for-TypeScript.git
cd Aspose.PDF-FOSS-for-TypeScript
npm install
npm run build通过在新的 TypeScript 文件中导入 Document 类来验证安装——包安装完成后,此行应能解析且不报错:
import { Document } from '@asposefoss/pdf';步骤 2:导入所需类
导入 Document 打开文件,并使用 OutlineItem 来处理第 4 步中构建的书签条目:
import { Document, OutlineItem } from '@asposefoss/pdf';步骤 3:添加目录
Page.AddTOC() 接受一个 { title, page } 条目的数组、一个边界 rect,以及样式选项;它绘制点状引导线并生成可点击链接指向目标页面:
const page = doc.Pages[0];
page.AddTOC(
[
{ title: '1. Introduction', page: 2 },
{ title: '2. Methodology', page: 5 },
{ title: '3. Results', page: 9 },
],
[72, 160, 400, 600],
{ font: 'Helvetica', fontSize: 13, color: [0.1, 0.1, 0.15], rowGap: 18 },
);步骤 4:添加书签
Document.SetOutlines() 用一个 OutlineItem 对象数组替换文档的书签面板。每个项可以携带一个 Dest(页面目标)、样式(Color、Bold)以及用于可折叠树结构的嵌套 Children:
const items: OutlineItem[] = [
{ Title: 'Sales', Dest: { name: 'section.sales' }, Open: true, Color: [0.1, 0.4, 0.1] },
{ Title: 'Appendix', Dest: { name: 'section.appendix' } },
];
doc.SetOutlines(items);步骤 5:读取大纲和命名目标
Document.GetOutlines() 返回当前的书签树。Document.GetNamedDestinations() 返回文档中注册的所有命名目标——这对于验证每个大纲目标和目录链接是否真的指向真实页面非常有用:
const destNames = new Set(doc.GetNamedDestinations().map((d) => d.name));
const walk = (nodes: OutlineItem[]): void => {
for (const item of nodes) {
if (item.Dest && 'name' in item.Dest) {
console.log(destNames.has(item.Dest.name), item.Title);
}
if (item.Children) walk(item.Children);
}
};
walk(doc.GetOutlines());步骤 6:构建标记结构树
Document.GetStructTree() 返回现有的结构树(如果文档尚未标记,则返回 null)。StructTreeRoot.Append(tag) 添加子元素,例如 'H2' 或 'P',并返回一个 StructElement,StructElement.MarkContent(page, quad) 将该元素与其描述的页面内容关联:
function tagPage(doc: Document, page: Page, headingText: string): void {
const root = doc.GetStructTree();
if (!root) throw new Error('tagPage: the document is not tagged yet');
for (const block of page.GetStructuredText()) {
const text = block.text.trim();
if (text.length === 0) continue;
const el = root.Append(text.startsWith(headingText) ? 'H2' : 'P');
el.MarkContent(page, block.quad);
}
}常见问题与解决方案
Document.GetStructTree() returns null. 文档尚未标记 — 调用 Document.CreateStructTree() (或 Document.AutoTag()) 在手工编写元素之前使用 StructTreeRoot.Append() / StructElement.MarkContent().
大纲项目未跳转到正确的页面。 OutlineItem.Dest 必须命名一个实际存在的目标 — 交叉检查 item.Dest.name against Document.GetNamedDestinations() 在保存之前,如步骤5所示。
Page.AddTOC() 条目指向了错误的页面。 page 每个 TOC 条目中的页面编号是目标页面自身的页码 — 请与之核对 page.Number 在实际目标上 Page, 而不是假设的数组索引。
书签在稍后会消失 Document.SetOutlines() 调用。 SetOutlines() 替换整个书签树 — 使用以下方式读取当前树 Document.GetOutlines() 首先,修改数组;如果是添加书签而不是替换,则将整个数组传回。
常见问题
书签可以默认展开吗?
是——在 OutlineItem 上设置 Open: true,即可使该节点的子项在书签面板中默认可见。
书签和命名目标之间有什么区别?
书签(OutlineItem)是导航面板中的可见条目;命名目标(Document.GetNamedDestinations() / SetNamedDestination())是一个内部的、可重复使用的页面目标,书签、目录条目或链接注释都可以通过名称指向它。
为文档添加标签会影响其外观吗?
否——结构树(StructTreeRoot,StructElement)是描述阅读顺序和语义的平行逻辑层;它不会改变页面的可见内容。
我可以嵌套书签吗?
是——在 OutlineItem 上设置 Children 为进一步的 OutlineItem 对象数组,以构建可折叠的多层树。