从编辑器导出 PDF

Available in Start planBetav0.6.2

使用 Tiptap 的 @tiptap-pro/extension-export-pdf 将编辑器内容导出为 .pdf 文件。此扩展通过将文档发送到 Tiptap 转换服务进行转换,把 PDF 导出功能集成到你的编辑器中。

如果你更希望在自己的端处理转换,也可以改用 REST API。

安装 PDF 导出扩展

Conversion 扩展发布在 Tiptap 的私有 npm 仓库中。请按照 私有仓库指南 集成这些扩展。

完成后,你就可以安装并导入 Export PDF 扩展包。

npm i @tiptap-pro/extension-export-pdf
import { ExportPdf } from '@tiptap-pro/extension-export-pdf'

配置扩展

ExportPdf 扩展可以使用 ExportPdfOptions(object)作为 configure 方法的参数进行配置,包含以下属性:

import { ExportPdf } from '@tiptap-pro/extension-export-pdf'

const editor = new Editor({
  extensions: [
    // 其他扩展 ...
    ExportPdf.configure({
      region: 'eu', // 'eu' or 'us'
      token: 'YOUR_TOKEN',
      styleOverrides: {},
      headers: {},
      footers: {},
      pageSize: { width: '21cm', height: '29.7cm' },
      pageMargins: { top: '1cm', bottom: '1cm', left: '1cm', right: '1cm' },
      customNodes: [],
      onCompleteExport: (result) => {
        // 处理导出的 Blob
      },
    }),
    // 其他扩展 ...
  ],
})
参数类型描述默认值
endpoint用于转换的 Tiptap API 端点。设置后会覆盖 regionundefined
region未设置 endpoint 时使用的 Tiptap Cloud 区域,可选 'eu' 或 'us''eu'
tokenstring用于身份验证的 Tiptap JWT,参见身份验证''
styleOverridesRecord<string, string>应用于导出的样式覆盖undefined
headersHeaderConfig导出文档的页眉配置undefined
footersFooterConfig导出文档的页脚配置undefined
pageSizePageSize页面大小配置undefined
pageMarginsPageMargins页面边距配置undefined
customNodesCustomNodeDefinition[]基于 DOCX 转换的自定义节点定义(需要 @tiptap-pro/extension-export-docx)undefined
customFontsCustomFont[]为 PDF 导出提供的自定义字体文件(仅限本地部署)undefined
onCompleteExport(result: Blob) => void接收导出文件 Blob 的回调未提供时抛出错误
onExportError(error: Error) => void导出流程被拒绝时调用的可选回调(动态导入失败、DOCX 转换失败、网络错误、非 OK 响应)。省略时,错误将通过 console.error 记录。undefined

选择您的区域

默认端点 api.tiptap.dev 位于欧盟区域。转换服务也在美国区域提供,端点为 api-us.tiptap.dev。编辑器扩展通过 region 选项('eu' 或 'us')选择区域。 REST 调用则直接使用对应的主机。

页眉和页脚插槽值

每个页眉和页脚插槽(default、first、even)都接受五种形态中的任意一种;请选择与你的数据相匹配的形式。前三种仅使用 @tiptap-pro/extension-export-pdf 即可;后两种还需要安装 @tiptap-pro/extension-export-docx(它们会走先 DOCX 后 PDF 的导出路径,并且采用懒加载,因此不会影响仅使用 PDF 的消费者)。

形态示例说明
纯文本'Company name, 2026'无样式的页眉/页脚。
字符串化的 Tiptap JSONContentJSON.stringify(myHeaderDoc)预先序列化的 Tiptap 节点。保留富文本格式。
Tiptap JSONContent 对象{ type: 'doc', content: [/* … */] }直接传入富文本 Tiptap 节点。无需字符串化。
docx Header / Footer 实例new Docx.Header({ children: [/* … */] })完整的 DOCX 级别控制。需要 DOCX 扩展。
返回 Header / Footer 的异步工厂() => convertHeader({ node })在导出时按需构建。典型用法:convertHeader / convertFooter。需要 DOCX 扩展。

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>)

