@docen/docx 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blockquote-DY80QC06.d.mts +21 -0
- package/dist/bullet-list-DF60pnSL.d.mts +12 -0
- package/dist/converters/docx.d.mts +25 -4
- package/dist/converters/docx.mjs +217 -41
- package/dist/converters/html.d.mts +1 -1
- package/dist/converters/html.mjs +1 -1
- package/dist/converters/markdown.d.mts +1 -1
- package/dist/converters/markdown.mjs +1 -1
- package/dist/converters/patch.d.mts +1 -1
- package/dist/converters/prepare.d.mts +1 -1
- package/dist/converters/prepare.mjs +6 -9
- package/dist/converters/styles.d.mts +7 -1
- package/dist/converters/styles.mjs +31 -8
- package/dist/{core-DT9IrTzN.mjs → core-BnF8XhVE.mjs} +229 -33
- package/dist/core-DC0_-WcE.d.mts +387 -0
- package/dist/core.d.mts +1 -1
- package/dist/core.mjs +1 -1
- package/dist/details-Dd5MqqmR.d.mts +17 -0
- package/dist/document-Cws7XTYL.d.mts +28 -0
- package/dist/editor.d.mts +1 -1
- package/dist/editor.mjs +1 -1
- package/dist/extensions/blockquote.d.mts +2 -22
- package/dist/extensions/bullet-list.d.mts +2 -0
- package/dist/extensions/bullet-list.mjs +21 -0
- package/dist/extensions/code-block.d.mts +1 -18
- package/dist/extensions/column-break.d.mts +1 -30
- package/dist/extensions/column-break.mjs +1 -1
- package/dist/extensions/details.d.mts +2 -18
- package/dist/extensions/document.d.mts +1 -1
- package/dist/extensions/document.mjs +3 -6
- package/dist/extensions/extensions.d.mts +10 -6
- package/dist/extensions/extensions.mjs +9 -4
- package/dist/extensions/formatting-marks.d.mts +1 -37
- package/dist/extensions/formatting-marks.mjs +1 -1
- package/dist/extensions/heading.mjs +31 -9
- package/dist/extensions/image.d.mts +1 -1
- package/dist/extensions/image.mjs +20 -4
- package/dist/extensions/index.d.mts +17 -4
- package/dist/extensions/index.mjs +18 -3
- package/dist/extensions/link.d.mts +2 -0
- package/dist/extensions/link.mjs +56 -0
- package/dist/extensions/mention.d.mts +2 -24
- package/dist/extensions/ordered-list.d.mts +1 -24
- package/dist/extensions/ordered-list.mjs +10 -1
- package/dist/extensions/page-break.d.mts +1 -1
- package/dist/extensions/page-break.mjs +1 -1
- package/dist/extensions/paragraph.mjs +14 -6
- package/dist/extensions/passthrough.d.mts +2 -2
- package/dist/extensions/passthrough.mjs +2 -2
- package/dist/extensions/scroll.d.mts +2 -0
- package/dist/extensions/scroll.mjs +33 -0
- package/dist/extensions/section-break.d.mts +1 -43
- package/dist/extensions/section-break.mjs +1 -1
- package/dist/extensions/strike.mjs +1 -5
- package/dist/extensions/tab.d.mts +2 -0
- package/dist/extensions/tab.mjs +2 -0
- package/dist/extensions/table-cell.mjs +3 -8
- package/dist/extensions/table-header.mjs +1 -6
- package/dist/extensions/table-row.mjs +1 -6
- package/dist/extensions/table.mjs +1 -6
- package/dist/extensions/task-item.d.mts +2 -26
- package/dist/extensions/text-style.mjs +1 -6
- package/dist/extensions/tiptap.d.mts +1 -1
- package/dist/extensions/toc-field.d.mts +2 -0
- package/dist/extensions/toc-field.mjs +2 -0
- package/dist/extensions/utils.d.mts +2 -2
- package/dist/extensions/utils.mjs +10 -1
- package/dist/extensions/wpg-group.d.mts +2 -2
- package/dist/extensions/wpg-group.mjs +2 -2
- package/dist/extensions/wps-shape.d.mts +1 -1
- package/dist/extensions/wps-shape.mjs +1 -1
- package/dist/index.d.mts +48 -11
- package/dist/index.mjs +19 -4
- package/dist/link-BawPjQZR.d.mts +6 -0
- package/dist/mention-BGLzLVYw.d.mts +23 -0
- package/dist/ordered-list-DFAe-YEV.d.mts +25 -0
- package/dist/scroll-ZNeThJsJ.d.mts +18 -0
- package/dist/task-item-B0ntvQ1Y.d.mts +25 -0
- package/dist/{tiptap-pZsNPsvV.d.mts → tiptap-BKqn41uT.d.mts} +2 -2
- package/dist/{utils-D87vukzp.d.mts → utils-BJwDQts7.d.mts} +10 -1
- package/package.json +3 -3
- package/dist/core-C9VunJ8o.d.mts +0 -174
- package/dist/document-BH1y4qHM.d.mts +0 -6
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/extensions/blockquote.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Blockquote extension — owns the DOCX expression of a blockquote.
|
|
4
|
+
*
|
|
5
|
+
* DOCX has no blockquote element; the convention is a left indent plus a left
|
|
6
|
+
* border on each contained paragraph. This module owns that signature; the
|
|
7
|
+
* DocxManager walks the blockquote's child paragraphs and applies it to each.
|
|
8
|
+
*/
|
|
9
|
+
/** Left indent (twips, ~0.5 inch) marking a blockquote. */
|
|
10
|
+
declare const BLOCKQUOTE_INDENT_LEFT = 720;
|
|
11
|
+
/** Left border marking a blockquote. */
|
|
12
|
+
declare const BLOCKQUOTE_BORDER: {
|
|
13
|
+
readonly style: "single";
|
|
14
|
+
readonly size: 18;
|
|
15
|
+
readonly space: 12;
|
|
16
|
+
readonly color: "CCCCCC";
|
|
17
|
+
};
|
|
18
|
+
/** Apply the blockquote signature (left indent + left border) to paragraph opts. */
|
|
19
|
+
declare function applyBlockquoteStyle(paraObj: Record<string, unknown>): void;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { BLOCKQUOTE_INDENT_LEFT as n, applyBlockquoteStyle as r, BLOCKQUOTE_BORDER as t };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/extensions/bullet-list.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* BulletList — carries the source DOCX abstractNum reference (when the list came
|
|
4
|
+
* from parseDOCX) so the round-trip reuses the original numbering definition
|
|
5
|
+
* (custom bullet glyph/font/indent, e.g. a Wingdings marker) instead of
|
|
6
|
+
* regenerating the default. The `numbering` attr is DOCX-only (not rendered
|
|
7
|
+
* to HTML); lists created in the editor carry null and compile to the default
|
|
8
|
+
* bullet.
|
|
9
|
+
*/
|
|
10
|
+
declare const BulletList: import("@tiptap/core").Node<import("@tiptap/extension-bullet-list").BulletListOptions, any>;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { BulletList as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as JSONContent } from "../core-
|
|
1
|
+
import { a as JSONContent } from "../core-DC0_-WcE.mjs";
|
|
2
2
|
import { PrepareStep } from "./prepare.mjs";
|
|
3
3
|
import { DocumentOptions, OutputByType, OutputType, PackerOptions, parseDocument } from "@office-open/docx";
|
|
4
4
|
|
|
@@ -68,15 +68,36 @@ declare class DocxManager {
|
|
|
68
68
|
private resolveSectionChild;
|
|
69
69
|
/** Wrap an opaque SectionChild in a passthrough atom (attrs.data = JSON). */
|
|
70
70
|
private resolvePassthrough;
|
|
71
|
+
/**
|
|
72
|
+
* Resolve a table of contents into an editable `tableOfContents` container:
|
|
73
|
+
* `attrs.options` carries the field switches, `content` is the entry
|
|
74
|
+
* paragraphs. Each entry's inner HYPERLINK field has content-less runs that
|
|
75
|
+
* office-open parses as `null`; resolving the entries through
|
|
76
|
+
* `resolveSectionChild` → `resolveParagraphChildren` drops those nulls, so the
|
|
77
|
+
* TOC no longer reaches the generate path as an opaque blob of nulls (the
|
|
78
|
+
* `stringifyRunInline(null).break` crash). When `entries` is absent/empty
|
|
79
|
+
* (a fresh, unrendered TOC), keep the node valid for `content: "block+"` with
|
|
80
|
+
* a placeholder empty paragraph.
|
|
81
|
+
*/
|
|
82
|
+
private resolveToc;
|
|
71
83
|
/**
|
|
72
84
|
* Resolve a details group-SDT: the summary-style paragraph becomes
|
|
73
85
|
* detailsSummary, the remaining blocks fold into detailsContent.
|
|
74
86
|
*/
|
|
75
87
|
private resolveDetailsSdt;
|
|
76
88
|
private resolveParagraph;
|
|
77
|
-
/**
|
|
78
|
-
*
|
|
79
|
-
|
|
89
|
+
/** Heading level (1-9) for a paragraph, or undefined when it isn't a heading.
|
|
90
|
+
* DOCX marks a heading several ways, checked in priority order:
|
|
91
|
+
* 1. office-open lifts a HeadingLevel pStyle ("Heading1".."Title") into `heading`.
|
|
92
|
+
* 2. An explicit `outlineLevel` (0-8 → 1-9) — Word's outline/TOC key off this
|
|
93
|
+
* even without a heading pStyle; the Heading1-9 styles carry outlineLvl 0-8.
|
|
94
|
+
* 3. A pStyle that names a heading style: directly ("Heading7", which stays on
|
|
95
|
+
* `style` because office-open's HeadingLevel type caps at 6), by localized
|
|
96
|
+
* NAME ("heading 1"/"标题 1"), or via the `basedOn` chain (a custom style
|
|
97
|
+
* "MyTitle" basedOn="Heading1"). `heading` and `style` carry the same pStyle.
|
|
98
|
+
* `outlineLevel` is read loosely — office-open's public type omits the field
|
|
99
|
+
* even though it round-trips (w:outlineLvl) at runtime. */
|
|
100
|
+
private detectHeadingLevel;
|
|
80
101
|
/** reference → level-0 format/start, for classifying numbering paragraphs. */
|
|
81
102
|
private buildNumberingLookup;
|
|
82
103
|
/**
|
package/dist/converters/docx.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import { parseDocx as parseDocx$7, renderDocx as renderDocx$8 } from "../extensi
|
|
|
14
14
|
import { createTaskCheckbox, isTaskCheckbox, readCheckboxState } from "../extensions/task-item.mjs";
|
|
15
15
|
import { parseDocx as parseDocx$8, renderDocx as renderDocx$9 } from "../extensions/text-style.mjs";
|
|
16
16
|
import { prepareDocument } from "./prepare.mjs";
|
|
17
|
+
import { indexParagraphStyles } from "./styles.mjs";
|
|
17
18
|
import { emojis, shortcodeToEmoji } from "@tiptap/extension-emoji";
|
|
18
19
|
import { generateDocument, generateDocumentStream, generateDocumentSync, parseDocument } from "@office-open/docx";
|
|
19
20
|
import { encodeBase64 } from "@office-open/core";
|
|
@@ -119,13 +120,16 @@ var DocxManager = class {
|
|
|
119
120
|
const core = docAttrs.core ?? void 0;
|
|
120
121
|
const background = docAttrs.background ?? void 0;
|
|
121
122
|
const documentExtras = docAttrs.documentExtras ?? void 0;
|
|
123
|
+
const origNumberingConfig = docAttrs.numbering?.config ?? [];
|
|
124
|
+
const regeneratedRefs = new Set(this.numberingConfigs.map((c) => c.reference));
|
|
125
|
+
const numberingConfig = [...origNumberingConfig.filter((c) => !regeneratedRefs.has(c.reference)), ...this.numberingConfigs];
|
|
122
126
|
return {
|
|
123
127
|
sections,
|
|
124
128
|
...styles ? { styles } : {},
|
|
125
129
|
...core,
|
|
126
130
|
...background ? { background } : {},
|
|
127
131
|
...documentExtras,
|
|
128
|
-
...
|
|
132
|
+
...numberingConfig.length > 0 ? { numbering: { config: numberingConfig } } : {}
|
|
129
133
|
};
|
|
130
134
|
}
|
|
131
135
|
/** Assemble a SectionOptions from compiled children + optional layout/headers/footers. */
|
|
@@ -217,6 +221,7 @@ var DocxManager = class {
|
|
|
217
221
|
const attrs = {};
|
|
218
222
|
if (docOpts.styles) attrs.styles = docOpts.styles;
|
|
219
223
|
if (docOpts.background) attrs.background = docOpts.background;
|
|
224
|
+
if (docOpts.numbering) attrs.numbering = docOpts.numbering;
|
|
220
225
|
const core = extractCoreProperties(docOpts);
|
|
221
226
|
if (core) attrs.core = core;
|
|
222
227
|
const lastSection = sections[lastIndex];
|
|
@@ -266,6 +271,20 @@ var DocxManager = class {
|
|
|
266
271
|
case "orderedList":
|
|
267
272
|
case "taskList": return this.compileListFromNode(node, 0);
|
|
268
273
|
case "details": return this.compileDetailsNode(node);
|
|
274
|
+
case "tocField": {
|
|
275
|
+
const options = node.attrs?.options ?? {};
|
|
276
|
+
const entries = [];
|
|
277
|
+
for (const child of node.content ?? []) {
|
|
278
|
+
const compiled = this.compileSectionChild(child);
|
|
279
|
+
if (!compiled) continue;
|
|
280
|
+
if (Array.isArray(compiled)) entries.push(...compiled);
|
|
281
|
+
else entries.push(compiled);
|
|
282
|
+
}
|
|
283
|
+
return { toc: {
|
|
284
|
+
...options,
|
|
285
|
+
entries
|
|
286
|
+
} };
|
|
287
|
+
}
|
|
269
288
|
case "passthrough": {
|
|
270
289
|
const data = node.attrs?.data ?? "{}";
|
|
271
290
|
try {
|
|
@@ -443,15 +462,20 @@ var DocxManager = class {
|
|
|
443
462
|
const items = [];
|
|
444
463
|
const isOrdered = node.type === "orderedList";
|
|
445
464
|
const isTask = node.type === "taskList";
|
|
465
|
+
const numbering = node.attrs?.numbering;
|
|
446
466
|
let ordered;
|
|
447
|
-
if (isOrdered) ordered = this.registerOrderedNumbering(node);
|
|
467
|
+
if (isOrdered && !numbering) ordered = this.registerOrderedNumbering(node);
|
|
448
468
|
for (const listItem of node.content ?? []) {
|
|
449
469
|
if (listItem.type !== "listItem" && listItem.type !== "taskItem") continue;
|
|
450
470
|
const checked = Boolean(listItem.attrs?.checked);
|
|
451
471
|
for (const child of listItem.content ?? []) if (child.type === "paragraph" || child.type === "heading") {
|
|
452
472
|
const para = child.type === "heading" ? this.compileHeadingNode(child) : this.compileParagraphNode(child);
|
|
453
473
|
const paraObj = typeof para === "string" ? { text: para } : para;
|
|
454
|
-
if (
|
|
474
|
+
if (numbering) paraObj.numbering = {
|
|
475
|
+
reference: numbering,
|
|
476
|
+
level
|
|
477
|
+
};
|
|
478
|
+
else if (ordered) paraObj.numbering = {
|
|
455
479
|
reference: ordered.reference,
|
|
456
480
|
instance: ordered.instance,
|
|
457
481
|
level
|
|
@@ -544,6 +568,17 @@ var DocxManager = class {
|
|
|
544
568
|
case "columnBreak":
|
|
545
569
|
children.push({ columnBreak: true });
|
|
546
570
|
break;
|
|
571
|
+
case "tab":
|
|
572
|
+
children.push({ tab: true });
|
|
573
|
+
break;
|
|
574
|
+
case "inlinePassthrough": {
|
|
575
|
+
const data = node.attrs?.data ?? "{}";
|
|
576
|
+
try {
|
|
577
|
+
const parsed = JSON.parse(data);
|
|
578
|
+
if (parsed) children.push(parsed);
|
|
579
|
+
} catch {}
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
547
582
|
case "image": {
|
|
548
583
|
const imageRun = renderDocx$2(node);
|
|
549
584
|
if (imageRun) children.push(imageRun);
|
|
@@ -555,8 +590,18 @@ var DocxManager = class {
|
|
|
555
590
|
break;
|
|
556
591
|
}
|
|
557
592
|
case "wpsShape": {
|
|
558
|
-
const
|
|
559
|
-
|
|
593
|
+
const geometry = node.attrs?.wpsShape ?? {};
|
|
594
|
+
const body = [];
|
|
595
|
+
for (const child of node.content ?? []) {
|
|
596
|
+
const compiled = this.compileSectionChild(child);
|
|
597
|
+
if (!compiled) continue;
|
|
598
|
+
const items = Array.isArray(compiled) ? compiled : [compiled];
|
|
599
|
+
for (const it of items) if (it && typeof it === "object" && "paragraph" in it) body.push(it.paragraph);
|
|
600
|
+
}
|
|
601
|
+
children.push({ wpsShape: {
|
|
602
|
+
...geometry,
|
|
603
|
+
children: body
|
|
604
|
+
} });
|
|
560
605
|
break;
|
|
561
606
|
}
|
|
562
607
|
case "mention":
|
|
@@ -645,6 +690,7 @@ var DocxManager = class {
|
|
|
645
690
|
if (sdt.properties?.tag === "docen-details") return this.resolveDetailsSdt(sdt);
|
|
646
691
|
return this.resolvePassthrough(child);
|
|
647
692
|
}
|
|
693
|
+
if ("toc" in child) return this.resolveToc(child.toc);
|
|
648
694
|
return this.resolvePassthrough(child);
|
|
649
695
|
}
|
|
650
696
|
/** Wrap an opaque SectionChild in a passthrough atom (attrs.data = JSON). */
|
|
@@ -655,6 +701,35 @@ var DocxManager = class {
|
|
|
655
701
|
};
|
|
656
702
|
}
|
|
657
703
|
/**
|
|
704
|
+
* Resolve a table of contents into an editable `tableOfContents` container:
|
|
705
|
+
* `attrs.options` carries the field switches, `content` is the entry
|
|
706
|
+
* paragraphs. Each entry's inner HYPERLINK field has content-less runs that
|
|
707
|
+
* office-open parses as `null`; resolving the entries through
|
|
708
|
+
* `resolveSectionChild` → `resolveParagraphChildren` drops those nulls, so the
|
|
709
|
+
* TOC no longer reaches the generate path as an opaque blob of nulls (the
|
|
710
|
+
* `stringifyRunInline(null).break` crash). When `entries` is absent/empty
|
|
711
|
+
* (a fresh, unrendered TOC), keep the node valid for `content: "block+"` with
|
|
712
|
+
* a placeholder empty paragraph.
|
|
713
|
+
*/
|
|
714
|
+
resolveToc(toc) {
|
|
715
|
+
const { entries, ...options } = toc;
|
|
716
|
+
const content = [];
|
|
717
|
+
for (const entry of entries ?? []) {
|
|
718
|
+
const node = this.resolveSectionChild(entry);
|
|
719
|
+
if (!node) continue;
|
|
720
|
+
if (Array.isArray(node)) content.push(...node);
|
|
721
|
+
else content.push(node);
|
|
722
|
+
}
|
|
723
|
+
if (content.length === 0) content.push({ type: "paragraph" });
|
|
724
|
+
const node = {
|
|
725
|
+
type: "tocField",
|
|
726
|
+
content
|
|
727
|
+
};
|
|
728
|
+
const cleanOptions = cleanAttrs(options);
|
|
729
|
+
if (Object.keys(cleanOptions).length > 0) node.attrs = { options: cleanOptions };
|
|
730
|
+
return node;
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
658
733
|
* Resolve a details group-SDT: the summary-style paragraph becomes
|
|
659
734
|
* detailsSummary, the remaining blocks fold into detailsContent.
|
|
660
735
|
*/
|
|
@@ -692,20 +767,10 @@ var DocxManager = class {
|
|
|
692
767
|
const resolved = typeof opts === "string" ? { text: opts } : opts;
|
|
693
768
|
if (resolved.thematicBreak) return { type: "horizontalRule" };
|
|
694
769
|
if (resolved.style === "Code") return this.resolveCodeBlock(resolved);
|
|
695
|
-
|
|
696
|
-
if (!headingLevel && resolved.style) {
|
|
697
|
-
const name = this.styleNameOf(resolved.style);
|
|
698
|
-
if (name) {
|
|
699
|
-
const m = /^heading\s+(\d)$/i.exec(name);
|
|
700
|
-
if (m) {
|
|
701
|
-
const lvl = Number(m[1]);
|
|
702
|
-
if (lvl >= 1 && lvl <= 6) headingLevel = lvl;
|
|
703
|
-
} else if (/^title$/i.test(name)) headingLevel = 1;
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
if (headingLevel && !resolved.heading) resolved.heading = `Heading${headingLevel}`;
|
|
770
|
+
const headingLevel = this.detectHeadingLevel(resolved);
|
|
707
771
|
const nodeType = headingLevel ? "heading" : "paragraph";
|
|
708
772
|
const attrs = headingLevel ? parseDocx(resolved) : parseDocx$2(resolved);
|
|
773
|
+
if (headingLevel && attrs.level == null) attrs.level = headingLevel;
|
|
709
774
|
const content = this.resolveInlineContent(resolved);
|
|
710
775
|
const cleanAttrsObj = cleanAttrs(attrs);
|
|
711
776
|
const node = { type: nodeType };
|
|
@@ -713,10 +778,38 @@ var DocxManager = class {
|
|
|
713
778
|
if (content.length > 0) node.content = content;
|
|
714
779
|
return node;
|
|
715
780
|
}
|
|
716
|
-
/**
|
|
717
|
-
*
|
|
718
|
-
|
|
719
|
-
|
|
781
|
+
/** Heading level (1-9) for a paragraph, or undefined when it isn't a heading.
|
|
782
|
+
* DOCX marks a heading several ways, checked in priority order:
|
|
783
|
+
* 1. office-open lifts a HeadingLevel pStyle ("Heading1".."Title") into `heading`.
|
|
784
|
+
* 2. An explicit `outlineLevel` (0-8 → 1-9) — Word's outline/TOC key off this
|
|
785
|
+
* even without a heading pStyle; the Heading1-9 styles carry outlineLvl 0-8.
|
|
786
|
+
* 3. A pStyle that names a heading style: directly ("Heading7", which stays on
|
|
787
|
+
* `style` because office-open's HeadingLevel type caps at 6), by localized
|
|
788
|
+
* NAME ("heading 1"/"标题 1"), or via the `basedOn` chain (a custom style
|
|
789
|
+
* "MyTitle" basedOn="Heading1"). `heading` and `style` carry the same pStyle.
|
|
790
|
+
* `outlineLevel` is read loosely — office-open's public type omits the field
|
|
791
|
+
* even though it round-trips (w:outlineLvl) at runtime. */
|
|
792
|
+
detectHeadingLevel(resolved) {
|
|
793
|
+
if (resolved.heading) {
|
|
794
|
+
const lvl = HEADING_LEVEL_MAP[resolved.heading];
|
|
795
|
+
if (lvl) return lvl;
|
|
796
|
+
}
|
|
797
|
+
const outline = resolved.outlineLevel;
|
|
798
|
+
if (typeof outline === "number" && outline >= 0 && outline <= 8) return outline + 1;
|
|
799
|
+
const styleId = resolved.style;
|
|
800
|
+
if (!styleId || !this.resolveStyles) return void 0;
|
|
801
|
+
const byId = indexParagraphStyles(this.resolveStyles);
|
|
802
|
+
const visited = /* @__PURE__ */ new Set();
|
|
803
|
+
let curId = styleId;
|
|
804
|
+
while (curId && !visited.has(curId)) {
|
|
805
|
+
visited.add(curId);
|
|
806
|
+
if (HEADING_LEVEL_MAP[curId]) return HEADING_LEVEL_MAP[curId];
|
|
807
|
+
const style = byId.get(curId);
|
|
808
|
+
if (!style) break;
|
|
809
|
+
const lvl = headingLevelFromName(style.name);
|
|
810
|
+
if (lvl) return lvl;
|
|
811
|
+
curId = style.basedOn ?? void 0;
|
|
812
|
+
}
|
|
720
813
|
}
|
|
721
814
|
/** reference → level-0 format/start, for classifying numbering paragraphs. */
|
|
722
815
|
buildNumberingLookup(docOpts) {
|
|
@@ -795,10 +888,7 @@ var DocxManager = class {
|
|
|
795
888
|
if (cfg && cfg.format && cfg.format !== "bullet") {
|
|
796
889
|
kind = "ordered";
|
|
797
890
|
start = cfg.start;
|
|
798
|
-
} else
|
|
799
|
-
kind = "bullet";
|
|
800
|
-
reference = void 0;
|
|
801
|
-
}
|
|
891
|
+
} else kind = "bullet";
|
|
802
892
|
} else if (bullet) {
|
|
803
893
|
kind = "bullet";
|
|
804
894
|
level = bullet.level ?? 0;
|
|
@@ -847,7 +937,10 @@ var DocxManager = class {
|
|
|
847
937
|
type: listType,
|
|
848
938
|
content: [newItem]
|
|
849
939
|
};
|
|
850
|
-
|
|
940
|
+
const listAttrs = {};
|
|
941
|
+
if (listType === "orderedList" && info.level === 0 && typeof info.start === "number" && info.start !== 1) listAttrs.start = info.start;
|
|
942
|
+
if (info.reference) listAttrs.numbering = info.reference;
|
|
943
|
+
if (Object.keys(listAttrs).length > 0) newList.attrs = listAttrs;
|
|
851
944
|
if (top) top.currentItem.content.push(newList);
|
|
852
945
|
else topLevel.push(newList);
|
|
853
946
|
stack.push({
|
|
@@ -909,9 +1002,10 @@ var DocxManager = class {
|
|
|
909
1002
|
*/
|
|
910
1003
|
resolveListItemParagraph(para, info) {
|
|
911
1004
|
const resolved = typeof para === "string" ? { text: para } : para;
|
|
912
|
-
const headingLevel =
|
|
1005
|
+
const headingLevel = this.detectHeadingLevel(resolved);
|
|
913
1006
|
const nodeType = headingLevel ? "heading" : "paragraph";
|
|
914
1007
|
const attrs = headingLevel ? parseDocx(resolved) : parseDocx$2(resolved);
|
|
1008
|
+
if (headingLevel && attrs.level == null) attrs.level = headingLevel;
|
|
915
1009
|
const stripped = info.kind === "task" ? this.stripTaskCheckbox(resolved) : resolved;
|
|
916
1010
|
const content = this.resolveInlineContent(stripped);
|
|
917
1011
|
const node = { type: nodeType };
|
|
@@ -1072,21 +1166,55 @@ var DocxManager = class {
|
|
|
1072
1166
|
return nodes;
|
|
1073
1167
|
}
|
|
1074
1168
|
resolveParagraphChild(child) {
|
|
1169
|
+
if ("tab" in child) return { type: "tab" };
|
|
1075
1170
|
if ("text" in child || "children" in child || "break" in child) return this.resolveRun(child);
|
|
1076
1171
|
if ("image" in child) return this.resolveImage(child.image);
|
|
1077
1172
|
if ("wpgGroup" in child) return {
|
|
1078
1173
|
type: "wpgGroup",
|
|
1079
1174
|
attrs: { wpgGroup: child.wpgGroup }
|
|
1080
1175
|
};
|
|
1081
|
-
if ("wpsShape" in child)
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1176
|
+
if ("wpsShape" in child) {
|
|
1177
|
+
const ws = child.wpsShape;
|
|
1178
|
+
const content = [];
|
|
1179
|
+
if (ws?.children) for (const para of ws.children) {
|
|
1180
|
+
if (typeof para !== "object" || para === null) {
|
|
1181
|
+
const node = this.resolveParagraph(para);
|
|
1182
|
+
if (node) content.push(node);
|
|
1183
|
+
continue;
|
|
1184
|
+
}
|
|
1185
|
+
const defRPr = para.run ?? {};
|
|
1186
|
+
const children = Array.isArray(para.children) ? para.children.map((c) => typeof c !== "object" || c === null ? {
|
|
1187
|
+
...defRPr,
|
|
1188
|
+
text: c
|
|
1189
|
+
} : {
|
|
1190
|
+
...defRPr,
|
|
1191
|
+
...c
|
|
1192
|
+
}) : void 0;
|
|
1193
|
+
const node = this.resolveParagraph({
|
|
1194
|
+
...para,
|
|
1195
|
+
run: void 0,
|
|
1196
|
+
...children ? { children } : {}
|
|
1197
|
+
});
|
|
1198
|
+
if (node) content.push(node);
|
|
1199
|
+
}
|
|
1200
|
+
if (content.length === 0) content.push({ type: "paragraph" });
|
|
1201
|
+
const { children: _omit, ...geometry } = ws ?? {};
|
|
1202
|
+
const node = {
|
|
1203
|
+
type: "wpsShape",
|
|
1204
|
+
content
|
|
1205
|
+
};
|
|
1206
|
+
const cleanGeometry = cleanAttrs(geometry);
|
|
1207
|
+
if (Object.keys(cleanGeometry).length > 0) node.attrs = { wpsShape: cleanGeometry };
|
|
1208
|
+
return node;
|
|
1209
|
+
}
|
|
1085
1210
|
if ("sdt" in child) return this.resolveInlineSdt(child);
|
|
1086
1211
|
if ("hyperlink" in child) return this.resolveHyperlink(child.hyperlink);
|
|
1087
1212
|
if ("pageBreak" in child) return { type: "pageBreak" };
|
|
1088
1213
|
if ("columnBreak" in child) return { type: "columnBreak" };
|
|
1089
|
-
return
|
|
1214
|
+
return {
|
|
1215
|
+
type: "inlinePassthrough",
|
|
1216
|
+
attrs: { data: JSON.stringify(child) }
|
|
1217
|
+
};
|
|
1090
1218
|
}
|
|
1091
1219
|
/** Resolve an inline SDT (mention carrier; other inline SDTs unsupported). */
|
|
1092
1220
|
resolveInlineSdt(child) {
|
|
@@ -1106,15 +1234,48 @@ var DocxManager = class {
|
|
|
1106
1234
|
if (opts.break && opts.text === void 0 && !opts.children) return { type: "hardBreak" };
|
|
1107
1235
|
const text = opts.text;
|
|
1108
1236
|
if (text === void 0 && !opts.children) return null;
|
|
1109
|
-
if (opts.children
|
|
1110
|
-
const
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1237
|
+
if (opts.children) {
|
|
1238
|
+
const marks = this.resolveMarks(opts);
|
|
1239
|
+
const nodes = [];
|
|
1240
|
+
let parts = [];
|
|
1241
|
+
const flushText = () => {
|
|
1242
|
+
if (parts.length > 0) {
|
|
1243
|
+
const node = {
|
|
1244
|
+
type: "text",
|
|
1245
|
+
text: parts.join("")
|
|
1246
|
+
};
|
|
1247
|
+
if (marks) node.marks = marks;
|
|
1248
|
+
nodes.push(node);
|
|
1249
|
+
parts = [];
|
|
1250
|
+
}
|
|
1117
1251
|
};
|
|
1252
|
+
for (const c of opts.children) if (typeof c === "string") parts.push(c);
|
|
1253
|
+
else if (c && typeof c === "object") {
|
|
1254
|
+
if ("pageBreak" in c) {
|
|
1255
|
+
flushText();
|
|
1256
|
+
nodes.push({ type: "pageBreak" });
|
|
1257
|
+
} else if ("columnBreak" in c) {
|
|
1258
|
+
flushText();
|
|
1259
|
+
nodes.push({ type: "columnBreak" });
|
|
1260
|
+
} else if ("break" in c) {
|
|
1261
|
+
flushText();
|
|
1262
|
+
nodes.push({ type: "hardBreak" });
|
|
1263
|
+
} else if ("tab" in c) {
|
|
1264
|
+
flushText();
|
|
1265
|
+
nodes.push({ type: "tab" });
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
flushText();
|
|
1269
|
+
if (text !== void 0) {
|
|
1270
|
+
const node = {
|
|
1271
|
+
type: "text",
|
|
1272
|
+
text,
|
|
1273
|
+
marks
|
|
1274
|
+
};
|
|
1275
|
+
nodes.push(node);
|
|
1276
|
+
}
|
|
1277
|
+
if (nodes.length === 0) return null;
|
|
1278
|
+
return nodes.length === 1 ? nodes[0] : nodes;
|
|
1118
1279
|
}
|
|
1119
1280
|
return {
|
|
1120
1281
|
type: "text",
|
|
@@ -1172,7 +1333,7 @@ var DocxManager = class {
|
|
|
1172
1333
|
type: "link",
|
|
1173
1334
|
attrs: {
|
|
1174
1335
|
href,
|
|
1175
|
-
target: "_blank",
|
|
1336
|
+
target: href.startsWith("#") ? null : "_blank",
|
|
1176
1337
|
rel: "noopener noreferrer nofollow",
|
|
1177
1338
|
class: null,
|
|
1178
1339
|
title: hyperlink.tooltip ?? null
|
|
@@ -1190,8 +1351,23 @@ const HEADING_LEVEL_MAP = {
|
|
|
1190
1351
|
Heading4: 4,
|
|
1191
1352
|
Heading5: 5,
|
|
1192
1353
|
Heading6: 6,
|
|
1354
|
+
Heading7: 7,
|
|
1355
|
+
Heading8: 8,
|
|
1356
|
+
Heading9: 9,
|
|
1193
1357
|
Title: 1
|
|
1194
1358
|
};
|
|
1359
|
+
/** Heading level (1-9) from a localized style NAME: "heading 1"/"标题 1" → 1,
|
|
1360
|
+
* "title" → 1. office-open's built-in names are English ("heading 1"), but
|
|
1361
|
+
* zh-CN Word labels the same styles "标题 1"; both map to the same level. */
|
|
1362
|
+
function headingLevelFromName(name) {
|
|
1363
|
+
if (!name) return void 0;
|
|
1364
|
+
const m = /^heading\s+(\d)$/i.exec(name) ?? /^标题\s*(\d)$/.exec(name);
|
|
1365
|
+
if (m) {
|
|
1366
|
+
const lvl = Number(m[1]);
|
|
1367
|
+
if (lvl >= 1 && lvl <= 9) return lvl;
|
|
1368
|
+
}
|
|
1369
|
+
return /^title$/i.test(name) ? 1 : void 0;
|
|
1370
|
+
}
|
|
1195
1371
|
const defaultManager = new DocxManager();
|
|
1196
1372
|
/**
|
|
1197
1373
|
* Parse a DOCX file into Tiptap JSON (runtime model).
|
package/dist/converters/html.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as docxExtensions } from "../core-BnF8XhVE.mjs";
|
|
2
2
|
import { sectionLinePitchCss, sectionMarginCss } from "../extensions/utils.mjs";
|
|
3
3
|
import { getSchema } from "@tiptap/core";
|
|
4
4
|
import { encodeBase64 } from "@office-open/core";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as docxExtensions } from "../core-BnF8XhVE.mjs";
|
|
2
2
|
import { Markdown, MarkdownManager } from "@tiptap/markdown";
|
|
3
3
|
//#region src/converters/markdown.ts
|
|
4
4
|
const markdownManager = new MarkdownManager({ extensions: [...docxExtensions, Markdown] });
|
|
@@ -67,15 +67,12 @@ async function prepareDocument(json, steps = DEFAULT_STEPS) {
|
|
|
67
67
|
}
|
|
68
68
|
async function toDataUrl(src, handler) {
|
|
69
69
|
const data = await handler(src);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
svg: "image/svg+xml",
|
|
77
|
-
webp: "image/webp"
|
|
78
|
-
}[src.split(".").pop()?.toLowerCase() ?? "png"] ?? "image/png"};base64,${encodeBase64(data)}`;
|
|
70
|
+
let mime = "image/png";
|
|
71
|
+
try {
|
|
72
|
+
const type = imageMeta(data).type;
|
|
73
|
+
if (type) mime = type === "jpg" ? "image/jpeg" : `image/${type}`;
|
|
74
|
+
} catch {}
|
|
75
|
+
return `data:${mime};base64,${encodeBase64(data)}`;
|
|
79
76
|
}
|
|
80
77
|
async function walkImages(node, handler) {
|
|
81
78
|
if (node.type === "image" && node.attrs) {
|
|
@@ -2,6 +2,11 @@ import { JSONContent } from "@tiptap/core";
|
|
|
2
2
|
import { StylesOptions } from "@office-open/docx";
|
|
3
3
|
|
|
4
4
|
//#region src/converters/styles.d.ts
|
|
5
|
+
/** A named style entry as office-open models it: BaseParagraphStyleOptions or
|
|
6
|
+
* BaseCharacterStyleOptions (both extend the internal StyleOptions, carrying
|
|
7
|
+
* name/uiPriority/quickFormat). Derived from the public StylesOptions — not
|
|
8
|
+
* imported — because StyleOptions is not a public export of @office-open/docx. */
|
|
9
|
+
type StyleEntry = NonNullable<StylesOptions["paragraphStyles"]>[number] | NonNullable<StylesOptions["characterStyles"]>[number];
|
|
5
10
|
/** The styleId of the document's default paragraph style (`w:default="1"`
|
|
6
11
|
* type="paragraph") — the implicit style applied to every paragraph WITHOUT an
|
|
7
12
|
* explicit pStyle. OOXML renders a pStyle-less paragraph as this style (usually
|
|
@@ -29,6 +34,7 @@ declare function defaultParagraphStyleId(styles: StylesOptions | null | undefine
|
|
|
29
34
|
* carries its full ancestor chain rather than relying on source order.
|
|
30
35
|
*/
|
|
31
36
|
declare function stylesToCss(styles: StylesOptions | null | undefined, scope: string): string;
|
|
37
|
+
declare function indexParagraphStyles(styles: StylesOptions): Map<string, StyleEntry>;
|
|
32
38
|
/** A gallery-ready paragraph-style entry for the Styles combobox. */
|
|
33
39
|
interface QuickStyleEntry {
|
|
34
40
|
id: string;
|
|
@@ -82,4 +88,4 @@ declare function effectiveRunProps(styles: StylesOptions | null | undefined, sty
|
|
|
82
88
|
*/
|
|
83
89
|
declare function inlineStyles(json: JSONContent, styles?: StylesOptions | null): JSONContent;
|
|
84
90
|
//#endregion
|
|
85
|
-
export { QuickStyleEntry, type StylesOptions, defaultParagraphStyleId, effectiveRunProps, inlineStyles, quickStyles, stylesToCss };
|
|
91
|
+
export { QuickStyleEntry, StyleEntry, type StylesOptions, defaultParagraphStyleId, effectiveRunProps, indexParagraphStyles, inlineStyles, quickStyles, stylesToCss };
|
|
@@ -85,7 +85,10 @@ function stylesToCss(styles, scope) {
|
|
|
85
85
|
* (key → pStyle id via pStyleIdFromKey). `document` is docDefaults, not a
|
|
86
86
|
* named style, so it is excluded. A built-in that also appears in
|
|
87
87
|
* paragraphStyles is deduped by id (paragraphStyles wins on insertion order). */
|
|
88
|
+
const styleIndexCache = /* @__PURE__ */ new WeakMap();
|
|
88
89
|
function indexParagraphStyles(styles) {
|
|
90
|
+
const cached = styleIndexCache.get(styles);
|
|
91
|
+
if (cached) return cached;
|
|
89
92
|
const byId = /* @__PURE__ */ new Map();
|
|
90
93
|
for (const ps of styles.paragraphStyles ?? []) byId.set(ps.id, ps);
|
|
91
94
|
const defaults = styles.default;
|
|
@@ -93,14 +96,34 @@ function indexParagraphStyles(styles) {
|
|
|
93
96
|
if (key === "document" || !style) continue;
|
|
94
97
|
byId.set(pStyleIdFromKey(key), style);
|
|
95
98
|
}
|
|
99
|
+
styleIndexCache.set(styles, byId);
|
|
96
100
|
return byId;
|
|
97
101
|
}
|
|
102
|
+
/** Whether `v` is a plain object — an OOXML property group (spacing/indent/
|
|
103
|
+
* border/shading/font) that merges key by key — as opposed to an array
|
|
104
|
+
* (tabStops) or scalar, which replace. */
|
|
105
|
+
function isPlainObject(v) {
|
|
106
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
107
|
+
}
|
|
108
|
+
/** Deep-merge `source` into `target` (mutates target) per the OOXML `basedOn`
|
|
109
|
+
* model: nested property groups merge key by key (a child's spacing.before
|
|
110
|
+
* merges with, not replaces, the parent's spacing.line); arrays and scalars
|
|
111
|
+
* replace. Nullish source values are skipped so an unset child key doesn't
|
|
112
|
+
* clobber an inherited value. */
|
|
113
|
+
function deepMergeInto(target, source) {
|
|
114
|
+
for (const [key, srcVal] of Object.entries(source)) {
|
|
115
|
+
if (srcVal === null || srcVal === void 0) continue;
|
|
116
|
+
const tgtVal = target[key];
|
|
117
|
+
target[key] = isPlainObject(srcVal) && isPlainObject(tgtVal) ? deepMergeInto({ ...tgtVal }, srcVal) : isPlainObject(srcVal) ? { ...srcVal } : srcVal;
|
|
118
|
+
}
|
|
119
|
+
return target;
|
|
120
|
+
}
|
|
98
121
|
/** Merge a paragraph style's run/paragraph properties with its `basedOn` chain
|
|
99
|
-
* (root first, child overrides per-property) — the OOXML inheritance model.
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
122
|
+
* (root first, child overrides per-property) — the OOXML inheritance model.
|
|
123
|
+
* Nested property groups (spacing/indent/border/font) merge key by key; arrays
|
|
124
|
+
* and scalars replace. Shared by stylesToCss (rendering) and effectiveRunProps
|
|
125
|
+
* (the caret resolver) so the gallery box and the rendered page resolve
|
|
126
|
+
* identical values. */
|
|
104
127
|
function mergeStyleChain(byId, styleId) {
|
|
105
128
|
const chain = [];
|
|
106
129
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -116,8 +139,8 @@ function mergeStyleChain(byId, styleId) {
|
|
|
116
139
|
const paragraph = {};
|
|
117
140
|
for (const style of chain) {
|
|
118
141
|
const s = style;
|
|
119
|
-
if (s.run)
|
|
120
|
-
if (s.paragraph)
|
|
142
|
+
if (s.run) deepMergeInto(run, s.run);
|
|
143
|
+
if (s.paragraph) deepMergeInto(paragraph, s.paragraph);
|
|
121
144
|
}
|
|
122
145
|
return {
|
|
123
146
|
run,
|
|
@@ -282,4 +305,4 @@ function inlineStyles(json, styles) {
|
|
|
282
305
|
return resolveNode(json, indexParagraphStyles(docStyles), indexCharacterRunStyles(docStyles));
|
|
283
306
|
}
|
|
284
307
|
//#endregion
|
|
285
|
-
export { defaultParagraphStyleId, effectiveRunProps, inlineStyles, quickStyles, stylesToCss };
|
|
308
|
+
export { defaultParagraphStyleId, effectiveRunProps, indexParagraphStyles, inlineStyles, quickStyles, stylesToCss };
|