将 DOCX 默认样式作为 CSS 注入

Available in Start planBetav0.10.0

Beta 功能

本页上的 API、属性名称和行为正在逐步稳定,但在正式可用之前仍可能继续 调整。如果你依赖此功能,请锁定确切的包版本,并在采用前阅读 哪些不会工作 这一节。

这会做什么

DOCX 文件包含一个样式目录:这些命名定义(Heading1NormalQuote 等)告诉 Word 正文、标题、超链接和列表应当如何显示。DOCX 中的每个段落或文本片段,要么继承自这些命名样式之一,要么在行内覆盖单独的属性。

如果没有这个功能,Tiptap Convert API 会把这些样式扁平化为按节点设置的属性。你会看到 fontSize、color 和 spacing 作为行内属性应用到每个段落上,但 Word 用作事实来源的可复用命名样式会被丢弃。

此功能会将命名样式目录提取为一个 CSS 对象(以 CSS 选择器为键),并且既可以直接返回给你,也可以将其作为带作用域的 <style> 标签注入到页面中。结果就是:文档的基础排版通过 CSS 级联规则来呈现,而不是在每个节点上使用行内属性,因此你的编辑器在视觉上与原始 DOCX 保持一致,同时又不会使文档模型膨胀。

安装

npm i @tiptap-pro/extension-import-docx@^0.10.0

它如何工作

一个四步流程:

  1. 在导入请求中选择启用。 扩展要求 Convert API 在 cssStyles.enabledtrue 时包含样式目录。
  2. 转换服务提取。 转换器读取 DOCX 的样式定义,解析继承链(basedOn),将 OOXML 属性翻译为 CSS 等价物,并在导入响应中返回一个 cssStyles 对象。
  3. 扩展构建 CSS 规则。cssStyles 对象编译为 CSS 字符串,并按你配置的选择器进行作用域隔离(默认 .tiptap)。
  4. 自动注入或交给你处理。 如果 autoInject: true,扩展会把作用域隔离的 <style> 元素追加到 document.head。如果 autoInject: false,你会在 onImport 回调中收到原始的 cssStyles 对象,并由你决定如何处理。
import { ImportDocx } from '@tiptap-pro/extension-import-docx'

ImportDocx.configure({
  appId: 'your-app-id',
  token: 'your-jwt',
  cssStyles: {
    enabled: true,      // 每次导入都提取样式
    autoInject: true,   // 自动以作用域 <style> 标签的形式注入
    selector: '.tiptap', // 将规则作用域限定在该选择器下
  },
})

或者在每次调用时控制:

editor
  .chain()
  .importDocx({
    file,
    cssStyles: { enabled: true, autoInject: false },
    onImport(context) {
      // `context.cssStyles` 是原始对象;你可以按自己的方式处理
      console.log(context.cssStyles)
    },
  })
  .run()

何者可用

选择器(16 个): 该功能只会把命名样式提取并映射到下列这些 CSS 选择器。

类型选择器
块级p, h1h6, blockquote, ul li, ol li
行内 / 标记strong, em, u, s, a, code

CSS 属性(11 个): 上述每个选择器都可以接收这些属性的任意子集,具体取决于 DOCX 样式里定义了哪些。