HeaderConfig

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

FooterConfig

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

PageSize

属性类型描述默认值
widthstring页面宽度。必须是一个正数,后跟有效单位(cm、in、pt、pc、mm、px)。"21cm"
heightstring页面高度。必须是一个正数,后跟有效单位(cm、in、pt、pc、mm、px)。"29.7cm"

页面边距

属性类型描述默认值
topstring页面上边距。可以为负值。必须是一个数字,后跟有效单位(cm、in、pt、pc、mm、px)。"1cm"
bottomstring页面下边距。可以为负值。必须是一个数字,后跟有效单位(cm、in、pt、pc、mm、px)。"1cm"
leftstring页面左边距。必须是一个正数,后跟有效单位(cm、in、pt、pc、mm、px)。"1cm"
rightstring页面右边距。必须是一个正数,后跟有效单位(cm、in、pt、pc、mm、px)。"1cm"

自定义字体

仅限本地部署

自定义字体仅可用于本地部署。如果你有兴趣在 Tiptap 的云服务中使用自定义 字体,请 联系我们了解 Enterprise 方案。

属性类型描述
urlstring直接指向 .ttf 或 .woff2 字体文件的 HTTPS URL
fontFamilystring文档中使用的字体族名称

导出 PDF 文件

安装该扩展后,你可以使用 exportPdf 命令将编辑器内容导出为 .pdf。

/**
 * 通过 Tiptap 转换服务将当前文档导出为 PDF 文件。
 *
 * @param options - 所有扩展级选项都可以在单次调用时被覆盖
 * @example editor.commands.exportPdf({ onCompleteExport: (result) => {} })
 */
exportPdf: (options?: ExportPdfCommandOptions) => ReturnType

ExportPdfCommandOptions 接口继承自 ExportPdfOptions,因此每个配置项都可以在每次命令调用时被覆盖:

PropertyTypeDescription
endpoint为此次特定导出覆盖 API 端点
region为此次特定导出覆盖区域,可选 'eu' 或 'us'
tokenstring覆盖此特定导出的 JWT 令牌
styleOverridesRecord<string, string>覆盖此特定导出的样式覆盖项
headersHeaderConfig覆盖此特定导出的页眉配置
footersFooterConfig覆盖此特定导出的页脚配置
pageSizePageSize覆盖此特定导出的页面尺寸
pageMarginsPageMargins覆盖此特定导出的页面边距
customNodesCustomNodeDefinition[]覆盖此特定导出的自定义节点定义
customFontsCustomFont[]覆盖此特定导出的自定义字体(仅限本地部署)
onCompleteExport(result: Blob) => void覆盖此特定导出的回调
onExportError(error: Error) => void覆盖此特定导出的错误处理器
import { ExportPdf } from '@tiptap-pro/extension-export-pdf'

const editor = new Editor({
  extensions: [
    // 其他扩展 ...
    ExportPdf.configure({
      token: 'YOUR_TOKEN',
      onCompleteExport(result) {
        // 下载 PDF 文件
        const url = URL.createObjectURL(result)
        const a = document.createElement('a')

        a.href = url
        a.download = 'document.pdf'
        a.click()

        URL.revokeObjectURL(url)
      },
    }),
    // 其他扩展 ...
  ],
})

// 使用扩展级回调
editor.chain().exportPdf().run()

// 或为特定导出覆盖选项
editor
  .chain()
  .exportPdf({
    pageSize: { width: '8.5in', height: '11in' },
    onCompleteExport(result) {
      // 为此次特定导出进行自定义处理
      const url = URL.createObjectURL(result)
      window.open(url)
    },
  })
  .run()

工作原理

当你调用 exportPdf 时,扩展会将编辑器文档序列化为 JSON,并连同任何已配置的选项(样式覆盖、页面布局、页眉、页脚、自定义字体)一起发送到 Tiptap 转换服务。该服务会将文档转换为 PDF,并以二进制 blob 的形式返回。onCompleteExport 回调会直接接收这个 blob,你可以随后按需下载或处理它。错误会作为异常抛出,而不是通过回调传递。

