使用页眉和页脚扩展您的 PDF 导出

Available in Start planBetav0.3.0

@tiptap-pro/extension-export-pdf 扩展内置支持自定义导出文档的页眉和页脚。您可以为首页、奇数页和偶数页配置不同的页眉和页脚。

页眉和页脚槽位值

每个页眉和页脚槽位(defaultfirsteven)都接受以下形态之一。可在各槽位间自由混用和匹配;选择最符合您数据的那一种。

形态示例说明
纯文本'Company name, 2026'渲染为无样式的页眉/页脚。
字符串化的 Tiptap JSONContentJSON.stringify(myHeaderDoc)预先序列化的 Tiptap 节点。渲染时会保留富文本格式(粗体、斜体、链接等)。
Tiptap JSONContent 对象{ type: 'doc', content: [/* … */] }直接传入的 Tiptap 节点。与字符串化 JSONContent 的保真度相同,但无需执行 JSON.stringify
docx Header / Footernew 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 对象用于配置运行中的页眉。每个槽位都接受一个页眉槽位值

属性类型说明
evenAndOddHeadersboolean是否为奇数页和偶数页使用不同的页眉。
differentFirstPageboolean是否在第一页使用不同的页眉。当为 true 时,第一页使用 first 值而不是 default
defaultHeaderSlotValue每一页的标准默认页眉;当 evenAndOddHeaders 启用时,则表示奇数页页眉。
firstHeaderSlotValue第一页的页眉。仅在 differentFirstPagetrue 时使用。
evenHeaderSlotValue偶数页的页眉。仅在 evenAndOddHeaderstrue 时使用。

页脚配置

footers 对象与 headers 相同。每个槽位都接受一个页脚槽位值

属性类型说明
evenAndOddFootersboolean是否为奇数页和偶数页使用不同的页脚。
differentFirstPageboolean是否在第一页使用不同的页脚。当为 true 时,第一页使用 first 值而不是 default
defaultFooterSlotValue每一页的标准默认页脚;当 evenAndOddFooters 启用时,则表示奇数页页脚。
firstFooterSlotValue第一页的页脚。仅在 differentFirstPagetrue 时使用。
evenFooterSlotValue偶数页的页脚。仅在 evenAndOddFooterstrue 时使用。

完整示例

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 实例(或返回其实例的异步工厂)。这要求在安装 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' }] },
          ],
        },
      }),
  },
})