导出自定义样式到 .docx
在导出为 DOCX 时,您可以定义将应用于导出文档的自定义样式。这在您想要跨文档保持一致的外观和感觉时非常有用。
// 导入 ExportDocx 扩展
import { ExportDocx } from '@tiptap-pro/extension-export-docx'
const editor = new Editor({
extensions: [
// 其他扩展 ...
ExportDocx.configure({
onCompleteExport: (result: string | Buffer<ArrayBufferLike> | Blob | Stream) => {}, // 必需
styleOverrides: { // 样式覆盖
paragraphStyles: [
// Heading 1 样式覆盖
{
id: 'Heading1',
name: 'Heading 1',
basedOn: 'Normal',
next: 'Normal',
quickFormat: true,
run: {
font: 'Aptos',
size: pointsToHalfPoints(16),
bold: true,
color: 'FF0000',
},
paragraph: {
spacing: {
before: pointsToTwips(12),
after: pointsToTwips(6),
line: lineHeightToDocx(1.15),
},
},
},
]
}
}),
// 其他扩展 ...
],
// 其他编辑器设置 ...
})在上面的示例中,我们正在导出一个带有自定义 Heading 1 样式的文档。该样式基于 Normal 样式,使用红色和 Aptos 字体。段落前的间距设置为 12pt,段落后为 6pt。行高设置为 1.15。
你还可以为其他元素创建自定义样式,如 Heading 2、Heading 3、List Bullet、List Number 等。paragraphStyles 数组接受具有以下属性的对象数组:
段落样式对象
paragraphStyle 对象接受以下属性:
| 属性 | 类型 | 描述 |
|---|---|---|
id | string | 样式的唯一标识符。 |
name | string | 样式的显示名称。 |
basedOn | string | 此样式基于的基础样式(例如 Normal)。 |
next | string | 应用于下一个段落的样式。 |
quickFormat | boolean | 如果为 true,该样式会出现在快速格式菜单中。 |
run | object | 定义文本格式(字体、大小、颜色、粗体等)。 |
paragraph | object | 定义段落格式(间距、对齐、边框等)。 |
Run 样式属性
来自 paragraphStyle 的 run 对象接受以下属性:
| 属性 | 类型 | 描述 |
|---|---|---|
font | string | 文本的字体族。 |
size | number | 以半点为单位的字体大小(例如,16 点 = 32)。 |
bold | boolean | 设置为 true 使文本加粗。 |
italics | boolean | 设置为 true 使用斜体文本。 |
color | string | 十六进制格式的文本颜色(例如,FF0000 表示红色。不需要 #)。 |
kern | number | 以点为单位调整字符之间的间距。 |
effect | 可以应用的特殊文本效果。 | |
emphasisMark | string | 出现在文本上方或下方的强调标记。(如 dot) |
smallCaps | boolean | 设置为 true 以小大写字母显示文本。 |
allCaps | boolean | 设置为 true 以大写字母显示文本。 |
strike | boolean | 设置为 true 应用单删除线。 |
doubleStrike | boolean | 设置为 true 应用双删除线。 |
subScript | boolean | 设置为 true 使用下标文本。 |
superScript | boolean | 设置为 true 使用上标文本。 |
highlight | 高亮颜色(预定义值)。 | |
characterSpacing | number | 以 TWIP 为单位调整字符之间的间距(…我们知道,TWIP 对吧? |
shading | object | 对文本应用背景阴影。 |
shading → type | ShadingType | 阴影类型(clear、solid、horizontalStripe 等)。 |
shading → fill | string | 十六进制格式的阴影填充颜色(例如,FF0000 表示红色)。 |
shading → color | string | 十六进制格式的阴影颜色(例如,FF0000 表示红色)。 |
scale | number | 调整文本宽度(以百分比为单位)。 |
underline | object | 下划线样式,指定如 color 和 type 等属性,或使用空对象表示简单下划线。 |
underline → color | string | 十六进制格式的下划线颜色(例如,FF0000 表示红色。不需要 #)。 |
underline → type | UnderlineType | 下划线类型(single、double 或 thick) |
有关更高级的样式选项和详细用法,你可以参考我们包中暴露的 IRunStylePropertiesOptions 类型,或参考 docx 文档。
段落样式属性
来自 paragraphStyle 的 paragraph 对象接受以下属性:
| 属性 | 类型 | 描述 |
|---|---|---|
spacing | object | 控制间距:如 before、after 和 line 等属性。 |
alignment | string | 设置段落对齐方式(left、center、right 或 justify)。 |
border | object | 定义段落周围的边框(上、下、左、右)。 |
shading | object | 对段落应用背景阴影。 |
indent | object | 指定缩进(首行、悬挂、左、右)。 |
contextualSpacing | boolean | 如果为 true,减少相同样式段落之间的间距。 |
keepNext | boolean | 将此段落与下一个段落保持在同一页面上。 |
keepLines | boolean | 将段落的所有行保持在同一页面上。 |
outlineLevel | number | 设置文档组织的大纲级别(通常为 1–9)。 |
thematicBreak | number | 设置为 true 时添加水平分页线。 |
rightTabStop | number | 设置右制表位的位置(以 twips 为单位)。 |
leftTabStop | number | 设置左制表位的位置(以 twips 为单位)。 |
numbering | object | 控制编号设置(例如引用、级别、自定义格式)。 |
numbering → reference | string | 编号样式引用 ID。 |
numbering → level | number | 编号层次结构中的级别(从 0 开始)。 |
spacing | object | 控制段落的间距。 |
spacing → before | number | 段落前的间距。 |
spacing → after | number | 段落后的间距。 |
spacing → line | number | 段落内的行间距。 |
spacing → lineRule | LineRuleType | 定义如何计算行间距。 |
有关更高级的样式选项和详细用法,你可以参考我们包中暴露的 IParagraphStylePropertiesOptions 类型,或参考 docx 文档。
Tiptap 的导出默认样式
Tiptap 为导出的文档提供了合理的默认样式,但你可以通过提供自己的自定义样式来覆盖这些样式。这使你可以在整个文档中创建一致的外观和感觉。
{
paragraphStyles: [
// Normal 样式(大多数段落的默认样式)
{
id: 'Normal',
name: 'Normal',
run: {
font: 'Aptos',
size: pointsToHalfPoints(11),
},
paragraph: {
spacing: {
before: 0,
after: pointsToTwips(10),
line: lineHeightToDocx(1.15),
},
},
},
// List Paragraph 样式(用于项目符号和编号)
{
id: 'ListParagraph',
name: 'List Paragraph',
basedOn: 'Normal',
quickFormat: true,
run: {
font: 'Aptos',
size: pointsToHalfPoints(11),
},
paragraph: {
spacing: {
before: 0,
after: pointsToTwips(2),
line: lineHeightToDocx(1),
},
},
},
// Heading 1 样式
{
id: 'Heading1',
name: 'Heading 1',
basedOn: 'Normal',
next: 'Normal',
quickFormat: true,
run: {
font: 'Aptos Light',
size: pointsToHalfPoints(16),
bold: true,
color: '2E74B5',
},
paragraph: {
spacing: {
before: pointsToTwips(12),
after: pointsToTwips(6),
line: lineHeightToDocx(1.15),
},
},
},
// Heading 2 样式
{
id: 'Heading2',
name: 'Heading 2',
basedOn: 'Normal',
next: 'Normal',
quickFormat: true,
run: {
font: 'Aptos Light',
size: pointsToHalfPoints(14),
bold: true,
color: '2E74B5',
},
paragraph: {
spacing: {
before: pointsToTwips(12),
after: pointsToTwips(6),
line: lineHeightToDocx(1.15),
},
},
},
// Heading 3 样式
{
id: 'Heading3',
name: 'Heading 3',
basedOn: 'Normal',
next: 'Normal',
quickFormat: true,
run: {
font: 'Aptos',
size: pointsToHalfPoints(13),
bold: true,
color: '2E74B5',
},
paragraph: {
spacing: {
before: pointsToTwips(12),
after: pointsToTwips(6),
line: lineHeightToDocx(1.15),
},
},
},
// Heading 4 样式
{
id: 'Heading4',
name: 'Heading 4',
basedOn: 'Normal',
next: 'Normal',
quickFormat: true,
run: {
font: 'Aptos',
size: pointsToHalfPoints(12),
bold: true,
color: '2E74B5',
},
paragraph: {
spacing: {
before: pointsToTwips(12),
after: pointsToTwips(6),
line: lineHeightToDocx(1.15),
},
},
},
// Heading 5 样式
{
id: 'Heading5',
name: 'Heading 5',
basedOn: 'Normal',
next: 'Normal',
quickFormat: true,
run: {
font: 'Aptos',
size: pointsToHalfPoints(11),
bold: true,
color: '2E74B5',
},
paragraph: {
spacing: {
before: pointsToTwips(12),
after: pointsToTwips(6),
line: lineHeightToDocx(1.15),
},
},
},
// Title 样式
{
id: 'Title',
name: 'Title',
basedOn: 'Normal',
next: 'Normal',
quickFormat: true,
run: {
font: 'Aptos Light',
size: pointsToHalfPoints(22),
bold: true,
color: '000000',
},
paragraph: {
alignment: AlignmentType.CENTER,
spacing: {
before: 0,
after: 0,
line: lineHeightToDocx(1.15),
},
},
},
// Subtitle 样式
{
id: 'Subtitle',
name: 'Subtitle',
basedOn: 'Normal',
next: 'Normal',
quickFormat: true,
run: {
font: 'Aptos Light',
size: pointsToHalfPoints(16),
italics: true,
color: '666666',
},
paragraph: {
alignment: AlignmentType.CENTER,
spacing: {
before: 0,
after: 0,
line: lineHeightToDocx(1.15),
},
},
},
// Quote 样式(通常用于缩进的斜体文本)
{
id: 'Quote',
name: 'Quote',
basedOn: 'Normal',
quickFormat: true,
run: {
font: 'Aptos',
italics: true,
},
paragraph: {
alignment: AlignmentType.CENTER,
spacing: {
before: pointsToTwips(10),
after: pointsToTwips(10),
line: lineHeightToDocx(1.15),
},
},
},
// Intense Quote 样式(更明显的缩进)
{
id: 'IntenseQuote',
name: 'Intense Quote',
basedOn: 'Normal',
quickFormat: true,
run: {
font: 'Aptos',
italics: true,
color: '444444',
},
paragraph: {
alignment: AlignmentType.CENTER,
spacing: {
before: pointsToTwips(10),
after: pointsToTwips(10),
line: lineHeightToDocx(1.15),
},
},
},
// No Spacing 样式(段落前后无额外间距)
{
id: 'NoSpacing',
name: 'No Spacing',
basedOn: 'Normal',
quickFormat: true,
paragraph: {
spacing: {
before: 0,
after: 0,
line: lineHeightToDocx(1),
},
},
},
// Hyperlink 样式
{
id: 'Hyperlink',
name: 'Hyperlink',
basedOn: 'Normal',
run: {
color: '0563C1',
underline: {
type: 'single',
},
},
},
],
}元素覆盖
元素覆盖与 styleOverrides 是分开的。虽然 styleOverrides 定义了 Word 通过其样式系统应用的命名段落样式(标题 1、正文等),但元素覆盖在转换过程中直接将基础默认值应用于每个段落、文本运行、表格、表格单元格或图像。
这让您无需定义命名样式即可对每种元素的原始 docx 属性进行细粒度控制。
浅层展开:所有覆盖对象都使用浅层展开,而非深层合并。在覆盖如
spacing或transformation等嵌套属性时,请提供完整的嵌套对象,以避免出现未定义字段。
paragraphOverrides
接受除 children 之外的任何 IParagraphOptions 属性。这些值会先作为基础默认值展开;每个节点计算出的值(alignment、列表编号、标题级别)会覆盖它们。
应用于:标准段落、标题、块引用、无序列表、有序列表和列表项。
ExportDocx.configure({
onCompleteExport: (result) => { /* ... */ },
paragraphOverrides: {
spacing: { after: 200, before: 100 },
alignment: AlignmentType.LEFT,
},
})
spacing是部分覆盖:paragraphOverrides中的spacing.before和spacing.after会保留在每个段落上,但spacing.line始终会根据每个段落的lineHeight重新计算,并替换您在此处传入的任何值。IParagraphOptions上其他嵌套对象仍然会被整体替换(不进行深度合并)。
textRunOverrides
接受除 text 之外的任何 IRunOptions 属性。这些值会先作为基础默认值展开;按标记的格式设置(粗体、斜体、下划线、字体族、字号、颜色、高亮等)会覆盖它们。
可用于为整个导出的文档设置默认字体或字号。
ExportDocx.configure({
onCompleteExport: (result) => { /* ... */ },
textRunOverrides: {
font: 'Arial',
size: 24, // 单位为半点数的 12pt
},
})tableOverrides
接受除 rows 之外的任何 ITableOptions 属性。这些值会最后展开,因此用户提供的表格级属性(边框、边距、布局)会优先于计算出的默认值。
ExportDocx.configure({
onCompleteExport: (result) => { /* ... */ },
tableOverrides: {
width: { size: 100, type: WidthType.PERCENTAGE },
layout: TableLayoutType.FIXED,
},
})tableCellOverrides
接受除 children 之外的任何 ITableCellOptions 属性。这些值会先作为基础默认值展开;每个单元格计算出的值(宽度、colspan、宽度类型)会覆盖它们。
可用于为所有表格单元格应用一致的底纹、垂直对齐或边框样式。
ExportDocx.configure({
onCompleteExport: (result) => { /* ... */ },
tableCellOverrides: {
shading: { fill: 'F0F0F0', type: ShadingType.SOLID },
verticalAlign: VerticalAlign.CENTER,
},
})imageOverrides
接受除 data、type 和 fallback 之外的任何 IImageOptions 属性。这些值会最后展开,因此用户提供的值(例如自定义 transformation 尺寸)会优先于从图像缓冲区检测到的内在尺寸。无论覆盖如何,SVG 回退数据始终会保留。
ExportDocx.configure({
onCompleteExport: (result) => { /* ... */ },
imageOverrides: {
transformation: { width: 400, height: 300 },
floating: {
horizontalPosition: { offset: 0 },
verticalPosition: { offset: 0 },
},
},
})组合使用示例
您可以在单个配置中组合所有元素覆盖:
editor.commands.exportDocx({
onCompleteExport: (result) => { /* ... */ },
exportType: 'blob',
paragraphOverrides: {
spacing: { after: 200, before: 100 },
},
textRunOverrides: {
font: 'Arial',
size: 24, // 单位为半点数的 12pt
},
tableCellOverrides: {
shading: { fill: 'F0F0F0', type: ShadingType.SOLID },
},
imageOverrides: {
transformation: { width: 400, height: 300 },
},
})headerFooterOverrides
元素覆盖同样适用于页眉和页脚内容以及正文——在顶层设置的 paragraphOverrides、textRunOverrides、tableOverrides、tableCellOverrides 或 imageOverrides 值也会流入运行中的页眉和页脚。
当您希望页眉/页脚内容与正文不同时,请使用 headerFooterOverrides。它接受相同的五个字段,并替换仅作用于页眉/页脚内容的对应正文级覆盖。每个字段都是完整替换,而不是深度合并——您未定义的字段会回退到同名的正文级覆盖。
import { BorderStyle } from 'docx'
ExportDocx.configure({
onCompleteExport: (result) => { /* ... */ },
// 正文表格将获得 1pt 网格边框
tableOverrides: {
borders: {
top: { style: BorderStyle.SINGLE, size: 4, color: '000000' },
bottom: { style: BorderStyle.SINGLE, size: 4, color: '000000' },
left: { style: BorderStyle.SINGLE, size: 4, color: '000000' },
right: { style: BorderStyle.SINGLE, size: 4, color: '000000' },
insideHorizontal: { style: BorderStyle.SINGLE, size: 4, color: '000000' },
insideVertical: { style: BorderStyle.SINGLE, size: 4, color: '000000' },
},
},
// 页眉/页脚表格(例如页面页眉中的布局表格)将
// 不显示边框。除非在这里覆盖,其他正文覆盖仍然适用。
headerFooterOverrides: {
tableOverrides: {
borders: {
top: { style: BorderStyle.NONE },
bottom: { style: BorderStyle.NONE },
left: { style: BorderStyle.NONE },
right: { style: BorderStyle.NONE },
insideHorizontal: { style: BorderStyle.NONE },
insideVertical: { style: BorderStyle.NONE },
},
},
},
})作用范围:
headerFooterOverrides仅适用于从 Pages 扩展 自动提取的页眉/页脚内容。如果您使用Docx.Header/Docx.Footer实例手动构建页眉/页脚,或者使用传入headers/footers选项的自定义工厂函数进行构建,这些构造都会绕过headerFooterOverrides—— 请在您的工厂函数内部自行应用这些覆盖。