使用页眉和页脚扩展您的 PDF 导出
@tiptap-pro/extension-export-pdf 扩展内置支持自定义导出文档的页眉和页脚。您可以为首页、奇数页和偶数页配置不同的页眉和页脚。
页眉和页脚槽位值
每个页眉和页脚槽位(default、first、even)都接受以下形态之一。可在各槽位间自由混用和匹配;选择最符合您数据的那一种。
| 形态 | 示例 | 说明 |
|---|---|---|
| 纯文本 | 'Company name, 2026' | 渲染为无样式的页眉/页脚。 |
| 字符串化的 Tiptap JSONContent | JSON.stringify(myHeaderDoc) | 预先序列化的 Tiptap 节点。渲染时会保留富文本格式(粗体、斜体、链接等)。 |
| Tiptap JSONContent 对象 | { type: 'doc', content: [/* … */] } | 直接传入的 Tiptap 节点。与字符串化 JSONContent 的保真度相同,但无需执行 JSON.stringify。 |
docx Header / Footer | new Docx.Header({ children: [/* … */] }) | 对段落和运行项提供完整的 DOCX 级别控制。需要 @tiptap-pro/extension-export-docx。 |
返回 Header / Footer 的异步工厂 | () => convertHeader({ node }) | 在执行导出时按需构建。典型用法:使用 DOCX 扩展中的 convertHeader / convertFooter。 |
Tiptap JSONContent 对象必须包含一个 type 字段。该扩展在运行时使用它来区分 Tiptap 节点和 docx 实例。不包含 type 且又不是 Header / Footer 实例的对象会被丢弃,并通过 console.warn 提示有问题的值;它们绝不会被转发到 DOCX 序列化器。
在 TypeScript 中,这些会收敛为两个别名(type 要求被编码在 TiptapNodeContent 中):
import type { JSONContent } from '@tiptap/core'
import type { Footer, Header } from 'docx'
type TiptapNodeContent = JSONContent & { type: string }
type HeaderSlotValue = string | TiptapNodeContent | Header | (() => Promise<Header>)
type FooterSlotValue = string | TiptapNodeContent | Footer | (() => Promise<Footer>)前三种形态可直接与 @tiptap-pro/extension-export-pdf 一起使用。Header / Footer 实例以及异步工厂形态需要安装 @tiptap-pro/extension-export-docx;它们会走优先使用 DOCX 的导出路径,并按需加载,因此不会影响仅使用 PDF 的消费者。
页眉配置
headers 对象用于配置运行中的页眉。每个槽位都接受一个页眉槽位值。
| 属性 | 类型 | 说明 |
|---|---|---|
evenAndOddHeaders | boolean | 是否为奇数页和偶数页使用不同的页眉。 |
differentFirstPage | boolean | 是否在第一页使用不同的页眉。当为 true 时,第一页使用 first 值而不是 default。 |
default | HeaderSlotValue | 每一页的标准默认页眉;当 evenAndOddHeaders 启用时,则表示奇数页页眉。 |
first | HeaderSlotValue | 第一页的页眉。仅在 differentFirstPage 为 true 时使用。 |
even | HeaderSlotValue | 偶数页的页眉。仅在 evenAndOddHeaders 为 true 时使用。 |
页脚配置
footers 对象与 headers 相同。每个槽位都接受一个页脚槽位值。
| 属性 | 类型 | 说明 |
|---|---|---|
evenAndOddFooters | boolean | 是否为奇数页和偶数页使用不同的页脚。 |
differentFirstPage | boolean | 是否在第一页使用不同的页脚。当为 true 时,第一页使用 first 值而不是 default。 |
default | FooterSlotValue | 每一页的标准默认页脚;当 evenAndOddFooters 启用时,则表示奇数页页脚。 |
first | FooterSlotValue | 第一页的页脚。仅在 differentFirstPage 为 true 时使用。 |
even | FooterSlotValue | 偶数页的页脚。仅在 evenAndOddFooters 为 true 时使用。 |
完整示例
import { ExportPdf } from '@tiptap-pro/extension-export-pdf'
const editor = new Editor({
extensions: [
// 其他扩展...
ExportPdf.configure({
token: 'YOUR_TOKEN',
appId: 'YOUR_APP_ID',
headers: {
evenAndOddHeaders: true,
default: 'My Document - Confidential',
first: 'Welcome to My Document',
even: 'My Document - Even Page',
},
footers: {
evenAndOddFooters: true,
default: 'Company Name - All Rights Reserved',
first: 'Draft Version 1.0',
even: 'Company Name - Even Page Footer',
},
}),
// 其他扩展...
],
})
// 使用页眉和页脚导出
editor
.chain()
.exportPdf({
onCompleteExport(result) {
const url = URL.createObjectURL(result)
const a = document.createElement('a')
a.href = url
a.download = 'document-with-headers.pdf'
a.click()
URL.revokeObjectURL(url)
},
})
.run()简单的页眉和页脚
如果不需要为奇数页和偶数页使用不同的页眉,可以只提供 default 值:
ExportPdf.configure({
token: 'YOUR_TOKEN',
appId: 'YOUR_APP_ID',
headers: {
default: 'My Document Title',
},
footers: {
default: 'Page Footer - Company Name',
},
})通过 Tiptap JSONContent 提供富文本页眉
直接在任意槽位上传入一个 Tiptap JSONContent 节点。如果您已经在内存中拥有该节点,并且不想手动构建一个 Docx.Header,这会很有用:
ExportPdf.configure({
token: 'YOUR_TOKEN',
appId: 'YOUR_APP_ID',
headers: {
default: {
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{ type: 'text', marks: [{ type: 'bold' }], text: 'My Document' },
{ type: 'text', text: ' · Confidential' },
],
},
],
},
},
})Docx Header / Footer 实例
如果您需要对段落属性、对齐方式和运行样式进行完全控制,可以传入一个 docx Header / Footer 实例(或返回其实例的异步工厂)。这要求在安装 PDF 扩展的同时也安装 @tiptap-pro/extension-export-docx:
import { Docx, convertHeader } from '@tiptap-pro/extension-export-docx'
ExportPdf.configure({
token: 'YOUR_TOKEN',
appId: 'YOUR_APP_ID',
headers: {
// 直接的 Docx.Header 实例
default: new Docx.Header({
children: [
new Docx.Paragraph({
children: [new Docx.TextRun({ text: 'My Document' })],
}),
],
}),
// 异步工厂:按需将 Tiptap 节点转换为 Docx.Header
first: () =>
convertHeader({
node: {
type: 'doc',
content: [
{ type: 'paragraph', content: [{ type: 'text', text: 'Welcome' }] },
],
},
}),
},
})