@portone/docx-editor 0.3.0 → 0.5.0
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/CHANGELOG.md +205 -0
- package/assets/editor.png +0 -0
- package/dist/DocxEditor.d.ts +1 -1
- package/dist/DocxEditor.js +45 -25
- package/dist/core.d.ts +1 -1
- package/dist/docx/cloning.js +4 -9
- package/dist/docx/commentOnlyChange.d.ts +5 -3
- package/dist/docx/comments/grammar.d.ts +27 -10
- package/dist/docx/comments/grammar.js +18 -67
- package/dist/docx/comments/model.d.ts +5 -6
- package/dist/docx/comments/model.js +0 -9
- package/dist/docx/comments/parts.d.ts +16 -10
- package/dist/docx/comments/parts.js +65 -13
- package/dist/docx/comments/people.d.ts +11 -3
- package/dist/docx/comments/people.js +20 -6
- package/dist/docx/comments/policy.js +7 -1
- package/dist/docx/comments/reading.d.ts +4 -2
- package/dist/docx/comments/reading.js +1 -14
- package/dist/docx/comments/writing.js +78 -35
- package/dist/docx/exportDocx.js +30 -18
- package/dist/docx/exportRefs.d.ts +8 -3
- package/dist/docx/exportRefs.js +2 -1
- package/dist/docx/fidelity.js +24 -10
- package/dist/docx/fields.d.ts +42 -0
- package/dist/docx/fields.js +76 -0
- package/dist/docx/formatting/attrs.d.ts +20 -2
- package/dist/docx/formatting/attrs.js +57 -2
- package/dist/docx/formatting/context.d.ts +10 -2
- package/dist/docx/formatting/context.js +23 -5
- package/dist/docx/formatting/resolve.d.ts +10 -4
- package/dist/docx/formatting/resolve.js +31 -11
- package/dist/docx/formatting/styles.d.ts +25 -2
- package/dist/docx/formatting/styles.js +84 -3
- package/dist/docx/headersFooters.d.ts +89 -20
- package/dist/docx/headersFooters.js +163 -172
- package/dist/docx/hyperlink.d.ts +14 -4
- package/dist/docx/hyperlink.js +4 -1
- package/dist/docx/importDocx.d.ts +1 -1
- package/dist/docx/importDocx.js +135 -74
- package/dist/docx/importParagraph.d.ts +16 -4
- package/dist/docx/importParagraph.js +87 -93
- package/dist/docx/importPolicy.d.ts +70 -0
- package/dist/docx/importPolicy.js +190 -0
- package/dist/docx/importPreserved.d.ts +30 -0
- package/dist/docx/importPreserved.js +54 -0
- package/dist/docx/importTable.d.ts +2 -2
- package/dist/docx/importTable.js +107 -66
- package/dist/docx/invariants.js +42 -31
- package/dist/docx/newLists.d.ts +30 -0
- package/dist/docx/newLists.js +24 -1
- package/dist/docx/notes.d.ts +6 -1
- package/dist/docx/notes.js +6 -16
- package/dist/docx/numberingPlanner.d.ts +6 -3
- package/dist/docx/numberingPlanner.js +38 -7
- package/dist/docx/pageGeometry.d.ts +5 -14
- package/dist/docx/pageGeometry.js +0 -12
- package/dist/docx/protectionPolicy.d.ts +13 -5
- package/dist/docx/protectionPolicy.js +32 -22
- package/dist/docx/scan.d.ts +18 -8
- package/dist/docx/scan.js +17 -11
- package/dist/docx/sections.d.ts +126 -0
- package/dist/docx/sections.js +207 -0
- package/dist/docx/serializeBlock.d.ts +13 -4
- package/dist/docx/serializeBlock.js +16 -18
- package/dist/docx/serializeParagraph.d.ts +4 -0
- package/dist/docx/serializeParagraph.js +1 -0
- package/dist/docx/serializePreserved.d.ts +14 -0
- package/dist/docx/serializePreserved.js +24 -0
- package/dist/docx/serializeStory.d.ts +17 -0
- package/dist/docx/serializeStory.js +17 -0
- package/dist/docx/serializeTable.js +10 -13
- package/dist/docx/session.d.ts +24 -7
- package/dist/docx/session.js +37 -6
- package/dist/docx/story.d.ts +140 -0
- package/dist/docx/story.js +237 -0
- package/dist/docx/storyProjection.js +6 -1
- package/dist/docx/tableFormatting/conditions.d.ts +76 -0
- package/dist/docx/tableFormatting/conditions.js +196 -0
- package/dist/docx/tableFormatting/editing.d.ts +7 -3
- package/dist/docx/tableFormatting/editing.js +4 -5
- package/dist/docx/tableFormatting/reading.d.ts +49 -19
- package/dist/docx/tableFormatting/reading.js +109 -35
- package/dist/docx/tableFormatting.d.ts +1 -0
- package/dist/docx/tableFormatting.js +1 -0
- package/dist/docx/tableTemplate.js +12 -12
- package/dist/editor/clipboard/htmlReader.d.ts +30 -0
- package/dist/editor/clipboard/htmlReader.js +302 -0
- package/dist/editor/clipboard/internalChannel.d.ts +57 -0
- package/dist/editor/clipboard/internalChannel.js +58 -0
- package/dist/editor/clipboard/normalizers.d.ts +99 -0
- package/dist/editor/clipboard/normalizers.js +199 -0
- package/dist/editor/clipboard/parser.d.ts +34 -0
- package/dist/editor/clipboard/parser.js +58 -0
- package/dist/editor/clipboard/plugin.d.ts +21 -0
- package/dist/editor/clipboard/plugin.js +320 -0
- package/dist/editor/clipboard/readContext.d.ts +24 -0
- package/dist/editor/clipboard/readContext.js +19 -0
- package/dist/editor/clipboard/readers.d.ts +33 -0
- package/dist/editor/clipboard/readers.js +37 -0
- package/dist/editor/commands/comments/editing.d.ts +26 -6
- package/dist/editor/commands/comments/editing.js +175 -105
- package/dist/editor/commands/comments/model.d.ts +34 -17
- package/dist/editor/commands/comments/model.js +11 -0
- package/dist/editor/commands/comments/reading.d.ts +5 -3
- package/dist/editor/commands/comments/reading.js +8 -58
- package/dist/editor/commands/formatting/propertyCommands.js +6 -1
- package/dist/editor/commands/index.d.ts +2 -2
- package/dist/editor/commands/index.js +2 -0
- package/dist/editor/commands/listCommands.d.ts +12 -3
- package/dist/editor/commands/listCommands.js +76 -30
- package/dist/editor/commands/noteQueries.d.ts +22 -5
- package/dist/editor/commands/noteQueries.js +34 -6
- package/dist/editor/commands/paragraphCommands.js +6 -1
- package/dist/editor/createEditor.js +14 -4
- package/dist/editor/documentStyles.d.ts +26 -4
- package/dist/editor/documentStyles.js +9 -4
- package/dist/editor/editorDocument.d.ts +7 -6
- package/dist/editor/editorDocument.js +18 -6
- package/dist/editor/imageFiles.d.ts +2 -2
- package/dist/editor/imageFiles.js +2 -0
- package/dist/editor/insertTable.d.ts +1 -1
- package/dist/editor/insertTable.js +2 -2
- package/dist/editor/paragraphEdits.d.ts +16 -1
- package/dist/editor/paragraphEdits.js +13 -5
- package/dist/editor/paragraphPlacement.d.ts +11 -0
- package/dist/editor/paragraphPlacement.js +18 -0
- package/dist/editor/plainText.d.ts +12 -0
- package/dist/editor/plainText.js +2 -1
- package/dist/editor/plugins/columnResize.js +6 -3
- package/dist/editor/plugins/commentComposer.d.ts +28 -0
- package/dist/editor/plugins/commentComposer.js +46 -0
- package/dist/editor/plugins/commentDecorations.d.ts +29 -2
- package/dist/editor/plugins/commentDecorations.js +89 -15
- package/dist/editor/plugins/documentProjection.d.ts +31 -0
- package/dist/editor/plugins/documentProjection.js +22 -0
- package/dist/editor/plugins/imagePaste.js +20 -10
- package/dist/editor/plugins/linkPanel.d.ts +7 -3
- package/dist/editor/plugins/linkPanel.js +13 -27
- package/dist/editor/plugins/numberingDecorations.d.ts +10 -4
- package/dist/editor/plugins/numberingDecorations.js +43 -2
- package/dist/editor/plugins/panelState.d.ts +67 -0
- package/dist/editor/plugins/panelState.js +54 -0
- package/dist/editor/plugins/paragraphDisplay.js +9 -3
- package/dist/editor/plugins/tableContextMenu.d.ts +1 -1
- package/dist/editor/plugins/tableContextMenu.js +30 -47
- package/dist/editor/plugins/tableDisplay.js +19 -5
- package/dist/editor/plugins/textContextMenu.d.ts +1 -1
- package/dist/editor/plugins/textContextMenu.js +30 -47
- package/dist/model/format.d.ts +50 -0
- package/dist/model/format.js +65 -0
- package/dist/numbering/listRegistry.d.ts +37 -0
- package/dist/numbering/listRegistry.js +112 -0
- package/dist/numbering/listTemplate.d.ts +26 -17
- package/dist/numbering/listTemplate.js +66 -20
- package/dist/numbering/markers.d.ts +15 -6
- package/dist/numbering/markers.js +26 -59
- package/dist/numbering/parseNumbering.d.ts +70 -5
- package/dist/numbering/parseNumbering.js +100 -26
- package/dist/numbering/spellers.d.ts +15 -0
- package/dist/numbering/spellers.js +96 -0
- package/dist/numbering/writeNumbering.d.ts +5 -3
- package/dist/numbering/writeNumbering.js +15 -15
- package/dist/ooxml/conformance.d.ts +28 -0
- package/dist/ooxml/conformance.js +20 -0
- package/dist/ooxml/errors.d.ts +5 -5
- package/dist/ooxml/fragment.d.ts +11 -0
- package/dist/ooxml/fragment.js +14 -4
- package/dist/ooxml/image.d.ts +3 -4
- package/dist/ooxml/image.js +50 -5
- package/dist/ooxml/names.d.ts +15 -0
- package/dist/ooxml/names.js +10 -1
- package/dist/ooxml/partSplice.d.ts +1 -1
- package/dist/ooxml/partSplice.js +10 -3
- package/dist/ooxml/props.d.ts +8 -0
- package/dist/ooxml/props.js +14 -4
- package/dist/ooxml/rangeMarkers.d.ts +5 -0
- package/dist/ooxml/rangeMarkers.js +24 -0
- package/dist/ooxml/xml.d.ts +8 -9
- package/dist/ooxml/xml.js +6 -6
- package/dist/page/PageGuides.d.ts +8 -4
- package/dist/page/PageGuides.js +13 -37
- package/dist/page/pageLayout.d.ts +62 -13
- package/dist/page/pageLayout.js +91 -37
- package/dist/page/usePageLayout.d.ts +14 -11
- package/dist/page/usePageLayout.js +21 -29
- package/dist/schema/attrRoles.js +44 -19
- package/dist/schema/docxSchema.d.ts +9 -1
- package/dist/schema/docxSchema.js +220 -139
- package/dist/schema/editGuard.d.ts +1 -1
- package/dist/schema/guards.js +2 -2
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/preservedFragments.d.ts +12 -0
- package/dist/schema/preservedFragments.js +38 -0
- package/dist/schema/preservedGuards.d.ts +9 -7
- package/dist/schema/preservedGuards.js +112 -11
- package/dist/schema/protection.d.ts +34 -5
- package/dist/schema/protection.js +42 -14
- package/dist/schema/rendering.js +3 -1
- package/dist/schema/stories.d.ts +49 -0
- package/dist/schema/stories.js +78 -0
- package/dist/styles/classNames.d.ts +16 -7
- package/dist/styles/classNames.js +16 -7
- package/dist/styles.css +90 -30
- package/dist/table/cellFormatting.js +1 -2
- package/dist/table/gridBorders.d.ts +11 -15
- package/dist/table/gridBorders.js +32 -18
- package/dist/table/resize.d.ts +5 -3
- package/dist/ui/CommentsPanel.d.ts +1 -3
- package/dist/ui/CommentsPanel.js +24 -15
- package/dist/ui/NotesPanel.js +2 -2
- package/dist/ui/TextMenu.d.ts +1 -3
- package/dist/ui/TextMenu.js +10 -6
- package/dist/ui/comments/CommentComposer.d.ts +6 -1
- package/dist/ui/comments/CommentComposer.js +26 -3
- package/package.json +1 -1
- package/dist/editor/externalClipboard.d.ts +0 -11
- package/dist/editor/externalClipboard.js +0 -456
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// src/numbering/listRegistry.ts
|
|
2
|
+
import { ST_SignedTwipsMeasure, ST_TwipsMeasure } from "../ooxml/simpleTypes.js";
|
|
3
|
+
import { MAX_ILVL } from "./listTemplate.js";
|
|
4
|
+
import {
|
|
5
|
+
LEVEL_ALIGNS,
|
|
6
|
+
LEVEL_SUFFIXES
|
|
7
|
+
} from "./parseNumbering.js";
|
|
8
|
+
import { isNumberFormat } from "./spellers.js";
|
|
9
|
+
var NO_NEW_LISTS = /* @__PURE__ */ new Map();
|
|
10
|
+
var NEW_LISTS_ATTR = "newLists";
|
|
11
|
+
function isRecord(value) {
|
|
12
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
|
+
}
|
|
14
|
+
function toLevelIndent(value) {
|
|
15
|
+
if (!isRecord(value)) return null;
|
|
16
|
+
const { startTwips, endTwips, hangingTwips, firstLineTwips } = value;
|
|
17
|
+
const signed = (slot) => slot === null || typeof slot === "number" && ST_SignedTwipsMeasure.format(slot) !== null;
|
|
18
|
+
const unsigned = (slot) => slot === null || typeof slot === "number" && ST_TwipsMeasure.format(slot) !== null;
|
|
19
|
+
if (!signed(startTwips) || !signed(endTwips) || !unsigned(hangingTwips) || !unsigned(firstLineTwips))
|
|
20
|
+
return null;
|
|
21
|
+
return { startTwips, endTwips, hangingTwips, firstLineTwips };
|
|
22
|
+
}
|
|
23
|
+
function levelValue(ilvl, level) {
|
|
24
|
+
return {
|
|
25
|
+
ilvl,
|
|
26
|
+
format: level.format,
|
|
27
|
+
text: level.text,
|
|
28
|
+
start: level.start,
|
|
29
|
+
indent: level.indent,
|
|
30
|
+
restartAfterLevel: level.restartAfterLevel,
|
|
31
|
+
legal: level.legal,
|
|
32
|
+
suffix: level.suffix,
|
|
33
|
+
align: level.align
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function toLevel(value) {
|
|
37
|
+
if (!isRecord(value)) return null;
|
|
38
|
+
const { ilvl, format, text, start } = value;
|
|
39
|
+
if (typeof ilvl !== "number" || !Number.isSafeInteger(ilvl) || ilvl < 0 || ilvl > MAX_ILVL)
|
|
40
|
+
return null;
|
|
41
|
+
if (typeof format !== "string" || !isNumberFormat(format)) return null;
|
|
42
|
+
if (typeof text !== "string") return null;
|
|
43
|
+
if (typeof start !== "number" || !Number.isSafeInteger(start)) return null;
|
|
44
|
+
const suffix = LEVEL_SUFFIXES.find((known) => known === value.suffix);
|
|
45
|
+
const align = LEVEL_ALIGNS.find((known) => known === value.align);
|
|
46
|
+
if (suffix === void 0 || align === void 0) return null;
|
|
47
|
+
const restartAfterLevel = value.restartAfterLevel;
|
|
48
|
+
if (restartAfterLevel !== null && (typeof restartAfterLevel !== "number" || !Number.isSafeInteger(restartAfterLevel) || restartAfterLevel < -1 || restartAfterLevel >= MAX_ILVL))
|
|
49
|
+
return null;
|
|
50
|
+
if (typeof value.legal !== "boolean") return null;
|
|
51
|
+
if (value.run !== void 0 && value.run !== null) return null;
|
|
52
|
+
if (value.tabStops !== void 0) return null;
|
|
53
|
+
const indent = toLevelIndent(value.indent);
|
|
54
|
+
if (value.indent !== null && indent === null) return null;
|
|
55
|
+
return {
|
|
56
|
+
ilvl,
|
|
57
|
+
level: {
|
|
58
|
+
format,
|
|
59
|
+
text,
|
|
60
|
+
start,
|
|
61
|
+
indent,
|
|
62
|
+
restartAfterLevel,
|
|
63
|
+
legal: value.legal,
|
|
64
|
+
suffix,
|
|
65
|
+
align,
|
|
66
|
+
run: null
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function toList(value) {
|
|
71
|
+
if (!isRecord(value)) return null;
|
|
72
|
+
const { numId, levels } = value;
|
|
73
|
+
if (typeof numId !== "number" || !Number.isSafeInteger(numId) || numId <= 0)
|
|
74
|
+
return null;
|
|
75
|
+
if (!Array.isArray(levels)) return null;
|
|
76
|
+
const read = /* @__PURE__ */ new Map();
|
|
77
|
+
for (const entry of levels) {
|
|
78
|
+
const level = toLevel(entry);
|
|
79
|
+
if (level === null || read.has(level.ilvl)) return null;
|
|
80
|
+
read.set(level.ilvl, level.level);
|
|
81
|
+
}
|
|
82
|
+
return read.size === 0 ? null : { numId, list: { levels: read } };
|
|
83
|
+
}
|
|
84
|
+
function newListsValue(lists) {
|
|
85
|
+
if (lists.size === 0) return null;
|
|
86
|
+
return Array.from(lists).sort(([left], [right]) => left - right).map(([numId, list]) => ({
|
|
87
|
+
numId,
|
|
88
|
+
levels: Array.from(list.levels).sort(([left], [right]) => left - right).map(([ilvl, level]) => levelValue(ilvl, level))
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
function newListsOf(value) {
|
|
92
|
+
if (!Array.isArray(value)) return NO_NEW_LISTS;
|
|
93
|
+
const lists = /* @__PURE__ */ new Map();
|
|
94
|
+
for (const entry of value) {
|
|
95
|
+
const read = toList(entry);
|
|
96
|
+
if (read === null || lists.has(read.numId)) return NO_NEW_LISTS;
|
|
97
|
+
lists.set(read.numId, read.list);
|
|
98
|
+
}
|
|
99
|
+
return lists;
|
|
100
|
+
}
|
|
101
|
+
function listsWorn(lists, worn) {
|
|
102
|
+
const dropped = [...lists.keys()].filter((numId) => !worn.has(numId));
|
|
103
|
+
if (dropped.length === 0) return lists;
|
|
104
|
+
return new Map([...lists].filter(([numId]) => worn.has(numId)));
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
NEW_LISTS_ATTR,
|
|
108
|
+
NO_NEW_LISTS,
|
|
109
|
+
listsWorn,
|
|
110
|
+
newListsOf,
|
|
111
|
+
newListsValue
|
|
112
|
+
};
|
|
@@ -1,29 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* A list started while editing: the definition it is started with, the number it takes, and the
|
|
3
|
+
* `<w:abstractNum>` that definition is written out as.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Drawing the markers on screen and writing the definitions into numbering.xml both look
|
|
9
|
-
* at the same template.
|
|
5
|
+
* Nothing here reads anything into the number. The definition is a value the editor registers
|
|
6
|
+
* (`./listRegistry`), the same value the markers are drawn from and the one written into
|
|
7
|
+
* numbering.xml, so a list goes out as what it was started as.
|
|
10
8
|
*/
|
|
11
|
-
import type { LevelIndent, Numbering, NumberingList } from "./parseNumbering";
|
|
9
|
+
import type { LevelIndent, NewList, Numbering, NumberingList } from "./parseNumbering";
|
|
12
10
|
export type ListKind = "numbered" | "bullet";
|
|
13
11
|
/** A list has nine levels (the count OOXML defines) */
|
|
14
12
|
export declare const LEVEL_COUNT = 9;
|
|
15
13
|
export declare const MAX_ILVL: number;
|
|
16
14
|
export declare function templateIndent(ilvl: number): LevelIndent;
|
|
17
|
-
|
|
15
|
+
/** The definition a list of this kind is started with */
|
|
16
|
+
export declare function templateList(kind: ListKind): NewList;
|
|
17
|
+
/**
|
|
18
|
+
* The definition standing behind this number: the one the document wrote down, or the one a list
|
|
19
|
+
* started while editing was registered with. Undefined for a number nothing defines, which is a
|
|
20
|
+
* list neither drawn nor written.
|
|
21
|
+
*/
|
|
22
|
+
export declare function listFor(numbering: Numbering, numId: number): NumberingList | undefined;
|
|
23
|
+
/** Reserves safe positive ids, using gaps if incrementing the highest id would lose precision. */
|
|
24
|
+
export declare function numberingIdAllocator(ids: Iterable<number>): () => number;
|
|
18
25
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
26
|
+
* A number for a new list, and the numbering that now defines it under that number.
|
|
27
|
+
*
|
|
28
|
+
* `used` names the numbers the document already spends, the ones no definition stands behind
|
|
29
|
+
* included: a number a paragraph already wears would otherwise join that paragraph to the new list.
|
|
22
30
|
*/
|
|
23
|
-
export declare function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
export declare function allocateList(numbering: Numbering, used: Iterable<number>, list: NewList): {
|
|
32
|
+
numId: number;
|
|
33
|
+
numbering: Numbering;
|
|
34
|
+
};
|
|
35
|
+
/** Writes one registered definition out as a `<w:abstractNum>` */
|
|
36
|
+
export declare function abstractNumXml(abstractNumId: number, list: NewList): string;
|
|
28
37
|
/** The `<w:num>` that ties a list number to a definition */
|
|
29
38
|
export declare function numXml(numId: number, abstractNumId: number): string;
|
|
@@ -29,7 +29,12 @@ function templateLevel(kind, ilvl) {
|
|
|
29
29
|
format: numbered ? cycled(NUMBER_CYCLE, ilvl, "decimal") : "bullet",
|
|
30
30
|
text: numbered ? `%${ilvl + 1}.` : cycled(BULLET_CYCLE, ilvl, "\u25CF"),
|
|
31
31
|
start: 1,
|
|
32
|
-
indent: templateIndent(ilvl)
|
|
32
|
+
indent: templateIndent(ilvl),
|
|
33
|
+
restartAfterLevel: null,
|
|
34
|
+
legal: false,
|
|
35
|
+
suffix: "tab",
|
|
36
|
+
align: "left",
|
|
37
|
+
run: null
|
|
33
38
|
};
|
|
34
39
|
}
|
|
35
40
|
function templateList(kind) {
|
|
@@ -39,19 +44,35 @@ function templateList(kind) {
|
|
|
39
44
|
}
|
|
40
45
|
return { levels };
|
|
41
46
|
}
|
|
42
|
-
var TEMPLATES = {
|
|
43
|
-
numbered: templateList("numbered"),
|
|
44
|
-
bullet: templateList("bullet")
|
|
45
|
-
};
|
|
46
|
-
function listKindOf(numId) {
|
|
47
|
-
return numId % 2 === 0 ? "numbered" : "bullet";
|
|
48
|
-
}
|
|
49
47
|
function listFor(numbering, numId) {
|
|
50
|
-
return numbering.lists.get(numId) ??
|
|
48
|
+
return numbering.lists.get(numId) ?? numbering.added.get(numId);
|
|
51
49
|
}
|
|
52
|
-
function
|
|
53
|
-
const
|
|
54
|
-
|
|
50
|
+
function numberingIdAllocator(ids) {
|
|
51
|
+
const used = new Set(ids);
|
|
52
|
+
let highest = 0;
|
|
53
|
+
for (const id of used) if (id > highest) highest = id;
|
|
54
|
+
let next = Number.isSafeInteger(highest + 1) ? highest + 1 : 1;
|
|
55
|
+
return () => {
|
|
56
|
+
while (used.has(next)) next += 1;
|
|
57
|
+
const id = next;
|
|
58
|
+
used.add(id);
|
|
59
|
+
next = Number.isSafeInteger(id + 1) ? id + 1 : 1;
|
|
60
|
+
return id;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function allocateList(numbering, used, list) {
|
|
64
|
+
const numId = numberingIdAllocator([
|
|
65
|
+
...numbering.lists.keys(),
|
|
66
|
+
...numbering.added.keys(),
|
|
67
|
+
...used
|
|
68
|
+
])();
|
|
69
|
+
return {
|
|
70
|
+
numId,
|
|
71
|
+
numbering: {
|
|
72
|
+
...numbering,
|
|
73
|
+
added: new Map([...numbering.added, [numId, list]])
|
|
74
|
+
}
|
|
75
|
+
};
|
|
55
76
|
}
|
|
56
77
|
function indXml(indent) {
|
|
57
78
|
const slots = [
|
|
@@ -68,8 +89,33 @@ function indXml(indent) {
|
|
|
68
89
|
[{ name: "ind", xml: elementXml(wName("ind"), attrs) }]
|
|
69
90
|
);
|
|
70
91
|
}
|
|
92
|
+
function lvlRestartXml(restartAfterLevel) {
|
|
93
|
+
if (restartAfterLevel === null) return null;
|
|
94
|
+
return {
|
|
95
|
+
name: "lvlRestart",
|
|
96
|
+
xml: elementXml(wName("lvlRestart"), [
|
|
97
|
+
[wName("val"), `${restartAfterLevel + 1}`]
|
|
98
|
+
])
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function isLglXml(legal) {
|
|
102
|
+
return legal ? { name: "isLgl", xml: elementXml(wName("isLgl"), []) } : null;
|
|
103
|
+
}
|
|
104
|
+
function suffXml(suffix) {
|
|
105
|
+
if (suffix === "tab") return null;
|
|
106
|
+
return {
|
|
107
|
+
name: "suff",
|
|
108
|
+
xml: elementXml(wName("suff"), [[wName("val"), suffix]])
|
|
109
|
+
};
|
|
110
|
+
}
|
|
71
111
|
function levelXml(ilvl, level) {
|
|
72
112
|
const ind = indXml(level.indent);
|
|
113
|
+
const optional = [
|
|
114
|
+
lvlRestartXml(level.restartAfterLevel),
|
|
115
|
+
isLglXml(level.legal),
|
|
116
|
+
suffXml(level.suffix),
|
|
117
|
+
ind === "" ? null : { name: "pPr", xml: ind }
|
|
118
|
+
];
|
|
73
119
|
return orderedElement(
|
|
74
120
|
wName("lvl"),
|
|
75
121
|
[[wName("ilvl"), `${ilvl}`]],
|
|
@@ -88,15 +134,14 @@ function levelXml(ilvl, level) {
|
|
|
88
134
|
},
|
|
89
135
|
{
|
|
90
136
|
name: "lvlJc",
|
|
91
|
-
xml: elementXml(wName("lvlJc"), [[wName("val"),
|
|
137
|
+
xml: elementXml(wName("lvlJc"), [[wName("val"), level.align]])
|
|
92
138
|
},
|
|
93
|
-
...
|
|
139
|
+
...optional.filter((child) => child !== null)
|
|
94
140
|
]
|
|
95
141
|
);
|
|
96
142
|
}
|
|
97
|
-
function abstractNumXml(abstractNumId,
|
|
98
|
-
const
|
|
99
|
-
const body = Array.from(levels.entries()).map(([ilvl, level]) => levelXml(ilvl, level)).join("");
|
|
143
|
+
function abstractNumXml(abstractNumId, list) {
|
|
144
|
+
const body = Array.from(list.levels.entries()).map(([ilvl, level]) => levelXml(ilvl, level)).join("");
|
|
100
145
|
return elementXml(
|
|
101
146
|
wName("abstractNum"),
|
|
102
147
|
[[wName("abstractNumId"), `${abstractNumId}`]],
|
|
@@ -114,9 +159,10 @@ export {
|
|
|
114
159
|
LEVEL_COUNT,
|
|
115
160
|
MAX_ILVL,
|
|
116
161
|
abstractNumXml,
|
|
162
|
+
allocateList,
|
|
117
163
|
listFor,
|
|
118
|
-
listKindOf,
|
|
119
|
-
nextNumId,
|
|
120
164
|
numXml,
|
|
121
|
-
|
|
165
|
+
numberingIdAllocator,
|
|
166
|
+
templateIndent,
|
|
167
|
+
templateList
|
|
122
168
|
};
|
|
@@ -4,21 +4,30 @@
|
|
|
4
4
|
* A pure computation that never touches the document model: the same paragraphs with the
|
|
5
5
|
* same numbering definitions always yield the same numbers.
|
|
6
6
|
*/
|
|
7
|
-
import type { NumberingRef } from "../model/format";
|
|
8
|
-
import { type LevelIndentPt, type Numbering } from "./parseNumbering";
|
|
7
|
+
import type { NumberingRef, RunFormat } from "../model/format";
|
|
8
|
+
import { type LevelAlign, type LevelIndentPt, type LevelSuffix, type Numbering } from "./parseNumbering";
|
|
9
9
|
/** The number to draw in front of one paragraph */
|
|
10
10
|
export interface ListMarker {
|
|
11
|
+
/**
|
|
12
|
+
* The number as it is drawn, the space a level asking for one (`w:suff`) included: a separator
|
|
13
|
+
* made of a character belongs to the marker, while the tab every other level asks for is the
|
|
14
|
+
* width the number sits in rather than anything to draw
|
|
15
|
+
*/
|
|
11
16
|
text: string;
|
|
12
17
|
/**
|
|
13
18
|
* The indent defined by the level this paragraph belongs to. Used when the paragraph
|
|
14
19
|
* records none of its own
|
|
15
20
|
*/
|
|
16
21
|
indent: LevelIndentPt;
|
|
22
|
+
/** Whether the number sits in a width of its own, which is what a tab suffix asks for */
|
|
23
|
+
suffix: LevelSuffix;
|
|
24
|
+
align: LevelAlign;
|
|
25
|
+
/** The formatting the level puts on the number, which dresses the number and nothing else */
|
|
26
|
+
run: RunFormat | null;
|
|
17
27
|
}
|
|
18
28
|
/**
|
|
19
|
-
* A slot is null for a paragraph that is not a list item, or whose level shape could not
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* the standard template's shape.
|
|
29
|
+
* A slot is null for a paragraph that is not a list item, or whose level shape could not be found.
|
|
30
|
+
* A number nothing defines - neither numbering.xml nor a list started while editing - draws no
|
|
31
|
+
* marker, which is what Word does with a `w:numId` no `w:num` answers.
|
|
23
32
|
*/
|
|
24
33
|
export declare function computeMarkers(paragraphs: readonly (NumberingRef | null)[], numbering: Numbering): (ListMarker | null)[];
|
|
@@ -3,71 +3,31 @@ import { listFor } from "./listTemplate.js";
|
|
|
3
3
|
import {
|
|
4
4
|
levelIndentPt
|
|
5
5
|
} from "./parseNumbering.js";
|
|
6
|
-
|
|
7
|
-
[1e3, "M"],
|
|
8
|
-
[900, "CM"],
|
|
9
|
-
[500, "D"],
|
|
10
|
-
[400, "CD"],
|
|
11
|
-
[100, "C"],
|
|
12
|
-
[90, "XC"],
|
|
13
|
-
[50, "L"],
|
|
14
|
-
[40, "XL"],
|
|
15
|
-
[10, "X"],
|
|
16
|
-
[9, "IX"],
|
|
17
|
-
[5, "V"],
|
|
18
|
-
[4, "IV"],
|
|
19
|
-
[1, "I"]
|
|
20
|
-
];
|
|
21
|
-
function toRoman(value) {
|
|
22
|
-
let rest = value;
|
|
23
|
-
let out = "";
|
|
24
|
-
for (const [amount, sign] of ROMAN) {
|
|
25
|
-
while (rest >= amount) {
|
|
26
|
-
out += sign;
|
|
27
|
-
rest -= amount;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return out;
|
|
31
|
-
}
|
|
32
|
-
function toLetters(value) {
|
|
33
|
-
const index = (value - 1) % 26;
|
|
34
|
-
const repeat = Math.floor((value - 1) / 26) + 1;
|
|
35
|
-
return String.fromCharCode(65 + index).repeat(repeat);
|
|
36
|
-
}
|
|
6
|
+
import { spellNumber } from "./spellers.js";
|
|
37
7
|
var MAX_MARKER_CHARS = 64;
|
|
38
|
-
|
|
39
|
-
decimal: Number.POSITIVE_INFINITY,
|
|
40
|
-
bullet: Number.POSITIVE_INFINITY,
|
|
41
|
-
lowerLetter: 26 * MAX_MARKER_CHARS,
|
|
42
|
-
upperLetter: 26 * MAX_MARKER_CHARS,
|
|
43
|
-
lowerRoman: 1e3 * MAX_MARKER_CHARS
|
|
44
|
-
};
|
|
45
|
-
function formatNumber(value, format) {
|
|
46
|
-
if (value < 1 || value > MAX_SPELLED_VALUE[format]) return `${value}`;
|
|
47
|
-
switch (format) {
|
|
48
|
-
case "lowerLetter":
|
|
49
|
-
return toLetters(value).toLowerCase();
|
|
50
|
-
case "upperLetter":
|
|
51
|
-
return toLetters(value);
|
|
52
|
-
case "lowerRoman":
|
|
53
|
-
return toRoman(value).toLowerCase();
|
|
54
|
-
default:
|
|
55
|
-
return `${value}`;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function fillLevels(text, list, counters) {
|
|
8
|
+
function fillLevels(text, list, counters, legal) {
|
|
59
9
|
return text.replaceAll(/%([1-9])/g, (_, digit) => {
|
|
60
10
|
const ilvl = Number(digit) - 1;
|
|
61
11
|
const level = list.levels.get(ilvl);
|
|
62
12
|
if (!level) return "";
|
|
63
|
-
|
|
13
|
+
const count = counters.get(ilvl) ?? level.start;
|
|
14
|
+
return spellNumber(
|
|
15
|
+
count,
|
|
16
|
+
legal ? "decimal" : level.format,
|
|
17
|
+
MAX_MARKER_CHARS
|
|
18
|
+
);
|
|
64
19
|
});
|
|
65
20
|
}
|
|
21
|
+
function restartedBy(level, ilvl) {
|
|
22
|
+
return level?.restartAfterLevel ?? ilvl - 1;
|
|
23
|
+
}
|
|
66
24
|
function advance(counters, list, ilvl, start) {
|
|
67
25
|
const current = counters.get(ilvl);
|
|
68
26
|
counters.set(ilvl, current === void 0 ? start : current + 1);
|
|
69
27
|
for (const deeper of [...counters.keys(), ...list.levels.keys()]) {
|
|
70
|
-
if (deeper > ilvl
|
|
28
|
+
if (deeper > ilvl && restartedBy(list.levels.get(deeper), deeper) >= ilvl) {
|
|
29
|
+
counters.delete(deeper);
|
|
30
|
+
}
|
|
71
31
|
}
|
|
72
32
|
}
|
|
73
33
|
var MARKER_SEGMENTER = new Intl.Segmenter();
|
|
@@ -87,17 +47,24 @@ function computeMarkers(paragraphs, numbering) {
|
|
|
87
47
|
return paragraphs.map((ref) => {
|
|
88
48
|
if (!ref) return null;
|
|
89
49
|
const list = listFor(numbering, ref.numId);
|
|
90
|
-
const level = list
|
|
91
|
-
if (!level) return null;
|
|
50
|
+
const level = list?.levels.get(ref.ilvl);
|
|
51
|
+
if (!list || !level) return null;
|
|
92
52
|
let counters = countersByList.get(ref.numId);
|
|
93
53
|
if (!counters) {
|
|
94
54
|
counters = /* @__PURE__ */ new Map();
|
|
95
55
|
countersByList.set(ref.numId, counters);
|
|
96
56
|
}
|
|
97
57
|
advance(counters, list, ref.ilvl, level.start);
|
|
98
|
-
const shape = level.format === "bullet" ? level.text : fillLevels(level.text, list, counters);
|
|
99
|
-
const
|
|
100
|
-
|
|
58
|
+
const shape = level.format === "bullet" ? level.text : fillLevels(level.text, list, counters, level.legal);
|
|
59
|
+
const drawn = capped(shape);
|
|
60
|
+
if (!drawn) return null;
|
|
61
|
+
return {
|
|
62
|
+
text: level.suffix === "space" ? `${drawn} ` : drawn,
|
|
63
|
+
indent: levelIndentPt(level.indent),
|
|
64
|
+
suffix: level.suffix,
|
|
65
|
+
align: level.align,
|
|
66
|
+
run: level.run
|
|
67
|
+
};
|
|
101
68
|
});
|
|
102
69
|
}
|
|
103
70
|
export {
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* numbering.xml itself goes out as the original bytes on export, so this reading never
|
|
6
6
|
* disturbs the file.
|
|
7
7
|
*/
|
|
8
|
+
import type { RunFormat } from "../model/format";
|
|
8
9
|
import type { TabStopDirective } from "../model/tabStops";
|
|
9
10
|
import { type XmlParser } from "../ooxml/xml";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export type NumberFormat = (typeof NUMBER_FORMATS)[number];
|
|
11
|
+
import { type NumberFormat } from "./spellers";
|
|
12
|
+
export type { NumberFormat } from "./spellers";
|
|
13
13
|
/**
|
|
14
14
|
* The indent a level defines. The unit is twips (1/20 of a point), and a slot that is
|
|
15
15
|
* not recorded is null
|
|
@@ -20,6 +20,10 @@ export interface LevelIndent {
|
|
|
20
20
|
hangingTwips: number | null;
|
|
21
21
|
firstLineTwips: number | null;
|
|
22
22
|
}
|
|
23
|
+
/** What stands between a level's number and the text of the paragraph (`w:suff`, §17.9.28) */
|
|
24
|
+
export type LevelSuffix = "tab" | "space" | "nothing";
|
|
25
|
+
/** Where a level's number sits in the space kept for it (`w:lvlJc`, §17.9.7) */
|
|
26
|
+
export type LevelAlign = "left" | "center" | "right";
|
|
23
27
|
export interface NumberingLevel {
|
|
24
28
|
format: NumberFormat;
|
|
25
29
|
/**
|
|
@@ -32,14 +36,58 @@ export interface NumberingLevel {
|
|
|
32
36
|
indent: LevelIndent | null;
|
|
33
37
|
/** Custom stops contributed by this level's paragraph properties. */
|
|
34
38
|
tabStops?: readonly TabStopDirective[];
|
|
39
|
+
/**
|
|
40
|
+
* The deepest level whose advance sends this one back to its start (`w:lvlRestart`, §17.9.10),
|
|
41
|
+
* as a level number: any level above that one restarts it too. A level of -1 never restarts, and
|
|
42
|
+
* null is the default, the level right above this one.
|
|
43
|
+
*/
|
|
44
|
+
restartAfterLevel: number | null;
|
|
45
|
+
/** Every level in this level's text spelled as a decimal, whatever format it counts in (`w:isLgl`, §17.9.4) */
|
|
46
|
+
legal: boolean;
|
|
47
|
+
suffix: LevelSuffix;
|
|
48
|
+
align: LevelAlign;
|
|
49
|
+
/**
|
|
50
|
+
* The formatting of the number itself (`w:rPr`, §17.9.24). It dresses the number alone and
|
|
51
|
+
* never the text of the paragraph the number stands in front of.
|
|
52
|
+
* Null where the level writes none, and where the caller handed in no reader for it.
|
|
53
|
+
*/
|
|
54
|
+
run: RunFormat | null;
|
|
35
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* How a level's `w:rPr` is read into display values.
|
|
58
|
+
* The vocabulary of run properties belongs with the reader that holds styles.xml and the theme
|
|
59
|
+
* fonts, which this folder cannot reach, so it is handed in.
|
|
60
|
+
*/
|
|
61
|
+
export type ReadLevelRun = (rPr: Element) => RunFormat | null;
|
|
36
62
|
export interface NumberingList {
|
|
37
63
|
/** The shape for each level (ilvl) */
|
|
38
|
-
levels:
|
|
64
|
+
levels: ReadonlyMap<number, NumberingLevel>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A level a list started while editing is registered with.
|
|
68
|
+
*
|
|
69
|
+
* It is a level minus the two things the writer of a new definition cannot spell where it stands:
|
|
70
|
+
* the formatting of the number (`w:rPr`) and the tab stops its paragraphs contribute (`w:tabs`)
|
|
71
|
+
* are written by `docx/`, which stands above this folder. Leaving them out of what may be
|
|
72
|
+
* registered is what keeps a registered definition from reaching the file with a part of it
|
|
73
|
+
* silently dropped.
|
|
74
|
+
*/
|
|
75
|
+
export interface NewListLevel extends NumberingLevel {
|
|
76
|
+
readonly run: null;
|
|
77
|
+
readonly tabStops?: undefined;
|
|
78
|
+
}
|
|
79
|
+
/** A list started while editing, whose definition the export writes into numbering.xml as it stands */
|
|
80
|
+
export interface NewList {
|
|
81
|
+
levels: ReadonlyMap<number, NewListLevel>;
|
|
39
82
|
}
|
|
40
83
|
export interface Numbering {
|
|
41
84
|
/** The list for each numId */
|
|
42
85
|
lists: Map<number, NumberingList>;
|
|
86
|
+
/**
|
|
87
|
+
* The lists started while editing, by the number each was given. numbering.xml defines none of
|
|
88
|
+
* them yet; the export writes the definition registered here.
|
|
89
|
+
*/
|
|
90
|
+
added: ReadonlyMap<number, NewList>;
|
|
43
91
|
}
|
|
44
92
|
export declare const EMPTY_NUMBERING: Numbering;
|
|
45
93
|
/**
|
|
@@ -62,6 +110,17 @@ export declare const NO_LEVEL_INDENT: LevelIndentPt;
|
|
|
62
110
|
* OOXML rule).
|
|
63
111
|
*/
|
|
64
112
|
export declare function levelIndentPt(indent: LevelIndent | null): LevelIndentPt;
|
|
113
|
+
export declare const LEVEL_SUFFIXES: readonly LevelSuffix[];
|
|
114
|
+
/** The justifications a number can take, which are the ones `w:lvlJc` offers a number */
|
|
115
|
+
export declare const LEVEL_ALIGNS: readonly LevelAlign[];
|
|
116
|
+
/**
|
|
117
|
+
* The list each numbering style names, by the style's id.
|
|
118
|
+
*
|
|
119
|
+
* A numbering style is never worn by a paragraph: it is a name on a list, which an abstract
|
|
120
|
+
* definition reaches through `w:numStyleLink` (§17.9.21). The map is built from styles.xml, which
|
|
121
|
+
* this folder cannot read for itself.
|
|
122
|
+
*/
|
|
123
|
+
export type NumberingStyleLinks = ReadonlyMap<string, number>;
|
|
65
124
|
/** What a caller may say about reading numbering beyond handing over the XML */
|
|
66
125
|
export interface NumberingOptions {
|
|
67
126
|
/**
|
|
@@ -69,10 +128,16 @@ export interface NumberingOptions {
|
|
|
69
128
|
* runtime carrying none refuses the read with `no-xml-parser`.
|
|
70
129
|
*/
|
|
71
130
|
xmlParser?: XmlParser;
|
|
131
|
+
/**
|
|
132
|
+
* The list each numbering style of the document names. Left out, a definition deferring to a
|
|
133
|
+
* numbering style is read through the definition that declares it stands behind that style.
|
|
134
|
+
*/
|
|
135
|
+
links?: NumberingStyleLinks;
|
|
136
|
+
/** How the formatting a level puts on its number is read. Left out, no level carries any */
|
|
137
|
+
readRun?: ReadLevelRun;
|
|
72
138
|
}
|
|
73
139
|
/**
|
|
74
140
|
* The lists this numbering XML defines. `null` is the document that has no numbering part, which
|
|
75
141
|
* defines none and asks nothing of a parser.
|
|
76
142
|
*/
|
|
77
143
|
export declare function parseNumbering(xml: string | null, options?: NumberingOptions): Numbering;
|
|
78
|
-
export {};
|