从编辑器导出 PDF

Available in Start planBetav0.3.0

使用 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 扩展可以使用 ExportPdfOptionsobject)作为 configure 方法的参数进行配置,包含以下属性:

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

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

页眉和页脚插槽值

每个页眉和页脚插槽(defaultfirsteven)都接受五种形态中的任意一种;请选择与你的数据相匹配的形式。前三种仅使用 @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第一页的页眉。仅在 differentFirstPagetrue 时使用。
evenHeaderSlotValue偶数页的页眉。仅在 evenAndOddHeaderstrue 时使用。

FooterConfig

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

PageSize

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

PageMargins

属性类型描述默认值
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"

CustomFont

仅限本地部署

自定义字体仅可用于本地部署。如果你有兴趣在 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,因此每个配置项都可以在每次命令调用时被覆盖:

属性类型描述
endpointstring为此次导出覆盖 API 端点
tokenstring为此次导出覆盖 JWT token
appIdstring为此次导出覆盖 App ID
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',
      appId: 'YOUR_APP_ID',
      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',
  appId: 'YOUR_APP_ID',
  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 服务,该服务需要 JWT(token)和你的 App ID。请在服务端生成 JWT;切勿在浏览器中嵌入你的密钥。参见 转换安装指南
  • 你可能需要处理的 HTTP 响应: 401(缺少或无效的 JWT)、403(该操作不允许你的应用使用——通常是你的套餐不包含的格式,或在云套餐中使用 customFonts)、5xx(临时性服务端错误;请采用退避重试)。
  • 自定义字体是 Enterprise / 本地部署功能。 在云套餐中,携带 customFonts 的请求会返回 403。如需启用,请联系我们了解 Enterprise 方案 或运行本地部署。
  • 每个请求都会在响应前端到端处理完成。 非常大的文档可能需要更长时间才能返回;请据此设置你端的请求超时时间。

不要预期

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

支持与限制

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

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