导出自定义节点为 .epub

Available in Start planBetav0.4.0

@tiptap-pro/extension-export-epub 扩展支持自定义节点转换,允许您定义 Tiptap 结构中自定义节点在 EPUB 导出中的渲染方式。

需要导出 DOCX 扩展

EPUB 导出的自定义节点转换需要安装并配置 @tiptap-pro/extension-export-docx 扩展。EPUB 导出在转换为 EPUB 之前,底层使用 DOCX 生成。如果没有该扩展,customNodes 选项将不会生效。

将自定义节点导出为 .epub

在配置 ExportEpub 时通过 customNodes 选项传入您的自定义节点定义:

import { ExportEpub } from '@tiptap-pro/extension-export-epub'
import { ExportDocx } from '@tiptap-pro/extension-export-docx'

const editor = new Editor({
  extensions: [
    ExportDocx,
    ExportEpub.configure({
      token: 'YOUR_TOKEN',
      appId: 'YOUR_APP_ID',
      customNodes: [
        {
          type: 'hintbox',
          render: node => {
            // 定义自定义节点在文档中的渲染方式
          },
        },
      ],
      onCompleteExport(result) {
        // 处理导出的 EPUB 文件
      },
    }),
  ],
})

自定义节点约定

自定义节点转换器必须遵守底层 DOCX 生成库的要求。在实践中,DOCX 的自定义转换函数应返回该节点允许的 DOCX 元素之一:Paragraph 类(或 Paragraph 类的数组)、Table 类、TextRun 类、ExternalHyperlink 类,或者如果该节点应在输出中跳过,则返回 null

定义自定义节点扩展

举例来说,假设你的编辑器有一个自定义节点类型 hintbox(一个带有提示样式的框)。你可以定义它在导出文档中应如何显示。

下面是 Hintbox 扩展的自定义节点示例:

import { mergeAttributes, Node } from '@tiptap/core'

export interface ParagraphOptions {
  /**
   * 段落节点的 HTML 属性。
   * @default {}
   * @example { class: 'foo' }
   */
  HTMLAttributes: Record<string, any>
}

declare module '@tiptap/core' {
  interface Commands<ReturnType> {
    hintbox: {
      /**
       * 设置一个提示框
       * @example editor.commands.setHintbox()
       */
      setHintbox: () => ReturnType
      /**
       * 切换一个提示框
       * @example editor.commands.toggleHintbox()
       */
      toggleHintbox: () => ReturnType
    }
  }
}

/**
 * 该扩展允许你创建提示框。
 * @see https://www.tiptap.dev/api/nodes/paragraph
 */
export const Hintbox = Node.create<ParagraphOptions>({
  name: 'hintbox',

  priority: 1000,

  addOptions() {
    return {
      HTMLAttributes: {
        style: 'padding: 20px; border: 1px solid #b8d8ff; border-radius: 5px; background-color: #e6f3ff;',
      },
    }
  },

  group: 'block',

  content: 'inline*',

  parseHTML() {
    return [{ tag: 'p' }]
  },

  renderHTML({ HTMLAttributes }) {
    return ['p', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]
  },

  addCommands() {
    return {
      setHintbox:
        () =>
        ({ commands }) => {
          return commands.setNode(this.name)
        },
      toggleHintbox:
        () =>
        ({ commands }) => {
          return commands.toggleNode(this.name, 'paragraph')
        },
    }
  },

  addKeyboardShortcuts() {
    return {
      'Mod-Alt-h': () => this.editor.commands.toggleHintbox(),
    }
  },
})

定义自定义节点渲染函数

我们将定义 Hintbox 自定义节点在导出文档中的渲染方式:

import { ExportEpub } from '@tiptap-pro/extension-export-epub'
import {
  convertTextNode,
  Docx,
  ExportDocx,
  lineHeightToDocx,
  pixelsToHalfPoints,
  pointsToTwips,
} from '@tiptap-pro/extension-export-docx'

const editor = new Editor({
  extensions: [
    // 其他扩展 ...
    ExportDocx,
    ExportEpub.configure({
      token: 'YOUR_TOKEN',
      appId: 'YOUR_APP_ID',
      onCompleteExport: result => {
        const url = URL.createObjectURL(result)
        const a = document.createElement('a')
        a.href = url
        a.download = 'export.epub'
        a.click()
        URL.revokeObjectURL(url)
      },
      customNodes: [
        {
          type: 'hintbox',
          render: node => {
            return new Docx.Paragraph({
              children: node.content.map(content => convertTextNode(content)),
              style: 'Hintbox',
            })
            },
        },
      ],
      styleOverrides: {
        paragraphStyles: [
          {
            id: 'Hintbox',
            name: 'Hintbox',
            basedOn: 'Normal',
            next: 'Normal',
            quickFormat: false,
            run: {
              font: 'Aptos Light',
              size: pixelsToHalfPoints(16),
            },
            paragraph: {
              spacing: {
                before: pointsToTwips(12),
                after: pointsToTwips(12),
                line: lineHeightToDocx(1),
              },
              border: {
                top: { style: Docx.BorderStyle.SINGLE, size: 1, color: 'b8d8ff', space: 5 },
                bottom: { style: Docx.BorderStyle.SINGLE, size: 1, color: 'b8d8ff', space: 5 },
                right: { style: Docx.BorderStyle.SINGLE, size: 1, color: 'b8d8ff', space: 5 },
                left: { style: Docx.BorderStyle.SINGLE, size: 1, color: 'b8d8ff', space: 5 },
              },
              shading: {
                type: Docx.ShadingType.SOLID,
                color: 'e6f3ff',
              },
            },
          },
        ],
      },
    }),
    // 其他扩展 ...
  ],
})

您可以在 render 函数中使用 @tiptap-pro/extension-export-docx 包中的 Docx 导入提供的 Docx 库类(如 ParagraphTextRunTable 等)构建任意支持的 DOCX 元素。