@stll/folio-core 0.60.0 → 0.62.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/README.md +13 -0
- package/dist/ai-edits/apply.js +139 -32
- package/dist/ai-edits/headless.d.ts +3 -0
- package/dist/ai-edits/headless.js +70 -75
- package/dist/ai-edits/note-story-inventory.d.ts +7 -0
- package/dist/ai-edits/note-story-inventory.js +11 -0
- package/dist/ai-edits/read.js +16 -9
- package/dist/ai-edits/revisionStretches.js +52 -1
- package/dist/ai-edits/table-row-column-mutations.js +6 -1
- package/dist/compare/reproducible-package.js +2 -1
- package/dist/compare/verification.js +1 -0
- package/dist/compat/eigenpal.d.ts +3 -1
- package/dist/controller/canonicalClipboard.js +2 -1
- package/dist/controller/canonicalListRendering.d.ts +4 -6
- package/dist/controller/canonicalListRendering.js +3 -157
- package/dist/controller/headerFooterEditorManager.d.ts +1 -0
- package/dist/controller/headerFooterEditorManager.js +11 -0
- package/dist/controller/hiddenEditorManager.d.ts +1 -1
- package/dist/controller/hiddenEditorManager.js +3 -1
- package/dist/controller/layoutPipeline.d.ts +3 -1
- package/dist/controller/layoutPipeline.js +13 -4
- package/dist/controller/layoutSession.d.ts +2 -1
- package/dist/controller/noteEditorManager.d.ts +1 -1
- package/dist/display-list/build/imagePrimitives.js +28 -12
- package/dist/display-list/build/paragraphPrimitives.js +15 -2
- package/dist/display-list/dom/renderDisplayListToDom.js +8 -5
- package/dist/display-list/types.d.ts +7 -3
- package/dist/docx/canonicalStoryRepack.js +3 -2
- package/dist/docx/commentTextParagraph.d.ts +6 -0
- package/dist/docx/commentTextParagraph.js +15 -0
- package/dist/docx/conformance.js +6 -1
- package/dist/docx/diagramPreview.js +2 -1
- package/dist/docx/documentParser.d.ts +13 -1
- package/dist/docx/documentParser.js +1 -1
- package/dist/docx/encryption/compoundFile.d.ts +0 -8
- package/dist/docx/encryption/compoundFile.js +2 -1
- package/dist/docx/encryption/cryptoBytes.d.ts +2 -2
- package/dist/docx/encryption/cryptoBytes.js +3 -18
- package/dist/docx/encryption/encryptionInfo.d.ts +0 -5
- package/dist/docx/encryption/encryptionInfo.js +2 -1
- package/dist/docx/ensureParaIds.js +2 -1
- package/dist/docx/groupDrawingParser.js +28 -13
- package/dist/docx/listModelNormalization.d.ts +10 -0
- package/dist/docx/listModelNormalization.js +166 -0
- package/dist/docx/metadataPrivacy.js +3 -2
- package/dist/docx/metafileRaster.js +2 -1
- package/dist/docx/packageParts.js +6 -5
- package/dist/docx/paragraphParser.js +6 -4
- package/dist/docx/parser.js +5 -4
- package/dist/docx/preservedRunContent.js +11 -2
- package/dist/docx/removeHeaderFooterParts.js +8 -7
- package/dist/docx/replyToComment.js +3 -13
- package/dist/docx/rezip.js +88 -89
- package/dist/docx/runParser.js +12 -6
- package/dist/docx/runPropertyPresence.d.ts +11 -0
- package/dist/docx/runPropertyPresence.js +14 -0
- package/dist/docx/selectiveSave.js +15 -14
- package/dist/docx/serializer/runSerializer.js +3 -2
- package/dist/docx/serializer/tableSerializer.js +2 -1
- package/dist/docx/server/applyDocxXmlPatchProposal.js +2 -2
- package/dist/docx/server/boundedArchive.js +2 -1
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +2 -1
- package/dist/docx/server/inspectDocxPackage.js +5 -1
- package/dist/docx/server/validateDocxConformance.js +2 -1
- package/dist/docx/tableParser.js +7 -4
- package/dist/docx/tableRowPropertyPresence.d.ts +9 -0
- package/dist/docx/tableRowPropertyPresence.js +15 -0
- package/dist/docx/unzip.js +2 -1
- package/dist/docx/xmlEncoding.js +6 -5
- package/dist/fonts/fontsourceFaces.js +2 -1
- package/dist/fonts/sfnt/parse.js +6 -9
- package/dist/fonts/sfnt/subset.js +2 -1
- package/dist/fonts/sfnt/tables.js +2 -3
- package/dist/fonts/sfnt/woff.js +2 -1
- package/dist/i18n/messages/ar.gen.js +1 -0
- package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
- package/dist/i18n/messages/catalogs.gen.js +17 -0
- package/dist/i18n/messages/cs.gen.js +1 -0
- package/dist/i18n/messages/de.gen.js +1 -0
- package/dist/i18n/messages/en.gen.js +1 -0
- package/dist/i18n/messages/es.gen.js +1 -0
- package/dist/i18n/messages/et.gen.js +1 -0
- package/dist/i18n/messages/fr.gen.js +1 -0
- package/dist/i18n/messages/he.gen.js +1 -0
- package/dist/i18n/messages/hi.gen.js +1 -0
- package/dist/i18n/messages/hu.gen.js +1 -0
- package/dist/i18n/messages/lt.gen.js +1 -0
- package/dist/i18n/messages/lv.gen.js +1 -0
- package/dist/i18n/messages/messages.gen.d.ts +1 -0
- package/dist/i18n/messages/pl.gen.js +1 -0
- package/dist/i18n/messages/pt-BR.gen.js +1 -0
- package/dist/i18n/messages/sk.gen.js +1 -0
- package/dist/i18n/messages/tr.gen.js +1 -0
- package/dist/i18n/messages/zh-CN.gen.js +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/internal/revisionResolutionInline.d.ts +2 -1
- package/dist/internal/revisionResolutionInline.js +22 -1
- package/dist/internal/wholeStoryRevisionResolution.js +24 -7
- package/dist/layout-bridge/convert/endnoteLayout.d.ts +1 -1
- package/dist/layout-bridge/convert/endnoteLayout.js +1 -3
- package/dist/layout-bridge/convert/flowConversionShared.d.ts +2 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +4 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +13 -3
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +5 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +12 -15
- package/dist/layout-bridge/convert/paragraphRuns.js +2 -1
- package/dist/layout-engine/index.js +2 -2
- package/dist/layout-engine/measure/crossRunWords.js +12 -2
- package/dist/layout-engine/noteAreaFlow.d.ts +4 -4
- package/dist/layout-engine/noteAreaFlow.js +12 -6
- package/dist/layout-engine/types.d.ts +5 -2
- package/dist/layout-painter/index.d.ts +1 -1
- package/dist/managers/AutoSaveManager.js +2 -1
- package/dist/managers/EditorModeManager.d.ts +10 -1
- package/dist/managers/EditorModeManager.js +15 -1
- package/dist/managers/FindReplaceManager.d.ts +13 -6
- package/dist/managers/FindReplaceManager.js +15 -9
- package/dist/managers/TableSelectionManager.d.ts +1 -1
- package/dist/managers/autoSaveCodec.js +2 -6
- package/dist/managers/reviewDisplayMode.d.ts +8 -0
- package/dist/managers/reviewDisplayMode.js +15 -0
- package/dist/paged-layout/editorScrollRoot.d.ts +3 -1
- package/dist/paged-layout/editorScrollRoot.js +2 -2
- package/dist/paged-layout/pageText.d.ts +11 -2
- package/dist/paged-layout/pageText.js +8 -3
- package/dist/pdf/fonts.js +5 -14
- package/dist/pdf/images.js +4 -2
- package/dist/pdf/objects.d.ts +0 -11
- package/dist/pdf/objects.js +7 -9
- package/dist/pdf/writePdf.js +2 -1
- package/dist/prosemirror/commands/comments.js +51 -13
- package/dist/prosemirror/commentRangeInlineAtoms.d.ts +40 -0
- package/dist/prosemirror/commentRangeInlineAtoms.js +33 -0
- package/dist/prosemirror/containerFinalParagraph.d.ts +8 -8
- package/dist/prosemirror/containerFinalParagraph.js +8 -8
- package/dist/prosemirror/conversion/fromProseDoc.js +14 -8
- package/dist/prosemirror/conversion/toProseDoc.js +16 -23
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +16 -2
- package/dist/prosemirror/fieldRepresentation.js +4 -2
- package/dist/prosemirror/findReplaceSelection.d.ts +48 -2
- package/dist/prosemirror/findReplaceSelection.js +153 -38
- package/dist/prosemirror/inlineDisplayText.d.ts +10 -0
- package/dist/prosemirror/inlineDisplayText.js +29 -0
- package/dist/prosemirror/markupViewProjection.d.ts +10 -1
- package/dist/prosemirror/markupViewProjection.js +21 -3
- package/dist/prosemirror/plugins/transactionInvariants.js +6 -1
- package/dist/prosemirror/replacedAnnotations.d.ts +15 -1
- package/dist/prosemirror/replacedAnnotations.js +71 -12
- package/dist/prosemirror/runFormattingReconciliation.d.ts +3 -1
- package/dist/prosemirror/runFormattingReconciliation.js +10 -2
- package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +5 -7
- package/dist/prosemirror/runIdentityAcrossRevisions.js +66 -38
- package/dist/prosemirror/runPropertyChangeResolution.js +13 -1
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -1
- package/dist/prosemirror/trackedRunInlineAtoms.js +1 -1
- package/dist/utils/base64.d.ts +1 -31
- package/dist/utils/base64.js +19 -33
- package/dist/utils/clipboard.js +5 -4
- package/dist/utils/colorResolver.js +11 -2
- package/dist/utils/findMatchView.d.ts +32 -0
- package/dist/utils/findMatchView.js +406 -0
- package/dist/utils/findReplace.d.ts +67 -4
- package/dist/utils/findReplace.js +359 -37
- package/dist/utils/findSearchKey.d.ts +29 -0
- package/dist/utils/findSearchKey.js +34 -0
- package/dist/utils/replaceText.js +138 -94
- package/dist/utils/sliceText.d.ts +2 -0
- package/dist/utils/sliceText.js +2 -0
- package/dist/utils/textEncoding.d.ts +2 -0
- package/dist/utils/textEncoding.js +2 -0
- package/dist/utils/tiffConverter.d.ts +0 -9
- package/dist/utils/tiffConverter.js +2 -4
- package/package.json +51 -25
|
@@ -1,160 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { computeListMarker } from "../docx/listMarkerComputation.js";
|
|
3
|
-
import { listRenderingDefinitionsMatch } from "../docx/listRendering.js";
|
|
4
|
-
import { computeListRendering, getCachedNumberingMap } from "../docx/numberingParser.js";
|
|
5
|
-
import { assignParagraphIndentationProjection, cloneParagraphWithPropertySource, paragraphFormattingWithAuthoredIndentation, sameParagraphIndentationProjection } from "../docx/paragraphPropertySource.js";
|
|
6
|
-
import { effectiveParagraphNumberingReference, paragraphNumberingSourcesFromBoundary } from "../prosemirror/numberingAttr.js";
|
|
7
|
-
import { paragraphIndentationFromFormatting } from "../prosemirror/paragraphIndentation.js";
|
|
8
|
-
import { listIndentationProvenancePatch } from "../prosemirror/styles/resolvedStyleAttrs.js";
|
|
9
|
-
import { createStyleResolver } from "../prosemirror/styles/styleResolver.js";
|
|
10
|
-
import { canonicalJson } from "../utils/canonicalJson.js";
|
|
11
|
-
import { panic } from "better-result";
|
|
12
|
-
import { DOCUMENT_OP_TYPES, EMPTY_PROPERTY_SETS, OP_STORIES, registerParagraphPropertySnapshot, withBodyContent } from "@stll/docx-core/ops";
|
|
1
|
+
import { normalizeListModelRendering } from "../docx/listModelNormalization.js";
|
|
13
2
|
//#region src/controller/canonicalListRendering.ts
|
|
14
|
-
/** Canonical edits
|
|
15
|
-
const normalizeCanonicalListRendering =
|
|
16
|
-
const inverse = [];
|
|
17
|
-
const styles = createStyleResolver(document.package.styles);
|
|
18
|
-
const numbering = document.package.numbering === void 0 ? null : getCachedNumberingMap(document.package.numbering);
|
|
19
|
-
const normalizeBody = (body, story) => {
|
|
20
|
-
const counters = {
|
|
21
|
-
numbering,
|
|
22
|
-
listCounters: /* @__PURE__ */ new Map(),
|
|
23
|
-
abstractCounters: /* @__PURE__ */ new Map(),
|
|
24
|
-
restartedNumIds: /* @__PURE__ */ new Set(),
|
|
25
|
-
previousList: {
|
|
26
|
-
abstractNumId: null,
|
|
27
|
-
fromStyle: false,
|
|
28
|
-
numId: null
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
const content = body.content.map((paragraph) => {
|
|
32
|
-
if (paragraph.type !== "paragraph") return paragraph;
|
|
33
|
-
registerParagraphPropertySnapshot({
|
|
34
|
-
paragraph,
|
|
35
|
-
snapshot: paragraphFormattingWithAuthoredIndentation(paragraph)
|
|
36
|
-
});
|
|
37
|
-
const formatting = paragraph.formatting;
|
|
38
|
-
const next = cloneParagraphWithPropertySource(paragraph, {});
|
|
39
|
-
if (formatting?.numPrFromStyle !== void 0 && (formatting.numPr?.kind === "reference" || formatting.numPr?.kind === "none")) {
|
|
40
|
-
next.formatting = { ...formatting };
|
|
41
|
-
delete next.formatting.numPrFromStyle;
|
|
42
|
-
inverse.push({
|
|
43
|
-
type: DOCUMENT_OP_TYPES.SET_PARAGRAPH_PROPS,
|
|
44
|
-
story,
|
|
45
|
-
blockId: paragraph.paraId ?? panic("Canonical numbering normalization requires paragraph identities."),
|
|
46
|
-
patch: { numPrFromStyle: formatting.numPrFromStyle }
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
const numPr = effectiveParagraphNumberingReference(paragraphNumberingSourcesFromBoundary({
|
|
50
|
-
numPr: next.formatting?.numPr,
|
|
51
|
-
numPrFromStyle: next.formatting?.numPrFromStyle
|
|
52
|
-
}));
|
|
53
|
-
if (paragraph.listRendering === void 0 && formatting?.numPrFromStyle === void 0 && (numPr?.kind !== "reference" || numbering === null)) {
|
|
54
|
-
computeListMarker(paragraph, counters);
|
|
55
|
-
return paragraph;
|
|
56
|
-
}
|
|
57
|
-
const definition = numPr?.kind === "reference" && numbering !== null ? computeListRendering(numPr, numbering) : null;
|
|
58
|
-
const cached = paragraph.listRendering;
|
|
59
|
-
const rendering = definition !== null && cached !== void 0 && listRenderingDefinitionsMatch(cached, definition) ? { ...cached } : definition;
|
|
60
|
-
const authoredFormatting = paragraphFormattingWithAuthoredIndentation(next);
|
|
61
|
-
assignParagraphIndentationProjection({
|
|
62
|
-
paragraph: next,
|
|
63
|
-
authored: authoredFormatting,
|
|
64
|
-
inherited: paragraphIndentationFromFormatting(listIndentationProvenancePatch({
|
|
65
|
-
direct: paragraphIndentationFromFormatting(authoredFormatting),
|
|
66
|
-
styleFormatting: styles.resolveParagraphStyle(next.formatting?.styleId).paragraphFormatting,
|
|
67
|
-
numberingSource: next.formatting?.numPr?.kind === "reference" || next.formatting?.numPrFromStyle === void 0 ? "paragraph" : "style",
|
|
68
|
-
numPr: numPr?.kind === "reference" ? {
|
|
69
|
-
numId: numPr.numId,
|
|
70
|
-
ilvl: numPr.ilvl ?? 0
|
|
71
|
-
} : void 0,
|
|
72
|
-
numbering
|
|
73
|
-
})._resolvedFormatting) ?? {}
|
|
74
|
-
});
|
|
75
|
-
if (canonicalJson(paragraphIndentationFromFormatting(next.formatting)) !== canonicalJson(paragraphIndentationFromFormatting(formatting))) inverse.unshift({
|
|
76
|
-
type: DOCUMENT_OP_TYPES.SET_PARAGRAPH_PROPS,
|
|
77
|
-
story,
|
|
78
|
-
blockId: paragraph.paraId ?? panic("Canonical numbering normalization requires paragraph identities."),
|
|
79
|
-
patch: {
|
|
80
|
-
indentLeft: formatting?.indentLeft ?? null,
|
|
81
|
-
indentRight: formatting?.indentRight ?? null,
|
|
82
|
-
indentFirstLine: formatting?.indentFirstLine ?? null,
|
|
83
|
-
hangingIndent: formatting?.hangingIndent ?? null
|
|
84
|
-
},
|
|
85
|
-
whenEmpty: formatting === void 0 ? EMPTY_PROPERTY_SETS.OMIT : EMPTY_PROPERTY_SETS.KEEP
|
|
86
|
-
});
|
|
87
|
-
if (rendering === null) delete next.listRendering;
|
|
88
|
-
else {
|
|
89
|
-
const fields = foldListNumberFields(paragraph.content.map(unfoldedListNumberContent), () => void 0).fieldCount;
|
|
90
|
-
if (fields === 0) delete rendering.implicitChildLevelAdvances;
|
|
91
|
-
else rendering.implicitChildLevelAdvances = fields;
|
|
92
|
-
const template = rendering.markerTemplate ?? rendering.marker;
|
|
93
|
-
const fold = planParagraphListNumberFold(paragraph.content, !rendering.isBullet && template !== "" && !template.includes(" "));
|
|
94
|
-
if (fold.suffix === void 0) delete rendering.foldedMarkerSuffix;
|
|
95
|
-
else rendering.foldedMarkerSuffix = fold.suffix;
|
|
96
|
-
const child = numbering?.getLevel(rendering.numId, rendering.level + 1)?.pPr;
|
|
97
|
-
if (fold.suffix !== void 0 && child?.hangingIndent === true && child.indentFirstLine !== void 0 && child.indentFirstLine < 0) rendering.markerSecondSlotOffsetTwips = -child.indentFirstLine;
|
|
98
|
-
else delete rendering.markerSecondSlotOffsetTwips;
|
|
99
|
-
rendering.marker = rendering.foldedMarkerSuffix !== void 0 && !template.includes(" ") ? `${template}\t${rendering.foldedMarkerSuffix}` : template;
|
|
100
|
-
next.listRendering = rendering;
|
|
101
|
-
}
|
|
102
|
-
computeListMarker(next, counters);
|
|
103
|
-
registerParagraphPropertySnapshot({
|
|
104
|
-
paragraph: next,
|
|
105
|
-
snapshot: authoredFormatting
|
|
106
|
-
});
|
|
107
|
-
if (sameParagraphIndentationProjection({
|
|
108
|
-
left: next,
|
|
109
|
-
right: paragraph
|
|
110
|
-
}) && canonicalJson(next.formatting) === canonicalJson(formatting) && canonicalJson(next.listRendering) === canonicalJson(cached)) return paragraph;
|
|
111
|
-
return next;
|
|
112
|
-
});
|
|
113
|
-
return content.every((paragraph, index) => paragraph === body.content[index]) ? body : {
|
|
114
|
-
...body,
|
|
115
|
-
...withBodyContent(body, content)
|
|
116
|
-
};
|
|
117
|
-
};
|
|
118
|
-
const pkg = document.package;
|
|
119
|
-
const body = normalizeBody(pkg.document, OP_STORIES.MAIN);
|
|
120
|
-
const normalizeParts = (parts, kind) => {
|
|
121
|
-
if (parts === void 0) return parts;
|
|
122
|
-
const normalized = new Map([...parts].map(([id, part]) => [id, normalizeBody(part, {
|
|
123
|
-
kind,
|
|
124
|
-
rId: id
|
|
125
|
-
})]));
|
|
126
|
-
return [...normalized].every(([id, part]) => part === parts.get(id)) ? parts : normalized;
|
|
127
|
-
};
|
|
128
|
-
const normalizeNotes = (notes, kind) => {
|
|
129
|
-
if (notes === void 0) return notes;
|
|
130
|
-
const normalized = notes.map((note) => normalizeBody(note, {
|
|
131
|
-
kind,
|
|
132
|
-
id: note.id
|
|
133
|
-
}));
|
|
134
|
-
return normalized.every((note, index) => note === notes[index]) ? notes : normalized;
|
|
135
|
-
};
|
|
136
|
-
const headers = normalizeParts(pkg.headers, "header");
|
|
137
|
-
const footers = normalizeParts(pkg.footers, "footer");
|
|
138
|
-
const footnotes = normalizeNotes(pkg.footnotes, "footnote");
|
|
139
|
-
const endnotes = normalizeNotes(pkg.endnotes, "endnote");
|
|
140
|
-
if (body === pkg.document && headers === pkg.headers && footers === pkg.footers && footnotes === pkg.footnotes && endnotes === pkg.endnotes) return {
|
|
141
|
-
document,
|
|
142
|
-
inverse
|
|
143
|
-
};
|
|
144
|
-
return {
|
|
145
|
-
inverse,
|
|
146
|
-
document: {
|
|
147
|
-
...document,
|
|
148
|
-
package: {
|
|
149
|
-
...pkg,
|
|
150
|
-
document: body,
|
|
151
|
-
...headers === void 0 ? {} : { headers },
|
|
152
|
-
...footers === void 0 ? {} : { footers },
|
|
153
|
-
...footnotes === void 0 ? {} : { footnotes },
|
|
154
|
-
...endnotes === void 0 ? {} : { endnotes }
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
};
|
|
3
|
+
/** Canonical edits share list model normalization with the headless projection. */
|
|
4
|
+
const normalizeCanonicalListRendering = normalizeListModelRendering;
|
|
159
5
|
//#endregion
|
|
160
6
|
export { normalizeCanonicalListRendering };
|
|
@@ -18,6 +18,7 @@ type HeaderFooterEditorTransaction = {
|
|
|
18
18
|
type HeaderFooterEditorManagerDeps = {
|
|
19
19
|
createView?: ((mountNode: HTMLElement, props: DirectEditorProps) => EditorView) | undefined;
|
|
20
20
|
getDocument: () => document_d_exports.Document | null;
|
|
21
|
+
getReadOnly?: (() => boolean) | undefined;
|
|
21
22
|
getCanonicalApi?: (() => HiddenEditorApi | null) | undefined;
|
|
22
23
|
getExperimentalSession?: (() => "canonical" | undefined) | undefined;
|
|
23
24
|
onSessionRefusal?: ((reason: string, gap: CanonicalGap, error?: Error) => void) | undefined;
|
|
@@ -91,6 +91,15 @@ const createHeaderFooterEditorManager = (deps) => {
|
|
|
91
91
|
const sync = () => {
|
|
92
92
|
const host = deps.getHost();
|
|
93
93
|
if (!host) return;
|
|
94
|
+
const getReadOnly = deps.getReadOnly;
|
|
95
|
+
if (getReadOnly) {
|
|
96
|
+
const readOnly = getReadOnly();
|
|
97
|
+
for (const part of mounted.values()) {
|
|
98
|
+
const expectedEditable = !readOnly;
|
|
99
|
+
if (part.view.editable === expectedEditable) continue;
|
|
100
|
+
part.view.setProps({ editable: () => !getReadOnly() });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
94
103
|
if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting) && !deps.getCanonicalApi?.()?.getCanonicalStoryProjection(OP_STORIES.MAIN)) return;
|
|
95
104
|
const document = getOwnedDocument();
|
|
96
105
|
const styles = deps.getStyles();
|
|
@@ -179,11 +188,13 @@ const createHeaderFooterEditorManager = (deps) => {
|
|
|
179
188
|
}
|
|
180
189
|
const view = (deps.createView ?? createEditorView)(mountNode, {
|
|
181
190
|
...canonical.props,
|
|
191
|
+
editable: () => !(deps.getReadOnly?.() ?? false),
|
|
182
192
|
state: canonicalProjection ? EditorState.create({
|
|
183
193
|
doc: canonicalProjection,
|
|
184
194
|
plugins: [createDocumentStyleContextPlugin(styles), createDocumentNumberingPlugin(numbering)]
|
|
185
195
|
}) : buildInitialState(headerFooter, styles, theme, numbering, manager),
|
|
186
196
|
dispatchTransaction(transaction) {
|
|
197
|
+
if ((deps.getReadOnly?.() ?? false) && transaction.docChanged) return;
|
|
187
198
|
if (canonical.dispatch(transaction)) return;
|
|
188
199
|
const nextState = view.state.apply(transaction);
|
|
189
200
|
const docChanged = !nextState.doc.eq(view.state.doc);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { CanonicalGap } from "../types/canonicalCapabilities.js";
|
|
3
|
+
import { EditorMode } from "../managers/EditorModeManager.js";
|
|
3
4
|
import { HiddenEditorApi } from "./hiddenEditorApi.js";
|
|
4
5
|
import { HiddenEditorStateReason } from "../layout-engine/layoutInstrumentation.js";
|
|
5
6
|
import { ExtensionManager } from "../prosemirror/extensions/ExtensionManager.js";
|
|
6
7
|
import { RemoteSelection } from "../types/remote-selection.js";
|
|
7
|
-
import { EditorMode } from "../managers/EditorModeManager.js";
|
|
8
8
|
import { EditorState, Plugin, Transaction } from "prosemirror-state";
|
|
9
9
|
import { EditorView } from "prosemirror-view";
|
|
10
10
|
import * as Yjs from "yjs";
|
|
@@ -812,7 +812,9 @@ const createHiddenEditorManager = (deps) => {
|
|
|
812
812
|
};
|
|
813
813
|
const syncEditable = () => {
|
|
814
814
|
if (!view) return;
|
|
815
|
-
|
|
815
|
+
const readOnly = deps.getReadOnly();
|
|
816
|
+
if (view.editable !== !readOnly) view.setProps({ editable: () => !deps.getReadOnly() });
|
|
817
|
+
syncHiddenEditorAccessibility(view, readOnly);
|
|
816
818
|
};
|
|
817
819
|
const publishStoryCommit = (storyView, commit) => {
|
|
818
820
|
if (!view || editorSession.type !== "canonical") return false;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { TemplatePreviewEntry, TemplatePreviewHiddenRange } from "../prosemirror/plugins/templatePreviewValues.js";
|
|
3
|
+
import { DisplayMode } from "../managers/EditorModeManager.js";
|
|
3
4
|
import { ColumnLayout, FlowBlock, FootnoteContent, HeaderFooterContent, Layout, Measure, PageHeaderFooterRefs, PageMargins } from "../layout-engine/types.js";
|
|
4
5
|
import { LayoutRunOptions } from "./layoutRunOptions.js";
|
|
5
|
-
import { DisplayMode } from "../managers/EditorModeManager.js";
|
|
6
6
|
import { LayoutSession } from "./layoutSession.js";
|
|
7
7
|
import { HyphenationReadiness } from "./hyphenationReadiness.js";
|
|
8
8
|
import { ConvertHeaderFooterOptions, HeaderFooterMetrics } from "../layout-bridge/convert/headerFooterLayout.js";
|
|
@@ -79,6 +79,8 @@ type LayoutPipelineDeps<THfPMs> = {
|
|
|
79
79
|
* authored text with runs hidden afterwards.
|
|
80
80
|
*/
|
|
81
81
|
markupView: DisplayMode;
|
|
82
|
+
/** Independent highlight visibility in All Markup; resolved views always hide comments. */
|
|
83
|
+
showCommentHighlights?: boolean;
|
|
82
84
|
};
|
|
83
85
|
declare function runLayoutPipeline<THfPMs>(deps: LayoutPipelineDeps<THfPMs>, state: EditorState, options?: LayoutRunOptions): LayoutOutcome;
|
|
84
86
|
//#endregion
|
|
@@ -42,7 +42,7 @@ const formatEndnoteTexts = (numbers, formatNumber) => {
|
|
|
42
42
|
for (const [id, displayNumber] of numbers) texts.set(id, formatNumber(displayNumber));
|
|
43
43
|
return texts;
|
|
44
44
|
};
|
|
45
|
-
const sameMeasureInputs = (left, right) => left.embeddedFontFamilyMap === right.embeddedFontFamilyMap && left.styles === right.styles && left.theme === right.theme && left.defaultTabStop === right.defaultTabStop && left.pageContentHeight === right.pageContentHeight && left.fontSet === right.fontSet && left.fontAlternates.size === right.fontAlternates.size && [...left.fontAlternates].every(([name, alternate]) => right.fontAlternates.get(name) === alternate) && left.markupView === right.markupView;
|
|
45
|
+
const sameMeasureInputs = (left, right) => left.embeddedFontFamilyMap === right.embeddedFontFamilyMap && left.styles === right.styles && left.theme === right.theme && left.defaultTabStop === right.defaultTabStop && left.pageContentHeight === right.pageContentHeight && left.fontSet === right.fontSet && left.fontAlternates.size === right.fontAlternates.size && [...left.fontAlternates].every(([name, alternate]) => right.fontAlternates.get(name) === alternate) && left.markupView === right.markupView && left.showCommentHighlights === right.showCommentHighlights;
|
|
46
46
|
/**
|
|
47
47
|
* The range an incremental pass re-measures, or `null` for a full measure: the
|
|
48
48
|
* committed measures must come from the same measure inputs, and the range is
|
|
@@ -136,7 +136,7 @@ function runLayoutPipeline(deps, state, options = {}) {
|
|
|
136
136
|
return result;
|
|
137
137
|
}
|
|
138
138
|
function runLayoutPipelineMeasured(deps, state, options) {
|
|
139
|
-
const { contentWidth, columns, pageSize, margins, pageGap, showMarginGuides, marginGuideColor, syncCoordinator, headerContent, footerContent, firstPageHeaderContent, firstPageFooterContent, headerContentRId, footerContentRId, firstPageHeaderContentRId, firstPageFooterContentRId, sectionHeaderFooterRefs, theme: _theme, sectionProperties, document, defaultTabStop, mirrorMargins, styles, layout, hfPMs, painter, pagesContainer, session, renderHfFromContentOrPm, renderHeaderFooterContentByRId, readFontSetSignature, buildFootnoteRenderItems, describeInvalidHighlightMarks, pageRenderer = PAGE_RENDERER.legacy, emptyTemplatePreviewEntries: EMPTY_TEMPLATE_PREVIEW_ENTRIES, emptyTemplatePreviewHidden: EMPTY_TEMPLATE_PREVIEW_HIDDEN, markupView } = deps;
|
|
139
|
+
const { contentWidth, columns, pageSize, margins, pageGap, showMarginGuides, marginGuideColor, syncCoordinator, headerContent, footerContent, firstPageHeaderContent, firstPageFooterContent, headerContentRId, footerContentRId, firstPageHeaderContentRId, firstPageFooterContentRId, sectionHeaderFooterRefs, theme: _theme, sectionProperties, document, defaultTabStop, mirrorMargins, styles, layout, hfPMs, painter, pagesContainer, session, renderHfFromContentOrPm, renderHeaderFooterContentByRId, readFontSetSignature, buildFootnoteRenderItems, describeInvalidHighlightMarks, pageRenderer = PAGE_RENDERER.legacy, emptyTemplatePreviewEntries: EMPTY_TEMPLATE_PREVIEW_ENTRIES, emptyTemplatePreviewHidden: EMPTY_TEMPLATE_PREVIEW_HIDDEN, markupView, showCommentHighlights = true } = deps;
|
|
140
140
|
let outcome = {};
|
|
141
141
|
let pendingArtifacts;
|
|
142
142
|
let pendingTemplatePreview;
|
|
@@ -154,6 +154,7 @@ function runLayoutPipelineMeasured(deps, state, options) {
|
|
|
154
154
|
let phaseStartedAt = performance.now();
|
|
155
155
|
const pageContentHeight = pageSize.h - margins.top - margins.bottom;
|
|
156
156
|
const fontAlternates = buildFontAlternates(document?.package.fontTable);
|
|
157
|
+
const displayCommentHighlights = showCommentHighlights && markupView !== "no-markup" && markupView !== "original";
|
|
157
158
|
const embeddedFontFamilies = session.embeddedFontFamilyMap ? { embeddedFontFamilyMap: session.embeddedFontFamilyMap } : {};
|
|
158
159
|
const measureInputs = {
|
|
159
160
|
...embeddedFontFamilies,
|
|
@@ -163,9 +164,11 @@ function runLayoutPipelineMeasured(deps, state, options) {
|
|
|
163
164
|
pageContentHeight,
|
|
164
165
|
fontSet,
|
|
165
166
|
fontAlternates,
|
|
166
|
-
markupView
|
|
167
|
+
markupView,
|
|
168
|
+
showCommentHighlights: displayCommentHighlights
|
|
167
169
|
};
|
|
168
170
|
const flowOpts = {
|
|
171
|
+
showCommentHighlights: displayCommentHighlights,
|
|
169
172
|
...embeddedFontFamilies,
|
|
170
173
|
pageContentHeight,
|
|
171
174
|
fontAlternates,
|
|
@@ -246,6 +249,8 @@ function runLayoutPipelineMeasured(deps, state, options) {
|
|
|
246
249
|
const buildHfOptions = (pageCount, fieldInputs) => {
|
|
247
250
|
const hfMeasureBlocks = (hfBlocks, hfWidth) => measureBlocks(hfBlocks, hfWidth, void 0, void 0, buildHeaderFooterFieldValues(hfBlocks, pageCount, hfClock, fieldInputs), { allowEndTabOverflow: true });
|
|
248
251
|
return {
|
|
252
|
+
markupView,
|
|
253
|
+
showCommentHighlights: displayCommentHighlights,
|
|
249
254
|
...flowOpts.styles ? { styles: flowOpts.styles } : {},
|
|
250
255
|
...document?.package.numbering === void 0 ? {} : { numbering: document.package.numbering },
|
|
251
256
|
...flowOpts.defaultSize === void 0 ? {} : { defaultSize: flowOpts.defaultSize },
|
|
@@ -378,7 +383,11 @@ function runLayoutPipelineMeasured(deps, state, options) {
|
|
|
378
383
|
outcome.measures = newMeasures;
|
|
379
384
|
recordPhaseDuration("measure-blocks", phaseStartedAt);
|
|
380
385
|
const buildNoteOptions = () => {
|
|
381
|
-
const noteOptions = {
|
|
386
|
+
const noteOptions = {
|
|
387
|
+
measureBlocks,
|
|
388
|
+
showCommentHighlights: displayCommentHighlights,
|
|
389
|
+
markupView
|
|
390
|
+
};
|
|
382
391
|
if (flowOpts.styles) noteOptions.styles = flowOpts.styles;
|
|
383
392
|
if (flowOpts.defaultSize !== void 0) noteOptions.defaultSize = flowOpts.defaultSize;
|
|
384
393
|
if (_theme !== void 0) noteOptions.theme = _theme;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { TemplatePreviewEntry, TemplatePreviewHiddenRange, TemplatePreviewValues } from "../prosemirror/plugins/templatePreviewValues.js";
|
|
3
|
-
import { FlowBlock, Measure } from "../layout-engine/types.js";
|
|
4
3
|
import { DisplayMode } from "../managers/EditorModeManager.js";
|
|
4
|
+
import { FlowBlock, Measure } from "../layout-engine/types.js";
|
|
5
5
|
import { FontAlternates } from "../fonts/fontAlternates.js";
|
|
6
6
|
import { EditorState } from "prosemirror-state";
|
|
7
7
|
//#region src/controller/layoutSession.d.ts
|
|
@@ -36,6 +36,7 @@ type LayoutMeasureInputs = {
|
|
|
36
36
|
fontAlternates: FontAlternates;
|
|
37
37
|
/** The review view: each lays out different text, so a measure is reusable only within one. */
|
|
38
38
|
markupView: DisplayMode;
|
|
39
|
+
showCommentHighlights?: boolean;
|
|
39
40
|
};
|
|
40
41
|
type LayoutSession = {
|
|
41
42
|
embeddedFontFamilyMap?: ReadonlyMap<string, string>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { CanonicalGap } from "../types/canonicalCapabilities.js";
|
|
3
|
-
import { HiddenEditorApi } from "./hiddenEditorApi.js";
|
|
4
3
|
import { NoteStoryKey } from "../types/editor-story.js";
|
|
4
|
+
import { HiddenEditorApi } from "./hiddenEditorApi.js";
|
|
5
5
|
import { Plugin } from "prosemirror-state";
|
|
6
6
|
import { DirectEditorProps, EditorView } from "prosemirror-view";
|
|
7
7
|
//#region src/controller/noteEditorManager.d.ts
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseRotationDegrees } from "../../utils/rotationBoundingBox.js";
|
|
2
2
|
import { sanitizeImageSrc } from "../../utils/sanitizeImageSrc.js";
|
|
3
|
+
import { base64ToBytes, encodeUtf8 } from "../../utils/textEncoding.js";
|
|
3
4
|
import { paintPreview } from "./previewPrimitives.js";
|
|
4
5
|
import { resolveBorderStroke } from "./strokes.js";
|
|
5
6
|
import { UNSUPPORTED_CONSTRUCT } from "./unsupported.js";
|
|
@@ -10,23 +11,19 @@ import { Result } from "better-result";
|
|
|
10
11
|
*
|
|
11
12
|
* The display list carries raw bytes plus the format they are already in, so a
|
|
12
13
|
* backend never owns a codec. This module therefore decodes only far enough to
|
|
13
|
-
* fill `pixelWidth`/`pixelHeight` and
|
|
14
|
-
* nothing:
|
|
15
|
-
* `unsupported` and
|
|
16
|
-
* than a named one.
|
|
14
|
+
* fill `pixelWidth`/`pixelHeight` and identify PNG, JPEG, or a generated SVG
|
|
15
|
+
* preview. It transcodes nothing: WMF, EMF, TIFF, GIF, and BMP are reported
|
|
16
|
+
* through `unsupported` and paint nothing, because a silently missing picture
|
|
17
|
+
* is worse than a named one.
|
|
17
18
|
*/
|
|
18
|
-
const DATA_URL_PATTERN = /^data:([^;,]*)(;base64)?,(.*)$/su;
|
|
19
|
+
const DATA_URL_PATTERN = /^data:([^;,]*)(?:;charset=[^;,]*)?(;base64)?,(.*)$/su;
|
|
19
20
|
/**
|
|
20
21
|
* `undefined` for a payload `atob` rejects: a malformed `data:` URL is a
|
|
21
22
|
* document the builder reports, not an exception that aborts the build.
|
|
22
23
|
*/
|
|
23
24
|
const decodeBase64 = (encoded) => {
|
|
24
|
-
const decoded = Result.try(() =>
|
|
25
|
-
|
|
26
|
-
const binary = decoded.value;
|
|
27
|
-
const bytes = new Uint8Array(binary.length);
|
|
28
|
-
for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
|
|
29
|
-
return bytes;
|
|
25
|
+
const decoded = Result.try(() => base64ToBytes(encoded));
|
|
26
|
+
return decoded.isOk() ? decoded.value : void 0;
|
|
30
27
|
};
|
|
31
28
|
const readUint32BE = (bytes, offset) => bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3];
|
|
32
29
|
const readUint16BE = (bytes, offset) => bytes[offset] << 8 | bytes[offset + 1];
|
|
@@ -164,7 +161,26 @@ var ImageTable = class {
|
|
|
164
161
|
if (safeSrc === void 0) return fail("image source is not a data: or blob: URL");
|
|
165
162
|
const match = DATA_URL_PATTERN.exec(safeSrc);
|
|
166
163
|
if (!match) return fail("image bytes are not inline (blob: URLs need a fetch the builder cannot do)");
|
|
167
|
-
if (match[
|
|
164
|
+
if (match[1] === "image/svg+xml" && match[2] === void 0) {
|
|
165
|
+
const decoded = Result.try(() => decodeURIComponent(match[3]));
|
|
166
|
+
if (decoded.isErr()) return fail("SVG data: URL payload is not valid percent-encoding");
|
|
167
|
+
if (!decoded.value.includes("data-folio-preview=\"wpGroup\"")) return fail("SVG is not a supported generated group preview");
|
|
168
|
+
const width = /<svg\b[^>]*\bwidth="([0-9.]+)"/u.exec(decoded.value)?.at(1);
|
|
169
|
+
const height = /<svg\b[^>]*\bheight="([0-9.]+)"/u.exec(decoded.value)?.at(1);
|
|
170
|
+
const pixelWidth = width === void 0 ? 0 : Number(width);
|
|
171
|
+
const pixelHeight = height === void 0 ? 0 : Number(height);
|
|
172
|
+
if (!(pixelWidth > 0 && pixelHeight > 0)) return fail("SVG carries no positive numeric width and height");
|
|
173
|
+
const ref = this.sources.length;
|
|
174
|
+
this.sources.push({
|
|
175
|
+
format: "svg",
|
|
176
|
+
bytes: encodeUtf8(decoded.value),
|
|
177
|
+
pixelWidth,
|
|
178
|
+
pixelHeight
|
|
179
|
+
});
|
|
180
|
+
this.refBySrc.set(src, ref);
|
|
181
|
+
return ref;
|
|
182
|
+
}
|
|
183
|
+
if (match[2] === void 0) return fail("only base64 data: URLs and SVG previews carry decodable bytes");
|
|
168
184
|
const bytes = decodeBase64(match[3]);
|
|
169
185
|
if (bytes === void 0) return fail("image data: URL payload is not valid base64");
|
|
170
186
|
if (bytes.length === 0) return fail("image data: URL decoded to no bytes");
|
|
@@ -353,7 +353,7 @@ const smallCapsGlyphRunSegments = (glyphs, fontSizePx) => {
|
|
|
353
353
|
* first, then the glyphs, then the decorations whose geometry CSS would have
|
|
354
354
|
* derived from the font.
|
|
355
355
|
*/
|
|
356
|
-
const emitGlyphRun = ({ sink, context, run, glyphs, style, paintXPx, baselineYPx, lineTopYPx, lineHeightPx, isRtl, pmRange, collapsedEdge }) => {
|
|
356
|
+
const emitGlyphRun = ({ sink, context, run, glyphs, style, paintXPx, baselineYPx, lineTopYPx, lineHeightPx, isRtl, pmRange, decorationRange, collapsedEdge }) => {
|
|
357
357
|
if (glyphs.text.length === 0) return;
|
|
358
358
|
const fontSizePx = ptToPx(style.fontSize ?? 11);
|
|
359
359
|
const color = resolveRunColor(run, context);
|
|
@@ -423,6 +423,7 @@ const emitGlyphRun = ({ sink, context, run, glyphs, style, paintXPx, baselineYPx
|
|
|
423
423
|
...unicodeBidi === void 0 ? {} : { unicodeBidi },
|
|
424
424
|
...stroke === void 0 ? {} : { stroke },
|
|
425
425
|
...pmRange === void 0 ? {} : { pmRange },
|
|
426
|
+
...decorationRange === void 0 ? {} : { decorationRange },
|
|
426
427
|
...collapsedEdge === void 0 ? {} : { collapsedEdge }
|
|
427
428
|
};
|
|
428
429
|
const smallCapsRuns = style.fontVariant === "small-caps" && direction === "ltr" ? smallCapsGlyphRunSegments(glyphs, fontSizePx) : void 0;
|
|
@@ -436,6 +437,7 @@ const emitGlyphRun = ({ sink, context, run, glyphs, style, paintXPx, baselineYPx
|
|
|
436
437
|
else {
|
|
437
438
|
const adjustments = glyphs.adjustments === void 0 ? {} : { adjustments: glyphs.adjustments };
|
|
438
439
|
let segmentXPx = paintXPx;
|
|
440
|
+
let segmentTextOffset = 0;
|
|
439
441
|
for (const segment of smallCapsRuns) {
|
|
440
442
|
sink.glyphs.push({
|
|
441
443
|
kind: "glyphRun",
|
|
@@ -446,9 +448,14 @@ const emitGlyphRun = ({ sink, context, run, glyphs, style, paintXPx, baselineYPx
|
|
|
446
448
|
kerning: glyphs.kerning,
|
|
447
449
|
smallCaps: true,
|
|
448
450
|
...adjustments,
|
|
449
|
-
...commonFields
|
|
451
|
+
...commonFields,
|
|
452
|
+
...decorationRange === void 0 ? {} : { decorationRange: {
|
|
453
|
+
...decorationRange,
|
|
454
|
+
textOffset: decorationRange.textOffset + segmentTextOffset
|
|
455
|
+
} }
|
|
450
456
|
});
|
|
451
457
|
segmentXPx += segment.widthPx;
|
|
458
|
+
segmentTextOffset += segment.text.length;
|
|
452
459
|
}
|
|
453
460
|
}
|
|
454
461
|
emitDecorations({
|
|
@@ -681,6 +688,12 @@ const paintLine = ({ block, line, geometry, context, alignment, isRtl, isFirstLi
|
|
|
681
688
|
kind: "text",
|
|
682
689
|
text: glyphs.text
|
|
683
690
|
},
|
|
691
|
+
...run.pmStart === void 0 || run.pmEnd === void 0 || resolveFieldText(run, context) !== run.fallback ? {} : { decorationRange: {
|
|
692
|
+
start: run.pmStart,
|
|
693
|
+
end: run.pmEnd,
|
|
694
|
+
story: context.story,
|
|
695
|
+
textOffset: 0
|
|
696
|
+
} },
|
|
684
697
|
glyphs,
|
|
685
698
|
style,
|
|
686
699
|
paintXPx: paintXOf(),
|
|
@@ -48,7 +48,8 @@ const PAGE_ELEMENT_ID_PREFIX = "page-";
|
|
|
48
48
|
const DEGREES_PER_RADIAN = 180 / Math.PI;
|
|
49
49
|
const IMAGE_MIME_TYPES = {
|
|
50
50
|
png: "image/png",
|
|
51
|
-
jpeg: "image/jpeg"
|
|
51
|
+
jpeg: "image/jpeg",
|
|
52
|
+
svg: "image/svg+xml"
|
|
52
53
|
};
|
|
53
54
|
/**
|
|
54
55
|
* The display list does not say whether an embedded face is sfnt or WOFF, so a
|
|
@@ -252,11 +253,11 @@ const applyAdjustments = (span, adjustments) => {
|
|
|
252
253
|
* and in each note, so a surface has to know which one it landed in before it
|
|
253
254
|
* can use the number at all.
|
|
254
255
|
*/
|
|
255
|
-
const applyModelRange = (span, range) => {
|
|
256
|
+
const applyModelRange = (span, range, purpose = "editing") => {
|
|
256
257
|
if (range === void 0) return;
|
|
257
|
-
span.className = "layout-run-text";
|
|
258
|
-
span.dataset["pmStart"] = String(range.start);
|
|
259
|
-
span.dataset["pmEnd"] = String(range.end);
|
|
258
|
+
if (purpose === "editing") span.className = "layout-run-text";
|
|
259
|
+
span.dataset[purpose === "editing" ? "pmStart" : "decorationStart"] = String(range.start);
|
|
260
|
+
span.dataset[purpose === "editing" ? "pmEnd" : "decorationEnd"] = String(range.end);
|
|
260
261
|
const { story } = range;
|
|
261
262
|
span.dataset["story"] = story.kind;
|
|
262
263
|
switch (story.kind) {
|
|
@@ -300,6 +301,8 @@ const paintGlyphRun = (run, context) => {
|
|
|
300
301
|
applyAdjustments(span, run.adjustments);
|
|
301
302
|
span.dataset["advanceSum"] = String(advanceSum);
|
|
302
303
|
applyModelRange(span, run.pmRange);
|
|
304
|
+
applyModelRange(span, run.decorationRange, "decoration");
|
|
305
|
+
if (run.decorationRange) span.dataset["decorationTextOffset"] = String(run.decorationRange.textOffset);
|
|
303
306
|
if (run.collapsedEdge !== void 0) {
|
|
304
307
|
span.dataset[run.collapsedEdge.side === "leading" ? "collapsedLeadingSpaces" : "collapsedTrailingSpaces"] = "true";
|
|
305
308
|
span.dataset["collapsedSpaceAdvance"] = String(run.collapsedEdge.spaceAdvancePx);
|
|
@@ -124,11 +124,11 @@ type DisplayFontRef = number;
|
|
|
124
124
|
type DisplayImageRef = number;
|
|
125
125
|
/**
|
|
126
126
|
* Raw image bytes plus the format they are already in. `png` and `jpeg`
|
|
127
|
-
* pass through to PDF unchanged
|
|
128
|
-
*
|
|
127
|
+
* pass through to PDF unchanged. SVG is retained for vector previews in the
|
|
128
|
+
* DOM backend; PDF reports it as unsupported rather than silently rasterizing.
|
|
129
129
|
*/
|
|
130
130
|
type DisplayImageSource = {
|
|
131
|
-
readonly format: "png" | "jpeg";
|
|
131
|
+
readonly format: "png" | "jpeg" | "svg";
|
|
132
132
|
readonly bytes: Uint8Array;
|
|
133
133
|
readonly pixelWidth: number;
|
|
134
134
|
readonly pixelHeight: number;
|
|
@@ -278,6 +278,10 @@ type DisplayGlyphRun = {
|
|
|
278
278
|
* substituted field value, a tab leader.
|
|
279
279
|
*/
|
|
280
280
|
readonly pmRange?: DisplayModelRange;
|
|
281
|
+
/** Source atom for view-only decorations; never used for editing positions. */
|
|
282
|
+
readonly decorationRange?: DisplayModelRange & {
|
|
283
|
+
readonly textOffset: number;
|
|
284
|
+
};
|
|
281
285
|
};
|
|
282
286
|
/** An axis-aligned rectangle, filled and/or stroked. At least one is set. */
|
|
283
287
|
type DisplayRectPrimitive = {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { withTrackedSectionEndpointRemoval } from "../internal/sectionEndpointResolution.js";
|
|
2
2
|
import { withSectionReferenceResolution } from "../internal/sectionReferenceResolution.js";
|
|
3
|
+
import { decodeText, encodeUtf8 } from "../utils/textEncoding.js";
|
|
3
4
|
import { readDocumentSectionFacts } from "./documentSectionFacts.js";
|
|
4
5
|
import { parseDocx } from "./parser.js";
|
|
5
6
|
import { assertSectionCarriersMatchModel } from "./rezip.js";
|
|
@@ -9,7 +10,7 @@ import JSZip from "jszip";
|
|
|
9
10
|
/** Canonical snapshots own explicit section references; the PM projection does not. */
|
|
10
11
|
const referenceKey = ({ element, type, rId }) => `${element}:${type}:${rId}`;
|
|
11
12
|
const xmlFingerprint = async (xml) => {
|
|
12
|
-
const digest = await crypto.subtle.digest("SHA-256",
|
|
13
|
+
const digest = await crypto.subtle.digest("SHA-256", encodeUtf8(xml));
|
|
13
14
|
let fingerprint = "";
|
|
14
15
|
for (const byte of new Uint8Array(digest)) fingerprint += byte.toString(16).padStart(2, "0");
|
|
15
16
|
return fingerprint;
|
|
@@ -18,7 +19,7 @@ const xmlFingerprint = async (xml) => {
|
|
|
18
19
|
const repackWithCanonicalStoryRemovals = async ({ document, repack }) => {
|
|
19
20
|
const originalBuffer = document.originalBuffer;
|
|
20
21
|
if (!originalBuffer) return repack();
|
|
21
|
-
const xml = await (await JSZip.loadAsync(originalBuffer)).file("word/document.xml")?.async("
|
|
22
|
+
const xml = await (await JSZip.loadAsync(originalBuffer)).file("word/document.xml")?.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
22
23
|
if (!xml) return repack();
|
|
23
24
|
const before = readDocumentSectionFacts(xml);
|
|
24
25
|
const serializedXml = serializeDocument(document);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
//#region src/docx/commentTextParagraph.d.ts
|
|
3
|
+
/** Plain comment text has no authored paragraph formatting. */
|
|
4
|
+
declare const commentTextParagraph: (text: string) => document_d_exports.Paragraph;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { commentTextParagraph };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/docx/commentTextParagraph.ts
|
|
2
|
+
/** Plain comment text has no authored paragraph formatting. */
|
|
3
|
+
const commentTextParagraph = (text) => ({
|
|
4
|
+
type: "paragraph",
|
|
5
|
+
content: [{
|
|
6
|
+
type: "run",
|
|
7
|
+
formatting: {},
|
|
8
|
+
content: [{
|
|
9
|
+
type: "text",
|
|
10
|
+
text
|
|
11
|
+
}]
|
|
12
|
+
}]
|
|
13
|
+
});
|
|
14
|
+
//#endregion
|
|
15
|
+
export { commentTextParagraph };
|
package/dist/docx/conformance.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sliceText } from "../utils/sliceText.js";
|
|
1
2
|
import { getLocalName, getNamespacePrefix, parseXmlDocument } from "./xmlParser.js";
|
|
2
3
|
import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
|
|
3
4
|
//#region src/docx/conformance.ts
|
|
@@ -62,7 +63,11 @@ function scanRootStartTag(scanTarget) {
|
|
|
62
63
|
}
|
|
63
64
|
const detectDocxConformanceClass = (documentXml) => {
|
|
64
65
|
if (documentXml === null) return DOCX_CONFORMANCE_CLASSES.UNKNOWN;
|
|
65
|
-
const rootStartTag = scanRootStartTag(documentXml.length <= ROOT_START_TAG_SCAN_LIMIT ? documentXml :
|
|
66
|
+
const rootStartTag = scanRootStartTag(documentXml.length <= ROOT_START_TAG_SCAN_LIMIT ? documentXml : sliceText({
|
|
67
|
+
text: documentXml,
|
|
68
|
+
units: "codeUnits",
|
|
69
|
+
end: ROOT_START_TAG_SCAN_LIMIT
|
|
70
|
+
}));
|
|
66
71
|
const root = (rootStartTag === void 0 ? null : parseXmlDocument(rootStartTag.endsWith("/>") ? rootStartTag : `${rootStartTag.slice(0, -1)}/>`)) ?? parseXmlDocument(documentXml);
|
|
67
72
|
return detectDocxConformanceClassFromRoot(root);
|
|
68
73
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeText } from "../utils/textEncoding.js";
|
|
1
2
|
import { resolveRelationshipIdOfType, resolveRelativePath } from "./relsParser.js";
|
|
2
3
|
import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, findChildByNamespaceUri, getAttribute, getAttributeByNamespaceUri, getLocalName, parseNumericAttribute, parseXmlDocument } from "./xmlParser.js";
|
|
3
4
|
//#region src/docx/diagramPreview.ts
|
|
@@ -31,7 +32,7 @@ const partByRelationshipId = ({ rels, media, rId, type, cache }) => {
|
|
|
31
32
|
const cached = cache.get(path);
|
|
32
33
|
if (cached !== void 0) return cached;
|
|
33
34
|
const file = media.get(path);
|
|
34
|
-
const parsed = file?.data ? parseXmlDocument(new
|
|
35
|
+
const parsed = file?.data ? parseXmlDocument(decodeText(new Uint8Array(file.data))) : null;
|
|
35
36
|
cache.set(path, parsed);
|
|
36
37
|
return parsed;
|
|
37
38
|
};
|
|
@@ -19,6 +19,18 @@ declare function extractTemplateVariables(text: string): string[];
|
|
|
19
19
|
* @returns Array of unique variable names
|
|
20
20
|
*/
|
|
21
21
|
declare function extractAllTemplateVariables(content: document_d_exports.BlockContent[]): string[];
|
|
22
|
+
/**
|
|
23
|
+
* Build sections from content based on section properties in paragraphs
|
|
24
|
+
*
|
|
25
|
+
* In OOXML, sections are delimited by:
|
|
26
|
+
* 1. w:pPr/w:sectPr within a paragraph (marks end of a section)
|
|
27
|
+
* 2. w:body/w:sectPr (final section properties)
|
|
28
|
+
*
|
|
29
|
+
* @param content - All block content
|
|
30
|
+
* @param finalSectPr - Final section properties from body
|
|
31
|
+
* @returns Array of sections
|
|
32
|
+
*/
|
|
33
|
+
declare function buildSections(content: document_d_exports.BlockContent[], finalSectPr: document_d_exports.SectionProperties | undefined): document_d_exports.Section[];
|
|
22
34
|
/**
|
|
23
35
|
* Parse document.xml body content
|
|
24
36
|
*
|
|
@@ -67,4 +79,4 @@ declare function getSectionCount(body: document_d_exports.DocumentBody): number;
|
|
|
67
79
|
*/
|
|
68
80
|
declare function hasTemplateVariables(body: document_d_exports.DocumentBody): boolean;
|
|
69
81
|
//#endregion
|
|
70
|
-
export { extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getParagraphCount, getSectionCount, hasTemplateVariables, parseDocumentBody, parseDocumentBodyTree };
|
|
82
|
+
export { buildSections, extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getParagraphCount, getSectionCount, hasTemplateVariables, parseDocumentBody, parseDocumentBodyTree };
|
|
@@ -257,4 +257,4 @@ function hasTemplateVariables(body) {
|
|
|
257
257
|
return extractAllTemplateVariables(body.content).length > 0;
|
|
258
258
|
}
|
|
259
259
|
//#endregion
|
|
260
|
-
export { extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getParagraphCount, getSectionCount, hasTemplateVariables, parseDocumentBody, parseDocumentBodyTree };
|
|
260
|
+
export { buildSections, extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getParagraphCount, getSectionCount, hasTemplateVariables, parseDocumentBody, parseDocumentBodyTree };
|