自定义字体

仅限本地部署

自定义字体仅适用于本地部署。如果你有兴趣在 Tiptap 的云服务中使用自定义 字体,请联系我们了解 Enterprise 方案。

PDF 导出支持本地部署中的自定义字体。你可以通过提供一组字体 URL 来配置自定义字体,转换服务会下载这些字体并在生成 PDF 时使用它们。

字体文件必须是 .ttf 或 .woff2 格式,并且可通过 HTTPS 访问。fontFamily 必须与文档文本样式中使用的字体族名称一致。

ExportPdf.configure({
  token: 'YOUR_TOKEN',
  customFonts: [
    {
      url: 'https://your-cdn.com/fonts/CustomFont-Regular.ttf',
      fontFamily: 'Custom Font',
    },
    {
      url: 'https://your-cdn.com/fonts/CustomFont-Bold.ttf',
      fontFamily: 'Custom Font',
    },
  ],
  onCompleteExport(result) {
    // 处理导出的 PDF
  },
})

你也可以在命令调用时覆盖自定义字体:

editor
  .chain()
  .exportPdf({
    customFonts: [
      {
        url: 'https://your-cdn.com/fonts/SpecialFont.woff2',
        fontFamily: 'Special Font',
      },
    ],
  })
  .run()

预期结果

  • 需要身份验证。 导出会将文档发送到 Convert 服务,该服务需要携带 aud: "Convert" 的签名令牌(token)。请在你的服务器上签发令牌,切勿在浏览器中暴露签名密钥。参见 身份验证。
  • 你可能需要处理的 HTTP 响应: 401(缺少或无效的 JWT)、403(该操作不允许你的应用使用——最常见的是你的套餐不包含某种格式,或在云计划下使用 customFonts)、5xx(临时性的服务端错误;请使用退避重试)。
  • 自定义字体是企业版 / 本地部署功能。 在云计划中,携带 customFonts 的请求会返回 403。如需启用,请联系我们了解 企业版计划 或运行本地部署。
  • 每个请求都会在响应前端到端处理完毕。 非常大的文档可能需要更长时间才能返回;请相应地设置你这边的请求超时时间。

不要预期

  • 从 DOCX → PDF 的往返完全一致。 PDF 是固定布局格式;Word 和 Convert 服务使用不同的布局引擎,因此视觉输出可能会与 Word 的 PDF 打印在细微之处有所不同。
  • **复杂布局与 Word 像素级完全一致,**尤其是多栏分节、从右到左文本,或依赖 Word 特定布局怪癖的内容。
  • 在生成的 PDF 中进行编辑。 PDF 导出是单向的。若要保留可编辑性,也请导出为 DOCX。

支持与限制

功能支持
文本内容✓ 基本文本、间距、标点
文本格式✓ 加粗、斜体、下划线、删除线、对齐、行高
块级元素✓ 段落、标题(1–6)、引用块、有序列表和无序列表
表格✓ 基本结构、表头行、colspan
链接✓ 超链接
媒体(图片)✓ 嵌入图片,保留尺寸
样式✓ 字体族*、字体颜色、字体大小、背景颜色、行高
自定义字体✓ 仅限本地部署(.ttf 和 .woff2)
页眉和页脚✓
数学公式✓
分页符✓
分节✗
脚注和尾注✗
评论和修订✗
目录✗
高级格式✗ 栏、文字方向、表单、宏、嵌入式脚本
元数据✗
文本框、形状、SmartArt✗
  • 当使用 Google 字体(自动可用)或通过 customFonts 选项配置的自定义字体(仅限本地部署)时,支持字体族。

如需查看按功能细分的详细说明,请参阅 支持的功能 矩阵。请注意,DOCX 导出扩展 中可用的元素覆盖(paragraphOverrides、textRunOverrides、tableOverrides、tableCellOverrides、imageOverrides)不会传递到 PDF 转换中。