@portone/docx-editor 0.2.1 → 0.3.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 +109 -0
- package/CONTRIBUTING.md +5 -1
- package/dist/DocxEditor.d.ts +6 -0
- package/dist/DocxEditor.js +12 -29
- package/dist/core.d.ts +23 -8
- package/dist/core.js +9 -4
- package/dist/docx/cloning.d.ts +38 -0
- package/dist/docx/cloning.js +74 -0
- package/dist/docx/commentOnlyChange.d.ts +12 -7
- package/dist/docx/commentOnlyChange.js +9 -156
- package/dist/docx/comments/constants.d.ts +0 -1
- package/dist/docx/comments/constants.js +0 -2
- package/dist/docx/comments/grammar.d.ts +10 -4
- package/dist/docx/comments/grammar.js +12 -2
- package/dist/docx/comments/{verifying.d.ts → parts.d.ts} +26 -18
- package/dist/docx/comments/parts.js +279 -0
- package/dist/docx/comments/people.d.ts +11 -2
- package/dist/docx/comments/people.js +16 -92
- package/dist/docx/comments/policy.d.ts +7 -0
- package/dist/docx/comments/policy.js +43 -0
- package/dist/docx/comments/reading.js +8 -10
- package/dist/docx/comments/writing.d.ts +19 -7
- package/dist/docx/comments/writing.js +52 -111
- package/dist/docx/documentSettings.d.ts +7 -0
- package/dist/docx/documentSettings.js +10 -2
- package/dist/docx/exportDocx.d.ts +40 -5
- package/dist/docx/exportDocx.js +69 -71
- package/dist/docx/exportRefs.d.ts +5 -2
- package/dist/docx/exportRefs.js +3 -1
- package/dist/docx/fidelity.d.ts +43 -0
- package/dist/docx/fidelity.js +64 -0
- package/dist/docx/formatting/attrs.d.ts +27 -0
- package/dist/docx/formatting/attrs.js +31 -0
- package/dist/docx/formatting/context.d.ts +31 -0
- package/dist/docx/formatting/context.js +42 -0
- package/dist/docx/formatting/direct.d.ts +6 -5
- package/dist/docx/formatting/direct.js +52 -78
- package/dist/docx/formatting/resolve.d.ts +45 -0
- package/dist/docx/formatting/resolve.js +140 -0
- package/dist/docx/formatting/runProperties.d.ts +93 -0
- package/dist/docx/formatting/runProperties.js +316 -0
- package/dist/docx/formatting/styles.js +3 -3
- package/dist/docx/formatting/tabStops.js +7 -22
- package/dist/docx/formatting.d.ts +4 -1
- package/dist/docx/formatting.js +4 -1
- package/dist/docx/headersFooters.js +6 -13
- package/dist/docx/identities.d.ts +67 -0
- package/dist/docx/identities.js +174 -0
- package/dist/docx/importDocx.d.ts +12 -1
- package/dist/docx/importDocx.js +72 -79
- package/dist/docx/importParagraph.d.ts +1 -1
- package/dist/docx/importParagraph.js +4 -3
- package/dist/docx/importTable.d.ts +1 -1
- package/dist/docx/importTable.js +17 -1
- package/dist/docx/invariants.d.ts +33 -0
- package/dist/docx/invariants.js +256 -0
- package/dist/docx/media.d.ts +6 -4
- package/dist/docx/media.js +6 -37
- package/dist/docx/newLists.d.ts +20 -0
- package/dist/docx/newLists.js +36 -0
- package/dist/docx/notes.js +3 -7
- package/dist/docx/numberingPlanner.d.ts +8 -0
- package/dist/docx/numberingPlanner.js +19 -0
- package/dist/docx/packageParts.d.ts +42 -0
- package/dist/docx/packageParts.js +135 -0
- package/dist/docx/pageGeometry.d.ts +2 -0
- package/dist/docx/pageGeometry.js +18 -9
- package/dist/docx/paraProps.d.ts +10 -22
- package/dist/docx/paraProps.js +47 -76
- package/dist/docx/partPlan.d.ts +36 -0
- package/dist/docx/partPlan.js +59 -0
- package/dist/docx/protectionPolicy.d.ts +127 -0
- package/dist/docx/protectionPolicy.js +169 -0
- package/dist/docx/relationships.d.ts +1 -1
- package/dist/docx/relationships.js +8 -13
- package/dist/docx/runProps.d.ts +9 -22
- package/dist/docx/runProps.js +15 -168
- package/dist/docx/scan.js +20 -52
- package/dist/docx/sdt.js +9 -34
- package/dist/docx/sdtProps.d.ts +8 -1
- package/dist/docx/sdtProps.js +10 -0
- package/dist/docx/serializeBlock.d.ts +2 -0
- package/dist/docx/serializeBlock.js +8 -5
- package/dist/docx/serializeTable.js +32 -15
- package/dist/docx/session.d.ts +42 -14
- package/dist/docx/session.js +43 -17
- package/dist/docx/tableFormatting/editing.js +108 -139
- package/dist/docx/tableFormatting/reading.d.ts +16 -6
- package/dist/docx/tableFormatting/reading.js +40 -20
- package/dist/docx/tableTemplate.js +18 -7
- package/dist/download.d.ts +8 -5
- package/dist/download.js +2 -0
- package/dist/editor/clipboard/inlineFormatting.js +19 -30
- package/dist/editor/commands/comments/editing.d.ts +6 -2
- package/dist/editor/commands/comments/editing.js +19 -31
- package/dist/editor/commands/exportQueries.d.ts +15 -0
- package/dist/editor/commands/exportQueries.js +14 -0
- package/dist/editor/commands/fidelityQueries.d.ts +12 -0
- package/dist/editor/commands/fidelityQueries.js +8 -0
- package/dist/editor/commands/formatting/editing.d.ts +2 -2
- package/dist/editor/commands/formatting/editing.js +26 -108
- package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
- package/dist/editor/commands/formatting/propertyCommands.js +103 -0
- package/dist/editor/commands/formatting/shared.d.ts +3 -3
- package/dist/editor/commands/formatting/shared.js +5 -2
- package/dist/editor/commands/indentCommands.js +5 -4
- package/dist/editor/commands/index.d.ts +11 -0
- package/dist/editor/commands/index.js +5 -0
- package/dist/editor/commands/linkCommands.js +5 -6
- package/dist/editor/commands/listCommands.js +8 -10
- package/dist/editor/commands/lockCommands.d.ts +7 -1
- package/dist/editor/commands/lockCommands.js +35 -46
- package/dist/editor/commands/paragraphCommands.js +28 -31
- package/dist/editor/commands/spacingCommands.js +1 -1
- package/dist/editor/createEditor.d.ts +17 -29
- package/dist/editor/createEditor.js +58 -58
- package/dist/editor/documentStyles.d.ts +11 -35
- package/dist/editor/documentStyles.js +9 -52
- package/dist/editor/editorDocument.d.ts +56 -0
- package/dist/editor/editorDocument.js +74 -0
- package/dist/editor/externalClipboard.js +16 -45
- package/dist/editor/insertTable.js +4 -3
- package/dist/editor/paragraphEdits.d.ts +11 -18
- package/dist/editor/paragraphEdits.js +7 -16
- package/dist/editor/plugins/displayDerivation.d.ts +47 -0
- package/dist/editor/plugins/displayDerivation.js +78 -0
- package/dist/editor/plugins/imagePaste.js +4 -3
- package/dist/editor/plugins/keymap.js +18 -3
- package/dist/editor/plugins/numberingDecorations.d.ts +5 -11
- package/dist/editor/plugins/numberingDecorations.js +7 -17
- package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
- package/dist/editor/plugins/paragraphDisplay.js +51 -0
- package/dist/editor/plugins/tabLayout.js +1 -1
- package/dist/editor/plugins/tableDisplay.d.ts +6 -0
- package/dist/editor/plugins/tableDisplay.js +16 -0
- package/dist/index.d.ts +2 -0
- package/dist/model/format.d.ts +30 -8
- package/dist/model/format.js +35 -22
- package/dist/model/tabStops.d.ts +9 -0
- package/dist/model/tabStops.js +18 -0
- package/dist/numbering/listTemplate.js +24 -7
- package/dist/numbering/parseNumbering.d.ts +14 -1
- package/dist/numbering/parseNumbering.js +35 -22
- package/dist/numbering/writeNumbering.d.ts +3 -4
- package/dist/numbering/writeNumbering.js +14 -25
- package/dist/ooxml/childOrder.d.ts +34 -0
- package/dist/ooxml/childOrder.js +496 -0
- package/dist/ooxml/element.d.ts +24 -11
- package/dist/ooxml/element.js +31 -12
- package/dist/ooxml/errors.d.ts +3 -2
- package/dist/ooxml/image.d.ts +4 -0
- package/dist/ooxml/image.js +2 -5
- package/dist/ooxml/partSplice.d.ts +67 -0
- package/dist/ooxml/partSplice.js +169 -0
- package/dist/ooxml/props.d.ts +112 -0
- package/dist/{docx/propsXml.js → ooxml/props.js} +72 -200
- package/dist/ooxml/simpleTypes.d.ts +103 -0
- package/dist/ooxml/simpleTypes.js +182 -0
- package/dist/ooxml/tabStops.js +8 -27
- package/dist/ooxml/tagScan.d.ts +34 -0
- package/dist/ooxml/tagScan.js +108 -0
- package/dist/ooxml/units.d.ts +25 -13
- package/dist/ooxml/units.js +65 -31
- package/dist/ooxml/xml.d.ts +27 -6
- package/dist/ooxml/xml.js +37 -5
- package/dist/page/blockKinds.d.ts +61 -0
- package/dist/page/blockKinds.js +11 -0
- package/dist/page/kinds/index.d.ts +6 -0
- package/dist/page/kinds/index.js +10 -0
- package/dist/page/kinds/paragraphKind.d.ts +10 -0
- package/dist/page/kinds/paragraphKind.js +71 -0
- package/dist/page/kinds/tableKind.d.ts +10 -0
- package/dist/page/kinds/tableKind.js +178 -0
- package/dist/page/measureBlocks.d.ts +3 -14
- package/dist/page/measureBlocks.js +24 -45
- package/dist/page/pageDecorations.d.ts +27 -44
- package/dist/page/pageDecorations.js +70 -152
- package/dist/page/pageLayout.d.ts +2 -19
- package/dist/page/pageLayout.js +36 -6
- package/dist/page/usePageLayout.d.ts +2 -17
- package/dist/page/usePageLayout.js +3 -39
- package/dist/schema/attrRoles.d.ts +25 -18
- package/dist/schema/attrRoles.js +102 -67
- package/dist/schema/displayDerivation.d.ts +82 -0
- package/dist/schema/displayDerivation.js +130 -0
- package/dist/schema/docxSchema.d.ts +3 -0
- package/dist/schema/docxSchema.js +26 -14
- package/dist/schema/editGuard.d.ts +1 -1
- package/dist/schema/guards.d.ts +37 -14
- package/dist/schema/guards.js +29 -3
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/locks.d.ts +11 -44
- package/dist/schema/locks.js +0 -9
- package/dist/schema/preservedGuards.d.ts +26 -6
- package/dist/schema/preservedGuards.js +31 -1
- package/dist/schema/protection.d.ts +7 -1
- package/dist/schema/protection.js +2 -1
- package/dist/schema/sourceEquality.d.ts +1 -9
- package/dist/schema/sourceEquality.js +1 -27
- package/dist/styles/inlineStyle.js +15 -6
- package/dist/table/cellFormatting.d.ts +8 -1
- package/dist/table/cellFormatting.js +10 -18
- package/dist/table/commands.js +20 -18
- package/dist/table/format.d.ts +3 -0
- package/dist/table/format.js +3 -9
- package/dist/table/gridBorders.d.ts +14 -16
- package/dist/table/gridBorders.js +3 -41
- package/dist/table/merge.d.ts +2 -6
- package/dist/table/merge.js +5 -5
- package/package.json +7 -3
- package/dist/docx/comments/contentTypes.d.ts +0 -7
- package/dist/docx/comments/contentTypes.js +0 -38
- package/dist/docx/comments/verifying.js +0 -206
- package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
- package/dist/docx/formatting/effectiveParagraph.js +0 -81
- package/dist/docx/propsXml.d.ts +0 -67
- package/dist/docx/uniqueControls.d.ts +0 -14
- package/dist/docx/uniqueControls.js +0 -62
- package/dist/editor/plugins/commentReservations.d.ts +0 -5
- package/dist/editor/plugins/commentReservations.js +0 -26
- package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
- package/dist/editor/plugins/styledParagraphs.js +0 -65
- package/dist/page/tableMeasurements.d.ts +0 -18
- package/dist/page/tableMeasurements.js +0 -119
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
// src/docx/pageGeometry.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ST_SignedTwipsMeasure,
|
|
4
|
+
ST_TwipsMeasure,
|
|
5
|
+
TWIPS_PER_INCH,
|
|
6
|
+
TWIPS_PER_PT
|
|
7
|
+
} from "../ooxml/simpleTypes.js";
|
|
8
|
+
import { wAttr } from "../ooxml/units.js";
|
|
3
9
|
import { childByLocalName } from "../ooxml/xml.js";
|
|
4
|
-
var TWIPS_PER_CM =
|
|
5
|
-
var
|
|
10
|
+
var TWIPS_PER_CM = TWIPS_PER_INCH / 2.54;
|
|
11
|
+
var PX_PER_PT = 96 / 72;
|
|
12
|
+
var PX_PER_TWIP = PX_PER_PT / TWIPS_PER_PT;
|
|
6
13
|
function cm(value) {
|
|
7
14
|
return Math.round(value * TWIPS_PER_CM);
|
|
8
15
|
}
|
|
@@ -34,13 +41,14 @@ function readPageGeometry(sectPr) {
|
|
|
34
41
|
if (!sectPr) return A4_PORTRAIT;
|
|
35
42
|
const pgSz = childByLocalName(sectPr, "pgSz");
|
|
36
43
|
const pgMar = childByLocalName(sectPr, "pgMar");
|
|
37
|
-
const width = pgSz ? readSize(
|
|
38
|
-
const height = pgSz ? readSize(
|
|
44
|
+
const width = pgSz ? readSize(ST_TwipsMeasure.parse(wAttr(pgSz, "w"))) : null;
|
|
45
|
+
const height = pgSz ? readSize(ST_TwipsMeasure.parse(wAttr(pgSz, "h"))) : null;
|
|
39
46
|
const readable = width !== null && height !== null;
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
47
|
+
const margin = (side) => pgMar ? ST_SignedTwipsMeasure.parse(wAttr(pgMar, side)) : null;
|
|
48
|
+
const marginLeft = margin("left");
|
|
49
|
+
const marginRight = margin("right");
|
|
50
|
+
const marginTop = margin("top");
|
|
51
|
+
const marginBottom = margin("bottom");
|
|
44
52
|
const geometry = {
|
|
45
53
|
widthTwips: readable ? width : A4_PORTRAIT.widthTwips,
|
|
46
54
|
heightTwips: readable ? height : A4_PORTRAIT.heightTwips,
|
|
@@ -79,6 +87,7 @@ function firstSectPrIn(root) {
|
|
|
79
87
|
export {
|
|
80
88
|
A4_BODY_WIDTH,
|
|
81
89
|
A4_PORTRAIT,
|
|
90
|
+
PX_PER_PT,
|
|
82
91
|
bodyHeightTwips,
|
|
83
92
|
bodyWidth,
|
|
84
93
|
bodyWidthTwips,
|
package/dist/docx/paraProps.d.ts
CHANGED
|
@@ -5,14 +5,12 @@
|
|
|
5
5
|
* When one paragraph is changed, the formatting we do not touch, such as tab stops and borders,
|
|
6
6
|
* must stay as the original text wrote it. So we never rebuild the fragment; we change it one child
|
|
7
7
|
* at a time, and within a child one attribute at a time.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* and reopening.
|
|
8
|
+
* What the edited fragment is drawn with is not decided here: the caller hands the fragment to the
|
|
9
|
+
* resolver (`formatting/resolve`), the same one opening the document uses, so a changed paragraph's
|
|
10
|
+
* display values are the same as they would be after saving and reopening.
|
|
12
11
|
*/
|
|
13
|
-
import type { LineSpacing, NumberingRef, ParagraphAlign
|
|
12
|
+
import type { LineSpacing, NumberingRef, ParagraphAlign } from "../model/format";
|
|
14
13
|
import type { LevelIndent } from "../numbering/parseNumbering";
|
|
15
|
-
import { type StyleTable } from "./formatting";
|
|
16
14
|
/** What to do with the indents */
|
|
17
15
|
export type IndentChange =
|
|
18
16
|
/** Leaves the indents the paragraph wrote down as they are */
|
|
@@ -33,32 +31,22 @@ export interface ListChange {
|
|
|
33
31
|
numbering: NumberingRef | null;
|
|
34
32
|
indent: IndentChange;
|
|
35
33
|
}
|
|
36
|
-
/** The paragraph formatting we operated on
|
|
34
|
+
/** The paragraph formatting we operated on. A null `pPr` is a fragment with nothing left in it */
|
|
37
35
|
export interface ParagraphProps {
|
|
38
36
|
pPr: string | null;
|
|
39
|
-
format: ParagraphFormat | null;
|
|
40
|
-
/** The character formatting the style the paragraph now wears lays down (`styleRun` in `schema`) */
|
|
41
|
-
styleRun: RunFormat | null;
|
|
42
37
|
}
|
|
43
|
-
|
|
44
|
-
* Reads the fragment we operated on back along the same path used when opening the document.
|
|
45
|
-
*
|
|
46
|
-
* The values of the style the paragraph wears are laid down underneath (the same order as import),
|
|
47
|
-
* which for a paragraph pointing at no style of its own is the document's default paragraph style.
|
|
48
|
-
*/
|
|
49
|
-
export declare function readParagraphProps(pPr: string | null, styles?: StyleTable, defaultStyleId?: string | null): ParagraphFormat | null;
|
|
50
|
-
export declare function withListNumbering(pPr: string | null, change: ListChange, styles?: StyleTable, defaultStyleId?: string | null): ParagraphProps | null;
|
|
38
|
+
export declare function withListNumbering(pPr: string | null, change: ListChange): ParagraphProps | null;
|
|
51
39
|
/**
|
|
52
40
|
* The result of moving the paragraph's own left indent to `leftTwips`.
|
|
53
41
|
* Everything else the indent recorded - the hanging or first-line indent, the right indent -
|
|
54
42
|
* stays as it was.
|
|
55
43
|
*/
|
|
56
|
-
export declare function withLeftIndent(pPr: string | null, leftTwips: number
|
|
44
|
+
export declare function withLeftIndent(pPr: string | null, leftTwips: number): ParagraphProps | null;
|
|
57
45
|
/**
|
|
58
46
|
* The result of changing the line spacing. The space above and below the paragraph is left as it was.
|
|
59
47
|
* `auto` states the multiple in 240ths of a line; the other rules pin the height down in twips.
|
|
60
48
|
*/
|
|
61
|
-
export declare function withLineSpacing(pPr: string | null, spacing: LineSpacing
|
|
49
|
+
export declare function withLineSpacing(pPr: string | null, spacing: LineSpacing): ParagraphProps | null;
|
|
62
50
|
/**
|
|
63
51
|
* The result of pointing the paragraph at a named style.
|
|
64
52
|
* A null id takes the pStyle away, which is how the default style is worn.
|
|
@@ -66,9 +54,9 @@ export declare function withLineSpacing(pPr: string | null, spacing: LineSpacing
|
|
|
66
54
|
* Nothing else in the fragment moves: as in Word, a style is applied by naming it and not by
|
|
67
55
|
* copying its values into the paragraph, so the direct formatting written here keeps beating it.
|
|
68
56
|
*/
|
|
69
|
-
export declare function withParagraphStyle(pPr: string | null, styleId: string | null
|
|
57
|
+
export declare function withParagraphStyle(pPr: string | null, styleId: string | null): ParagraphProps | null;
|
|
70
58
|
/**
|
|
71
59
|
* The result of changing the paragraph alignment.
|
|
72
60
|
* One alignment is always on, so we provide no way to withdraw the setting (the same as Word).
|
|
73
61
|
*/
|
|
74
|
-
export declare function withParagraphAlign(pPr: string | null, align: ParagraphAlign
|
|
62
|
+
export declare function withParagraphAlign(pPr: string | null, align: ParagraphAlign): ParagraphProps | null;
|
package/dist/docx/paraProps.js
CHANGED
|
@@ -1,26 +1,23 @@
|
|
|
1
1
|
// src/docx/paraProps.ts
|
|
2
2
|
import {
|
|
3
|
-
attrPairs,
|
|
4
3
|
elementXml,
|
|
4
|
+
wAttrValue,
|
|
5
5
|
withoutAttrs
|
|
6
6
|
} from "../ooxml/element.js";
|
|
7
7
|
import { wName } from "../ooxml/names.js";
|
|
8
8
|
import { setAttr } from "../ooxml/precedence.js";
|
|
9
|
-
import { childByLocalName, localPart } from "../ooxml/xml.js";
|
|
10
9
|
import {
|
|
11
|
-
|
|
12
|
-
layerRunFormat,
|
|
13
|
-
NO_STYLES,
|
|
14
|
-
paragraphStyleFormat,
|
|
15
|
-
readParagraphFormat
|
|
16
|
-
} from "./formatting.js";
|
|
17
|
-
import {
|
|
18
|
-
P_PR_ORDER,
|
|
10
|
+
childElement,
|
|
19
11
|
parseProps,
|
|
20
|
-
parsePropsXml,
|
|
21
12
|
renderProps,
|
|
22
|
-
|
|
23
|
-
} from "
|
|
13
|
+
setChild
|
|
14
|
+
} from "../ooxml/props.js";
|
|
15
|
+
import {
|
|
16
|
+
ST_DecimalNumber,
|
|
17
|
+
ST_SignedTwipsMeasure,
|
|
18
|
+
TWIPS_PER_PT
|
|
19
|
+
} from "../ooxml/simpleTypes.js";
|
|
20
|
+
import { LINE_UNITS_PER_LINE } from "./formatting.js";
|
|
24
21
|
var LEFT_IND_ATTRS = ["left", "start", "leftChars", "startChars"];
|
|
25
22
|
var LEFT_TWIPS_IND_ATTRS = ["left", "start"];
|
|
26
23
|
var HANGING_IND_ATTRS = ["hanging", "hangingChars"];
|
|
@@ -29,15 +26,6 @@ var FIRST_LINE_IND_ATTRS = [
|
|
|
29
26
|
"firstLine",
|
|
30
27
|
"firstLineChars"
|
|
31
28
|
];
|
|
32
|
-
function readParagraphProps(pPr, styles = NO_STYLES, defaultStyleId = null) {
|
|
33
|
-
const direct = pPr === null ? null : readParagraphFormat(parsePropsXml(pPr));
|
|
34
|
-
const style = paragraphStyleFormat(pPr, styles, defaultStyleId);
|
|
35
|
-
return layerParagraphFormat(style?.paragraph ?? {}, direct);
|
|
36
|
-
}
|
|
37
|
-
function readParagraphStyleRun(pPr, styles = NO_STYLES, defaultStyleId = null) {
|
|
38
|
-
const style = paragraphStyleFormat(pPr, styles, defaultStyleId);
|
|
39
|
-
return layerRunFormat(style?.run ?? {}, null);
|
|
40
|
-
}
|
|
41
29
|
function numPrXml(ref) {
|
|
42
30
|
if (!ref) return null;
|
|
43
31
|
return elementXml(
|
|
@@ -66,91 +54,75 @@ function indXml(attrs) {
|
|
|
66
54
|
function nextIndXml(ind, change) {
|
|
67
55
|
if (change.kind === "keep") return void 0;
|
|
68
56
|
if (change.kind === "clearHanging") {
|
|
69
|
-
return indXml(withoutAttrs(
|
|
57
|
+
return indXml(withoutAttrs(ind, HANGING_IND_ATTRS));
|
|
70
58
|
}
|
|
71
59
|
const dropped = [...LEFT_IND_ATTRS, ...FIRST_LINE_IND_ATTRS];
|
|
72
60
|
return indXml([
|
|
73
61
|
...levelIndAttrs(change.indent),
|
|
74
|
-
...withoutAttrs(
|
|
62
|
+
...withoutAttrs(ind, dropped)
|
|
75
63
|
]);
|
|
76
64
|
}
|
|
77
|
-
function leftIndAttrs(
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
)?.[0];
|
|
65
|
+
function leftIndAttrs(original, leftTwips) {
|
|
66
|
+
const slot = LEFT_TWIPS_IND_ATTRS.find(
|
|
67
|
+
(name) => wAttrValue(original, name) !== null
|
|
68
|
+
);
|
|
82
69
|
const written = leftTwips > 0 ? `${leftTwips}` : null;
|
|
83
70
|
const kept = withoutAttrs(
|
|
84
71
|
original,
|
|
85
|
-
LEFT_IND_ATTRS.filter(
|
|
86
|
-
(name) => slot === void 0 || name !== localPart(slot)
|
|
87
|
-
)
|
|
72
|
+
LEFT_IND_ATTRS.filter((name) => name !== slot)
|
|
88
73
|
);
|
|
89
|
-
if (slot !== void 0) return setAttr(kept, "ind",
|
|
74
|
+
if (slot !== void 0) return setAttr(kept, "ind", slot, written);
|
|
90
75
|
return written === null ? kept : [[wName("left"), written], ...kept];
|
|
91
76
|
}
|
|
92
77
|
var EMPTY_P_PR = { tag: "w:pPr", attrs: null, children: [] };
|
|
93
|
-
function editParagraphProps(pPr,
|
|
78
|
+
function editParagraphProps(pPr, plan) {
|
|
94
79
|
const props = pPr === null ? EMPTY_P_PR : parseProps(pPr);
|
|
95
|
-
|
|
96
|
-
|
|
80
|
+
if (!props) return null;
|
|
81
|
+
const edits = plan(props);
|
|
82
|
+
if (!edits) return null;
|
|
97
83
|
const rendered = renderProps(
|
|
98
|
-
|
|
99
|
-
(kept, [name, xml]) => setPropsChild(kept, name, xml, P_PR_ORDER),
|
|
100
|
-
props
|
|
101
|
-
)
|
|
84
|
+
edits.reduce((kept, [name, xml]) => setChild(kept, name, xml), props)
|
|
102
85
|
);
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
pPr: next,
|
|
106
|
-
format: readParagraphProps(next, styles, defaultStyleId),
|
|
107
|
-
styleRun: readParagraphStyleRun(next, styles, defaultStyleId)
|
|
108
|
-
};
|
|
86
|
+
return { pPr: rendered === "" ? null : rendered };
|
|
109
87
|
}
|
|
110
|
-
function withListNumbering(pPr, change
|
|
111
|
-
return editParagraphProps(pPr,
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
);
|
|
88
|
+
function withListNumbering(pPr, change) {
|
|
89
|
+
return editParagraphProps(pPr, (props) => {
|
|
90
|
+
const current = childElement(props, "ind");
|
|
91
|
+
if (!current) return null;
|
|
92
|
+
const ind = nextIndXml(current.attrs, change.indent);
|
|
116
93
|
const numPr = ["numPr", numPrXml(change.numbering)];
|
|
117
94
|
return ind === void 0 ? [numPr] : [numPr, ["ind", ind]];
|
|
118
95
|
});
|
|
119
96
|
}
|
|
120
|
-
function withLeftIndent(pPr, leftTwips
|
|
121
|
-
return editParagraphProps(pPr,
|
|
122
|
-
const ind =
|
|
123
|
-
|
|
97
|
+
function withLeftIndent(pPr, leftTwips) {
|
|
98
|
+
return editParagraphProps(pPr, (props) => {
|
|
99
|
+
const ind = childElement(props, "ind");
|
|
100
|
+
if (!ind) return null;
|
|
101
|
+
return [["ind", indXml(leftIndAttrs(ind.attrs, leftTwips))]];
|
|
124
102
|
});
|
|
125
103
|
}
|
|
126
104
|
function spacingAttrs(spacing, edits) {
|
|
127
|
-
const changed = attrPairs(spacing).map(
|
|
128
|
-
([name, value]) => [
|
|
129
|
-
name,
|
|
130
|
-
edits.find(([edited]) => edited === localPart(name))?.[1] ?? value
|
|
131
|
-
]
|
|
132
|
-
);
|
|
133
105
|
return edits.reduce(
|
|
134
106
|
(attrs, [name, value]) => setAttr(attrs, "spacing", name, value),
|
|
135
|
-
|
|
107
|
+
spacing
|
|
136
108
|
);
|
|
137
109
|
}
|
|
138
|
-
function withLineSpacing(pPr, spacing
|
|
139
|
-
const line = spacing.rule === "auto" ? Math.round(spacing.lines *
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
110
|
+
function withLineSpacing(pPr, spacing) {
|
|
111
|
+
const line = spacing.rule === "auto" ? ST_DecimalNumber.format(Math.round(spacing.lines * LINE_UNITS_PER_LINE)) : ST_SignedTwipsMeasure.format(Math.round(spacing.pt * TWIPS_PER_PT));
|
|
112
|
+
if (line === null) return null;
|
|
113
|
+
return editParagraphProps(pPr, (props) => {
|
|
114
|
+
const current = childElement(props, "spacing");
|
|
115
|
+
if (!current) return null;
|
|
116
|
+
const attrs = spacingAttrs(current.attrs, [
|
|
117
|
+
["line", line],
|
|
144
118
|
["lineRule", spacing.rule]
|
|
145
119
|
]);
|
|
146
120
|
return [["spacing", elementXml(wName("spacing"), attrs)]];
|
|
147
121
|
});
|
|
148
122
|
}
|
|
149
|
-
function withParagraphStyle(pPr, styleId
|
|
123
|
+
function withParagraphStyle(pPr, styleId) {
|
|
150
124
|
const pStyle = styleId === null ? null : elementXml(wName("pStyle"), [[wName("val"), styleId]]);
|
|
151
|
-
return editParagraphProps(pPr,
|
|
152
|
-
["pStyle", pStyle]
|
|
153
|
-
]);
|
|
125
|
+
return editParagraphProps(pPr, () => [["pStyle", pStyle]]);
|
|
154
126
|
}
|
|
155
127
|
var JC_BY_ALIGN = {
|
|
156
128
|
left: "left",
|
|
@@ -158,12 +130,11 @@ var JC_BY_ALIGN = {
|
|
|
158
130
|
right: "right",
|
|
159
131
|
justify: "both"
|
|
160
132
|
};
|
|
161
|
-
function withParagraphAlign(pPr, align
|
|
133
|
+
function withParagraphAlign(pPr, align) {
|
|
162
134
|
const jc = elementXml(wName("jc"), [[wName("val"), JC_BY_ALIGN[align]]]);
|
|
163
|
-
return editParagraphProps(pPr,
|
|
135
|
+
return editParagraphProps(pPr, () => [["jc", jc]]);
|
|
164
136
|
}
|
|
165
137
|
export {
|
|
166
|
-
readParagraphProps,
|
|
167
138
|
withLeftIndent,
|
|
168
139
|
withLineSpacing,
|
|
169
140
|
withListNumbering,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The contract every part an export writes beside the body is written under.
|
|
3
|
+
*
|
|
4
|
+
* A planner answers with the parts it rewrites, keyed by path, and declares what a part it adds
|
|
5
|
+
* needs through the two writers the context carries: a relationship from the main part and a
|
|
6
|
+
* content type. Those two parts are written once at the end from everything every planner asked
|
|
7
|
+
* for, so no planner writes either of them itself and none can write over what another declared.
|
|
8
|
+
*/
|
|
9
|
+
import type { Node as PMNode } from "prosemirror-model";
|
|
10
|
+
import { type ContentTypeWriter } from "./packageParts";
|
|
11
|
+
import { type RelationshipWriter } from "./relationships";
|
|
12
|
+
import type { SessionStore } from "./session";
|
|
13
|
+
export interface PartPlanContext {
|
|
14
|
+
readonly relationships: RelationshipWriter;
|
|
15
|
+
readonly contentTypes: ContentTypeWriter;
|
|
16
|
+
}
|
|
17
|
+
export interface PartPlanner {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
/** The parts to write, keyed by path, and null when the document gives this planner nothing to write */
|
|
20
|
+
plan(doc: PMNode, session: SessionStore, context: PartPlanContext): ReadonlyMap<string, Uint8Array> | null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Every part the planners write, folded into one map in planner order.
|
|
24
|
+
*
|
|
25
|
+
* The body and context-owned parts are reserved. Earlier writes include media planned before
|
|
26
|
+
* the body, so no later planner can overwrite those bytes either. Part names are compared
|
|
27
|
+
* without regard to case, as package part names are.
|
|
28
|
+
*/
|
|
29
|
+
export declare function runPartPlanners(planners: readonly PartPlanner[], doc: PMNode, session: SessionStore, context: PartPlanContext, prior?: ReadonlyMap<string, Uint8Array>): Map<string, Uint8Array>;
|
|
30
|
+
/**
|
|
31
|
+
* Every rewritten XML part has to read back as XML, and a refusal names the part.
|
|
32
|
+
*
|
|
33
|
+
* The parts are spliced as text, so this is where a splice that cut in the wrong place shows up:
|
|
34
|
+
* before the package is repacked rather than when somebody opens it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function assertPartsParse(replacements: ReadonlyMap<string, Uint8Array>, contentTypes?: Uint8Array): void;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/docx/partPlan.ts
|
|
2
|
+
import { DocxExportError } from "../ooxml/errors.js";
|
|
3
|
+
import { decodeUtf8, parseXml } from "../ooxml/xml.js";
|
|
4
|
+
import {
|
|
5
|
+
CONTENT_TYPES_PATH,
|
|
6
|
+
declaredXmlParts
|
|
7
|
+
} from "./packageParts.js";
|
|
8
|
+
import { relsPathOf } from "./relationships.js";
|
|
9
|
+
function runPartPlanners(planners, doc, session, context, prior = /* @__PURE__ */ new Map()) {
|
|
10
|
+
const owned = new Set(
|
|
11
|
+
[
|
|
12
|
+
CONTENT_TYPES_PATH,
|
|
13
|
+
relsPathOf(session.mainPartPath),
|
|
14
|
+
session.mainPartPath
|
|
15
|
+
].map((path) => path.toLowerCase())
|
|
16
|
+
);
|
|
17
|
+
const parts = new Map(prior);
|
|
18
|
+
const written = new Set(
|
|
19
|
+
Array.from(prior.keys(), (path) => path.toLowerCase())
|
|
20
|
+
);
|
|
21
|
+
for (const planner of planners) {
|
|
22
|
+
for (const [path, bytes] of planner.plan(doc, session, context) ?? []) {
|
|
23
|
+
if (owned.has(path.toLowerCase())) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`the ${planner.name} planner wrote ${path}, which another export writer owns`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
if (written.has(path.toLowerCase())) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`the ${planner.name} planner wrote ${path}, which an earlier writer wrote already`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
parts.set(path, bytes);
|
|
34
|
+
written.add(path.toLowerCase());
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return parts;
|
|
38
|
+
}
|
|
39
|
+
var XML_PART = /\.(?:xml|rels)$/i;
|
|
40
|
+
function assertPartsParse(replacements, contentTypes) {
|
|
41
|
+
const paths = [...replacements.keys()];
|
|
42
|
+
const declared = contentTypes && paths.some((path) => !XML_PART.test(path)) ? declaredXmlParts(contentTypes, paths) : /* @__PURE__ */ new Set();
|
|
43
|
+
for (const [path, bytes] of replacements) {
|
|
44
|
+
if (!XML_PART.test(path) && !declared.has(path)) continue;
|
|
45
|
+
try {
|
|
46
|
+
parseXml(decodeUtf8(bytes).text);
|
|
47
|
+
} catch (cause) {
|
|
48
|
+
throw new DocxExportError(
|
|
49
|
+
"malformed-xml",
|
|
50
|
+
`${path} as written could not be parsed`,
|
|
51
|
+
{ cause }
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
assertPartsParse,
|
|
58
|
+
runPartPlanners
|
|
59
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a protection level lets a change rewrite, as one value every side of the package reads.
|
|
3
|
+
*
|
|
4
|
+
* A level says three things: which parts of the package a change may write, what an entry in one
|
|
5
|
+
* of those parts may look like, and how the story reads once the markup the level is about is
|
|
6
|
+
* taken out of it. The part planners write those parts and the verifier judges them, and each
|
|
7
|
+
* carried its own copy of the first two with nothing but a test holding them together. Declaring
|
|
8
|
+
* them once means a part the writer starts adding is a part the verifier already knows.
|
|
9
|
+
*
|
|
10
|
+
* A level with no policy registered is one no file is judged under: `protectionPolicyFor` answers
|
|
11
|
+
* undefined, and a caller holding the level alone has nothing to run.
|
|
12
|
+
*/
|
|
13
|
+
import { type XmlParser } from "../ooxml/xml";
|
|
14
|
+
import type { EditableComments, EditingProtection } from "../schema/protection";
|
|
15
|
+
import { type DocxBytes } from "./importDocx";
|
|
16
|
+
import type { SessionStore } from "./session";
|
|
17
|
+
import { type Story } from "./storyProjection";
|
|
18
|
+
/** One entry of a story part: the element read out of it, and its text for the byte-for-byte case */
|
|
19
|
+
export interface StoryEntry {
|
|
20
|
+
el: Element;
|
|
21
|
+
xml: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* How a part's entries are read.
|
|
25
|
+
*
|
|
26
|
+
* `arrived` is the reference a submission is held against rather than something to judge, so it
|
|
27
|
+
* takes what it can read and passes over the rest, leaving an entry standing on something
|
|
28
|
+
* unreadable to be judged as one that appeared. `submitted` is what is judged.
|
|
29
|
+
*/
|
|
30
|
+
export type EntryReading = "arrived" | "submitted";
|
|
31
|
+
/** What a judgement may be told about the reader beyond the two documents */
|
|
32
|
+
export interface PolicyOptions {
|
|
33
|
+
editableComments: EditableComments;
|
|
34
|
+
}
|
|
35
|
+
/** What a judgement over two files may be told beyond that */
|
|
36
|
+
export interface VerifyOptions extends PolicyOptions {
|
|
37
|
+
xmlParser?: XmlParser;
|
|
38
|
+
}
|
|
39
|
+
/** One package part a protection lets an editor rewrite, and how a rewritten entry is judged */
|
|
40
|
+
export interface StoryPartKind {
|
|
41
|
+
relType: string;
|
|
42
|
+
contentType: string;
|
|
43
|
+
/** Where the reader found this part, and null for a package that holds none */
|
|
44
|
+
pathIn(session: SessionStore): string | null;
|
|
45
|
+
/**
|
|
46
|
+
* Where the part goes when it is written: where it already sits, or the first name in the
|
|
47
|
+
* story's own folder that no part of the package has taken.
|
|
48
|
+
*/
|
|
49
|
+
writePathIn(session: SessionStore): string;
|
|
50
|
+
/**
|
|
51
|
+
* The part's entries keyed by the id the story or a sibling part refers to them by, and null
|
|
52
|
+
* for a submitted part this editor's writer could not have put out at all.
|
|
53
|
+
*/
|
|
54
|
+
entriesIn(session: SessionStore, reading: EntryReading): ReadonlyMap<string, StoryEntry> | null;
|
|
55
|
+
/**
|
|
56
|
+
* Whether the element the entries stand in came back as it left, save for the compatibility
|
|
57
|
+
* markup the writer declares on a part it writes a thread key into.
|
|
58
|
+
*/
|
|
59
|
+
rootKept(before: SessionStore, after: SessionStore): boolean;
|
|
60
|
+
/** The keys this file still stands behind, which is what an entry has to be keyed by */
|
|
61
|
+
referents(story: Story): ReadonlySet<string>;
|
|
62
|
+
/** Grammar alone: whether the entry is one this editor's writer could have put out, whoever it belongs to */
|
|
63
|
+
wellFormed(entry: Element): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Whether the entry came back differing from the one that arrived in nothing but a change this
|
|
66
|
+
* protection leaves to everyone. Such an entry is nobody's rewrite, so it is held neither to
|
|
67
|
+
* the grammar this editor writes in nor to who owns it.
|
|
68
|
+
*/
|
|
69
|
+
anyonesChange(entry: Element, original: Element): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Permission alone: whether `authorId` may have written (`original === null`) or rewritten this
|
|
72
|
+
* entry under `options`. `session` is the submission's, so a kind can look across at a sibling
|
|
73
|
+
* part, the way a comment's author resolves through the people part.
|
|
74
|
+
*/
|
|
75
|
+
allowed(entry: Element, original: Element | null, authorId: string, options: PolicyOptions, session: SessionStore): boolean;
|
|
76
|
+
}
|
|
77
|
+
/** The two reasons the package comparison answers with, whatever the policy */
|
|
78
|
+
export type PackageReason = "part-changed" | "relationship-changed";
|
|
79
|
+
/**
|
|
80
|
+
* The shape every verdict takes: a story reason carries no part, a part reason names the part it
|
|
81
|
+
* was reached over. A public verdict keeps its own declaration and is one instance of this.
|
|
82
|
+
*/
|
|
83
|
+
export type ChangeVerdict<StoryReason extends string, PartReason extends string> = {
|
|
84
|
+
ok: true;
|
|
85
|
+
} | {
|
|
86
|
+
ok: false;
|
|
87
|
+
reason: StoryReason;
|
|
88
|
+
} | {
|
|
89
|
+
ok: false;
|
|
90
|
+
reason: PartReason;
|
|
91
|
+
part: string;
|
|
92
|
+
};
|
|
93
|
+
export interface ProtectionPolicy<StoryReason extends string, PartReason extends string> {
|
|
94
|
+
level: EditingProtection;
|
|
95
|
+
parts: readonly StoryPartKind[];
|
|
96
|
+
/** The reason a rewritten entry is refused under when no part takes it as well formed and allowed */
|
|
97
|
+
rejectedMarkup: PartReason;
|
|
98
|
+
/** Whether the story reads the same once this protection's own markup is taken out */
|
|
99
|
+
storyKept(before: Story, after: Story, authorId: string, options: PolicyOptions): ChangeVerdict<StoryReason, PartReason>;
|
|
100
|
+
}
|
|
101
|
+
/** Records the policy under its level and hands it back, so a module declares and registers at once */
|
|
102
|
+
export declare function registerProtectionPolicy<S extends string, P extends string>(policy: ProtectionPolicy<S, P>): ProtectionPolicy<S, P>;
|
|
103
|
+
/**
|
|
104
|
+
* The policy for a level, and undefined for a level no loaded module declares one for, `none` and
|
|
105
|
+
* `readOnly` among them.
|
|
106
|
+
*/
|
|
107
|
+
export declare function protectionPolicyFor(level: EditingProtection): ProtectionPolicy<string, string> | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* Whether the parts this protection lets an edit rewrite came back holding only entries this
|
|
110
|
+
* editor writes, each of them one this author was in a position to write.
|
|
111
|
+
*
|
|
112
|
+
* This runs after the story has been judged, so a comment rewritten, re-anchored or deleted by the
|
|
113
|
+
* wrong hand is already named for what it is. What is left to this is everything the story cannot
|
|
114
|
+
* show: markup forged into a body, an entry nothing refers to, an identity recorded for somebody
|
|
115
|
+
* else.
|
|
116
|
+
*/
|
|
117
|
+
export declare function partsKept<P extends string>(policy: ProtectionPolicy<string, P>, before: Story, after: Story, authorId: string, options: PolicyOptions): ChangeVerdict<never, P>;
|
|
118
|
+
/**
|
|
119
|
+
* Whether the submitted file differs from the original in nothing this protection forbids.
|
|
120
|
+
*
|
|
121
|
+
* Both files are opened inside the one parser scope, so the parser named here is the parser both
|
|
122
|
+
* reads go through even though neither `importDocx` call is given it. The package is judged first,
|
|
123
|
+
* then the story, then the parts the policy excused from the package comparison: the parts are
|
|
124
|
+
* last so that an edit the wrong hand made is named for the edit rather than for the part it was
|
|
125
|
+
* written across.
|
|
126
|
+
*/
|
|
127
|
+
export declare function verifyChange<S extends string, P extends string>(policy: ProtectionPolicy<S, P>, original: DocxBytes, submitted: DocxBytes, authorId: string, { xmlParser, ...options }: VerifyOptions): ChangeVerdict<S, P | PackageReason>;
|