属性DOCX 来源备注
fontSizew:sz(半磅)转换为 NpxhalfPoints / 2
colorw:color十六进制;跳过 "auto"
fontFamilyw:rFonts优先使用 @w:ascii,其次是 hAnsi,再其次是 cs
fontWeightw:b"bold""normal"
fontStylew:i"italic""normal"
textDecorationw:u + w:strike组合(例如 "underline line-through"
backgroundColorw:shd[@fill]十六进制;跳过 "auto"
textAlignw:jcleft / center / right / justify(DOCX bothjustify
marginTopw:spacing[@before]Twips → Npt
marginBottomw:spacing[@after]Twips → Npt
lineHeightw:spacing[@line] + [@lineRule]auto → 无单位倍数;exact/atLeastNpt

其他行为:

  • 继承链。 basedOn 引用会通过迭代解析,并进行循环检测,因此子样式会包含其从父样式继承的全部内容。
  • 本地化文档。 使用非英语版 Word 创建的 DOCX 文件(例如西班牙语 Título1、德语 Überschrift1)会映射回其规范名称(heading 1),因此选择器分配仍然可用。
  • 失败是不可见的。 如果提取因任何原因抛出异常(styles.xml 损坏、属性意外),它会返回 {},导入会正常继续;该功能绝不会阻止可工作的导入。

什么不会工作

这些是有意为之的限制

这些并不是 bug。它们是该功能明确且已文档化的边界。如果你的工作流依赖下面任何一项,那么此功能并不是合适的工具。

  • 伪类。 :hover:focus:first-child 等。DOCX 样式模型没有对应概念;不会提取任何此类内容。
  • 任意选择器。 上表中的 16 个选择器就是完整映射。任何用途落在该映射之外的 DOCX 样式(TableGridTOC1FootnoteText、自定义用户样式)都会被丢弃。
  • 字母间距。 导出端编译器支持,但在导入时不会提取。
  • 超出选择器本身的特异性。 注入的规则会以你的选择器为作用域(例如 .tiptap h1 { … })。它们不会考虑与你自己的样式表之间的级联顺序;你必须确保你的 CSS 不会意外覆盖它们,反之亦然。
  • 行内覆盖。 如果某个特定段落或文本片段在 Word 中行内覆盖了其样式(例如某个 <w:r> 直接设置了 w:color),这些覆盖仍会通过 Tiptap 节点上的属性渲染,而不是通过注入的样式表。你无法通过编辑注入的 CSS 来“取消覆盖”行内格式。
  • 非文本样式。 表格样式(w:tblStyle)、编号样式(w:numId),以及诸如页码之类的字符样式都不在映射范围内。

你可以期待

  • 在每次成功导入且 autoInject: true 时,都会有一个附加到 document.head 的带作用域 <style> 元素。后续导入会替换前一个元素,因此任意时刻只会有一个处于活动状态。
  • 无论 autoInject 设置如何,你都可以在 onImport 回调的上下文中获取原始 cssStyles 对象。你可以完全忽略自动注入,自己应用这些样式:把它们写入文件、发送到主题系统,或任何对你的应用最合适的方式。
  • 样式会限定在你的编辑器范围内(默认 .tiptap,可通过 cssStyles.selector 配置)。只有当你把选择器设为全局选择器(例如 body)时,才会泄漏到页面的其他部分。
  • 静默失败模式。如果 DOCX 没有样式目录,或者所有命名样式都落在 16 个选择器映射之外,cssStyles 会返回空,并且不会注入任何样式标签。

不要期待

  • 像素级 Word 还原。 Word 使用内建布局引擎,其段落间距语义与 CSS 不同(例如折叠外边距、上下文相关的前后间距)。提取出的 CSS 是对 DOCX 样式定义 的忠实翻译,而不是对 Word 布局输出的复现。
  • 对实时编辑的动态更新。 样式只会在每次导入时注入一次。如果用户之后在 Tiptap 中编辑文档,注入的样式不会重新计算;它们描述的是导入文档的基础样式,而不是当前编辑器状态。
  • 跨文档合并。 每次导入都会替换之前的 <style> 标签。连续导入两个 DOCX 文件时,你只能得到第二个文档的样式。如果你需要合并,可以从 onImport 中读取两个 cssStyles 对象并自行合并。
  • 往返保证。 提取再导出的循环并不是无损的;反向方向及其自身注意事项请参见 CSS to DOCX 导出页面。

配置参考

ImportDocx.configure({
  cssStyles: {
    enabled?: boolean,     // 默认:false。打开提取。
    autoInject?: boolean,  // 默认:false。将 <style> 标签追加到 document.head。
    selector?: string,     // 默认:'.tiptap'。注入规则的作用域。
  },
})

按次调用覆盖:

editor.chain().importDocx({
  file,
  cssStyles: { enabled, autoInject, selector }, // 任意子集
  onImport(context) { /* context.cssStyles 在此处可用 */ },
})

REST API 查询参数(若你直接调用 /import/docx):

POST /import/docx?extractCssStyles=true

相关

  • CSS to DOCX (导出):反向方向。将你的编辑器 CSS 重新编译为 DOCX 样式定义。
  • ConvertKit:配套扩展包,为 Paragraph、Heading、Table 等添加 DOCX 感知属性。
  • REST API: CSS 样式提取:原始的 extractCssStyles 参数和完整响应结构。