@stll/folio-core 0.40.0 → 0.42.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 +16 -0
- package/dist/ai-edits/clean-text.d.ts +28 -6
- package/dist/ai-edits/clean-text.js +28 -13
- package/dist/ai-edits/headless.d.ts +12 -0
- package/dist/ai-edits/headless.js +26 -3
- package/dist/ai-edits/snapshot.d.ts +6 -1
- package/dist/ai-edits/snapshot.js +15 -10
- package/dist/ai-suggestions/text-positions.js +15 -3
- package/dist/compare/inline-atoms.js +2 -2
- package/dist/compat/eigenpal.d.ts +4 -3
- package/dist/compat/eigenpal.js +2 -1
- package/dist/display-list/primitives.d.ts +2 -1
- package/dist/document-operations.d.ts +3 -2
- package/dist/docx/blockPlainText.d.ts +8 -0
- package/dist/docx/blockPlainText.js +40 -0
- package/dist/docx/compatibility.d.ts +16 -2
- package/dist/docx/compatibility.js +30 -9
- package/dist/docx/footnoteParser.js +4 -23
- package/dist/docx/graphicFrameLocks.d.ts +22 -0
- package/dist/docx/graphicFrameLocks.js +55 -0
- package/dist/docx/groupDrawingParser.d.ts +7 -1
- package/dist/docx/groupDrawingParser.js +11 -2
- package/dist/docx/headerFooterParser.d.ts +5 -1
- package/dist/docx/headerFooterParser.js +7 -21
- package/dist/docx/imageParser.js +5 -0
- package/dist/docx/imageRawXml.d.ts +33 -1
- package/dist/docx/imageRawXml.js +56 -1
- package/dist/docx/rezip.d.ts +20 -3
- package/dist/docx/rezip.js +24 -13
- package/dist/docx/runParser.js +43 -21
- package/dist/docx/serializer/runSerializer.js +4 -2
- package/dist/docx/server/createBilingualDocument.js +31 -5
- package/dist/docx/shapeParser.js +32 -6
- package/dist/docx/vmlImageParser.d.ts +11 -1
- package/dist/docx/vmlImageParser.js +29 -2
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -1
- package/dist/internal/compare/inline-presentation.d.ts +11 -3
- package/dist/internal/compare/inline-presentation.js +8 -1
- package/dist/prosemirror/attrs/index.js +33 -3
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +13 -2
- package/dist/prosemirror/conversion/fromProseDoc.js +80 -27
- package/dist/prosemirror/conversion/index.d.ts +2 -2
- package/dist/prosemirror/conversion/toProseDoc.js +10 -2
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +6 -0
- package/dist/prosemirror/imageCommit.js +7 -3
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +16 -1
- package/dist/prosemirror/runFormattingInlineCarriers.js +20 -1
- package/dist/prosemirror/schema/nodes.d.ts +20 -0
- package/dist/server.d.ts +2 -2
- package/package.json +2 -2
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { blockPlainText } from "./blockPlainText.js";
|
|
1
2
|
import { parseEndnoteProperties, parseFootnoteProperties } from "./notePropertiesParser.js";
|
|
2
|
-
import {
|
|
3
|
+
import { parseParagraph } from "./paragraphParser.js";
|
|
3
4
|
import { parseSdtProperties } from "./sdtProperties.js";
|
|
4
5
|
import { parseTable } from "./tableParser.js";
|
|
5
6
|
import { findChild, findChildren, getAttributes, getChildElements, getLocalName, parseXml } from "./xmlParser.js";
|
|
6
|
-
import { panic } from "better-result";
|
|
7
7
|
//#region src/docx/footnoteParser.ts
|
|
8
8
|
/**
|
|
9
9
|
* Parse note type attribute
|
|
@@ -173,32 +173,13 @@ function createEndnoteMap(byId, endnotes) {
|
|
|
173
173
|
* Uses the accepted tracked-change view and recurses through every note block.
|
|
174
174
|
*/
|
|
175
175
|
function getFootnoteText(footnote) {
|
|
176
|
-
return
|
|
176
|
+
return blockPlainText(footnote.content);
|
|
177
177
|
}
|
|
178
178
|
/**
|
|
179
179
|
* Get plain text content of an endnote.
|
|
180
180
|
*/
|
|
181
181
|
function getEndnoteText(endnote) {
|
|
182
|
-
return
|
|
183
|
-
}
|
|
184
|
-
function collectNoteBlockTexts(blocks) {
|
|
185
|
-
const texts = [];
|
|
186
|
-
for (const block of blocks) switch (block.type) {
|
|
187
|
-
case "paragraph":
|
|
188
|
-
texts.push(getParagraphText(block));
|
|
189
|
-
break;
|
|
190
|
-
case "table":
|
|
191
|
-
for (const row of block.rows) {
|
|
192
|
-
if (row.formatting?.hidden === true) continue;
|
|
193
|
-
texts.push(row.cells.map((cell) => collectNoteBlockTexts(cell.content).join("\n")).join(" "));
|
|
194
|
-
}
|
|
195
|
-
break;
|
|
196
|
-
case "blockSdt":
|
|
197
|
-
texts.push(...collectNoteBlockTexts(block.content));
|
|
198
|
-
break;
|
|
199
|
-
default: panic(`Unsupported note block in plain-text extraction: ${JSON.stringify(block)}`);
|
|
200
|
-
}
|
|
201
|
-
return texts;
|
|
182
|
+
return blockPlainText(endnote.content);
|
|
202
183
|
}
|
|
203
184
|
/**
|
|
204
185
|
* Check if a footnote is a separator (not regular content)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { XmlElement } from "./xmlParser.js";
|
|
3
|
+
//#region src/docx/graphicFrameLocks.d.ts
|
|
4
|
+
/** Every modeled lock, in schema order: the total map above keeps it exhaustive. */
|
|
5
|
+
declare const GRAPHIC_FRAME_LOCK_KEYS: ("noGrp" | "noDrilldown" | "noSelect" | "noChangeAspect" | "noMove" | "noResize")[];
|
|
6
|
+
/**
|
|
7
|
+
* Read the locks off a `wp:inline` or `wp:anchor` element.
|
|
8
|
+
*
|
|
9
|
+
* Returns undefined when the element is absent or carries no recognized
|
|
10
|
+
* attribute: an authored `<a:graphicFrameLocks/>` with nothing on it means the
|
|
11
|
+
* same as no element at all, so both stay "spec defaults" on the model.
|
|
12
|
+
*/
|
|
13
|
+
declare const parseGraphicFrameLocks: (parent: XmlElement) => document_d_exports.ImageFrameLocks | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Emit the whole `wp:cNvGraphicFramePr` element for regenerated DrawingML.
|
|
16
|
+
*
|
|
17
|
+
* Absent locks mean no authored frame was ever parsed (a Folio-created
|
|
18
|
+
* picture), which keeps the historical `noChangeAspect="1"`.
|
|
19
|
+
*/
|
|
20
|
+
declare const serializeGraphicFrameLocks: (locks: document_d_exports.ImageFrameLocks | undefined) => string;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { GRAPHIC_FRAME_LOCK_KEYS, parseGraphicFrameLocks, serializeGraphicFrameLocks };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { findChild, getAttribute, parseOnOffValue } from "./xmlParser.js";
|
|
2
|
+
//#region src/docx/graphicFrameLocks.ts
|
|
3
|
+
/**
|
|
4
|
+
* Model key → OOXML attribute. Insertion order is the schema's attribute
|
|
5
|
+
* order, which is also the order we emit.
|
|
6
|
+
*/
|
|
7
|
+
const GRAPHIC_FRAME_LOCK_ATTRIBUTES = {
|
|
8
|
+
noGrp: "noGrp",
|
|
9
|
+
noDrilldown: "noDrilldown",
|
|
10
|
+
noSelect: "noSelect",
|
|
11
|
+
noChangeAspect: "noChangeAspect",
|
|
12
|
+
noMove: "noMove",
|
|
13
|
+
noResize: "noResize"
|
|
14
|
+
};
|
|
15
|
+
const isGraphicFrameLockKey = (key) => key in GRAPHIC_FRAME_LOCK_ATTRIBUTES;
|
|
16
|
+
/** Every modeled lock, in schema order: the total map above keeps it exhaustive. */
|
|
17
|
+
const GRAPHIC_FRAME_LOCK_KEYS = Object.keys(GRAPHIC_FRAME_LOCK_ATTRIBUTES).filter(isGraphicFrameLockKey);
|
|
18
|
+
const DRAWINGML_NAMESPACE = "xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\"";
|
|
19
|
+
/**
|
|
20
|
+
* Read the locks off a `wp:inline` or `wp:anchor` element.
|
|
21
|
+
*
|
|
22
|
+
* Returns undefined when the element is absent or carries no recognized
|
|
23
|
+
* attribute: an authored `<a:graphicFrameLocks/>` with nothing on it means the
|
|
24
|
+
* same as no element at all, so both stay "spec defaults" on the model.
|
|
25
|
+
*/
|
|
26
|
+
const parseGraphicFrameLocks = (parent) => {
|
|
27
|
+
const locksEl = findChild(findChild(parent, "wp", "cNvGraphicFramePr"), "a", "graphicFrameLocks");
|
|
28
|
+
if (!locksEl) return;
|
|
29
|
+
const locks = {};
|
|
30
|
+
let present = false;
|
|
31
|
+
for (const key of GRAPHIC_FRAME_LOCK_KEYS) {
|
|
32
|
+
const value = parseOnOffValue(getAttribute(locksEl, null, GRAPHIC_FRAME_LOCK_ATTRIBUTES[key]));
|
|
33
|
+
if (value !== void 0) {
|
|
34
|
+
locks[key] = value;
|
|
35
|
+
present = true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return present ? locks : void 0;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Emit the whole `wp:cNvGraphicFramePr` element for regenerated DrawingML.
|
|
42
|
+
*
|
|
43
|
+
* Absent locks mean no authored frame was ever parsed (a Folio-created
|
|
44
|
+
* picture), which keeps the historical `noChangeAspect="1"`.
|
|
45
|
+
*/
|
|
46
|
+
const serializeGraphicFrameLocks = (locks) => {
|
|
47
|
+
const attrs = locks ? GRAPHIC_FRAME_LOCK_KEYS.flatMap((key) => {
|
|
48
|
+
const value = locks[key];
|
|
49
|
+
return value === void 0 ? [] : [`${GRAPHIC_FRAME_LOCK_ATTRIBUTES[key]}="${value ? "1" : "0"}"`];
|
|
50
|
+
}) : ["noChangeAspect=\"1\""];
|
|
51
|
+
const attrList = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
|
|
52
|
+
return `<wp:cNvGraphicFramePr><a:graphicFrameLocks ${DRAWINGML_NAMESPACE}${attrList}/></wp:cNvGraphicFramePr>`;
|
|
53
|
+
};
|
|
54
|
+
//#endregion
|
|
55
|
+
export { GRAPHIC_FRAME_LOCK_KEYS, parseGraphicFrameLocks, serializeGraphicFrameLocks };
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { XmlElement } from "./xmlParser.js";
|
|
3
3
|
//#region src/docx/groupDrawingParser.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Whether a `w:drawing` carries a WordprocessingGroup payload. A group this
|
|
6
|
+
* module declines to rasterize has no editable projection either — the shape
|
|
7
|
+
* model holds one shape, not a group — so the caller must preserve it raw.
|
|
8
|
+
*/
|
|
9
|
+
declare const isGroupDrawing: (drawing: XmlElement) => boolean;
|
|
4
10
|
/** Parse a WordprocessingGroup drawing into a safe SVG-backed image preview. */
|
|
5
11
|
declare const parseGroupDrawing: (drawing: XmlElement, rels?: document_d_exports.RelationshipMap, media?: Map<string, document_d_exports.MediaFile>) => document_d_exports.Image | null;
|
|
6
12
|
//#endregion
|
|
7
|
-
export { parseGroupDrawing };
|
|
13
|
+
export { isGroupDrawing, parseGroupDrawing };
|
|
@@ -165,9 +165,18 @@ const createSvg = (group, width, height, rels, media) => {
|
|
|
165
165
|
const viewBox = groupViewBox(group, width, height);
|
|
166
166
|
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}" width="${emuToPixels(width)}" height="${emuToPixels(height)}">${content}</svg>`;
|
|
167
167
|
};
|
|
168
|
+
const groupElement = (drawing) => {
|
|
169
|
+
return findChildByLocalName(findAllDeep(drawing, "a", "graphicData").at(0) ?? null, "wgp");
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Whether a `w:drawing` carries a WordprocessingGroup payload. A group this
|
|
173
|
+
* module declines to rasterize has no editable projection either — the shape
|
|
174
|
+
* model holds one shape, not a group — so the caller must preserve it raw.
|
|
175
|
+
*/
|
|
176
|
+
const isGroupDrawing = (drawing) => groupElement(drawing) !== null;
|
|
168
177
|
/** Parse a WordprocessingGroup drawing into a safe SVG-backed image preview. */
|
|
169
178
|
const parseGroupDrawing = (drawing, rels, media) => {
|
|
170
|
-
const group =
|
|
179
|
+
const group = groupElement(drawing);
|
|
171
180
|
if (!group) return null;
|
|
172
181
|
const image = parseImage(drawing, void 0, void 0);
|
|
173
182
|
if (!image || image.size.width <= 0 || image.size.height <= 0) return null;
|
|
@@ -179,4 +188,4 @@ const parseGroupDrawing = (drawing, rels, media) => {
|
|
|
179
188
|
return image;
|
|
180
189
|
};
|
|
181
190
|
//#endregion
|
|
182
|
-
export { parseGroupDrawing };
|
|
191
|
+
export { isGroupDrawing, parseGroupDrawing };
|
|
@@ -77,7 +77,11 @@ declare function createEmptyHeaderFooterMap(): HeaderFooterMap;
|
|
|
77
77
|
*/
|
|
78
78
|
declare function buildHeaderFooterMap(references: (document_d_exports.HeaderReference | document_d_exports.FooterReference)[], xmlContents: Map<string, string>, isHeader: boolean, styles?: StyleMap | null, theme?: document_d_exports.Theme | null, numbering?: NumberingMap | null, rels?: document_d_exports.RelationshipMap | null, media?: Map<string, document_d_exports.MediaFile> | null): HeaderFooterMap;
|
|
79
79
|
/**
|
|
80
|
-
* Get plain text content of a header/footer
|
|
80
|
+
* Get plain text content of a header/footer.
|
|
81
|
+
*
|
|
82
|
+
* Shares one walk with the note stories: this used to read only text runs, so a
|
|
83
|
+
* header's fields, hyperlinks, tabs and breaks were silently absent from its
|
|
84
|
+
* text while the same paragraph in a footnote read in full.
|
|
81
85
|
*/
|
|
82
86
|
declare function getHeaderFooterText(hf: document_d_exports.HeaderFooter): string;
|
|
83
87
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseBlockContent } from "./blockContentParser.js";
|
|
2
|
+
import { blockPlainText } from "./blockPlainText.js";
|
|
2
3
|
import { parseFooterReference, parseFooterReferences, parseHeaderReference, parseHeaderReferences } from "./headerFooterRefParser.js";
|
|
3
4
|
import { assignHeaderFooterVerbatimXml } from "./headerFooterVerbatim.js";
|
|
4
5
|
import { cloneParagraphWithPropertySource } from "./paragraphPropertySource.js";
|
|
@@ -139,29 +140,14 @@ function buildHeaderFooterMap(references, xmlContents, isHeader, styles = null,
|
|
|
139
140
|
return createHeaderFooterMap(byId);
|
|
140
141
|
}
|
|
141
142
|
/**
|
|
142
|
-
* Get plain text content of a header/footer
|
|
143
|
+
* Get plain text content of a header/footer.
|
|
144
|
+
*
|
|
145
|
+
* Shares one walk with the note stories: this used to read only text runs, so a
|
|
146
|
+
* header's fields, hyperlinks, tabs and breaks were silently absent from its
|
|
147
|
+
* text while the same paragraph in a footnote read in full.
|
|
143
148
|
*/
|
|
144
149
|
function getHeaderFooterText(hf) {
|
|
145
|
-
|
|
146
|
-
for (const item of hf.content) if (item.type === "paragraph") {
|
|
147
|
-
const paraTexts = [];
|
|
148
|
-
for (const content of item.content) if (content.type === "run") {
|
|
149
|
-
for (const runContent of content.content) if (runContent.type === "text") paraTexts.push(runContent.text);
|
|
150
|
-
}
|
|
151
|
-
texts.push(paraTexts.join(""));
|
|
152
|
-
} else if (item.type === "blockSdt") texts.push(getHeaderFooterText({
|
|
153
|
-
type: hf.type,
|
|
154
|
-
hdrFtrType: hf.hdrFtrType,
|
|
155
|
-
content: item.content
|
|
156
|
-
}));
|
|
157
|
-
else for (const row of item.rows) for (const cell of row.cells) for (const cellContent of cell.content) if (cellContent.type === "paragraph") {
|
|
158
|
-
const paraTexts = [];
|
|
159
|
-
for (const content of cellContent.content) if (content.type === "run") {
|
|
160
|
-
for (const runContent of content.content) if (runContent.type === "text") paraTexts.push(runContent.text);
|
|
161
|
-
}
|
|
162
|
-
texts.push(paraTexts.join(""));
|
|
163
|
-
}
|
|
164
|
-
return texts.join("\n");
|
|
150
|
+
return blockPlainText(hf.content);
|
|
165
151
|
}
|
|
166
152
|
/**
|
|
167
153
|
* Check if header/footer is empty (no content)
|
package/dist/docx/imageParser.js
CHANGED
|
@@ -2,6 +2,7 @@ import { sanitizeImageSrc } from "../utils/sanitizeImageSrc.js";
|
|
|
2
2
|
import { emuToPixels } from "../utils/units.js";
|
|
3
3
|
import { sanitizeExternalUrl } from "../utils/urlSecurity.js";
|
|
4
4
|
import { WRAP_ELEMENT_NAMES, parsePositionH, parsePositionV, parseWrapElement } from "./drawingUtils.js";
|
|
5
|
+
import { parseGraphicFrameLocks } from "./graphicFrameLocks.js";
|
|
5
6
|
import { resolveTarget } from "./relsParser.js";
|
|
6
7
|
import { isTextBoxDrawing } from "./textBoxParser.js";
|
|
7
8
|
import { findByFullName, findChild, getAttribute, getChildElements, parseNumericAttribute, parseOnOffValue } from "./xmlParser.js";
|
|
@@ -299,6 +300,7 @@ function parseInline(inlineEl, rels, media) {
|
|
|
299
300
|
const size = parseExtent(findByFullName(inlineEl, "wp:extent"));
|
|
300
301
|
const padding = parseEffectExtent(findByFullName(inlineEl, "wp:effectExtent"));
|
|
301
302
|
const props = parseDocProps(findByFullName(inlineEl, "wp:docPr"));
|
|
303
|
+
const frameLocks = parseGraphicFrameLocks(inlineEl);
|
|
302
304
|
const blipFill = findBlipFillElement(inlineEl);
|
|
303
305
|
const blip = blipFill ? findByFullName(blipFill, "a:blip") : null;
|
|
304
306
|
const rId = extractBlipRId(blip);
|
|
@@ -334,6 +336,7 @@ function parseInline(inlineEl, rels, media) {
|
|
|
334
336
|
if (transform) image.transform = transform;
|
|
335
337
|
if (crop) image.crop = crop;
|
|
336
338
|
if (opacity !== void 0) image.opacity = opacity;
|
|
339
|
+
if (frameLocks) image.frameLocks = frameLocks;
|
|
337
340
|
if (props.hlinkRId && rels) {
|
|
338
341
|
const safeHref = sanitizeExternalUrl(resolveTarget(rels, props.hlinkRId));
|
|
339
342
|
if (safeHref) {
|
|
@@ -355,6 +358,7 @@ function parseAnchor(anchorEl, rels, media) {
|
|
|
355
358
|
const size = parseExtent(findByFullName(anchorEl, "wp:extent"));
|
|
356
359
|
const padding = parseEffectExtent(findByFullName(anchorEl, "wp:effectExtent"));
|
|
357
360
|
const props = parseDocProps(findByFullName(anchorEl, "wp:docPr"));
|
|
361
|
+
const frameLocks = parseGraphicFrameLocks(anchorEl);
|
|
358
362
|
const behindDoc = parseOnOffValue(getAttribute(anchorEl, null, "behindDoc")) === true;
|
|
359
363
|
const layoutInCell = parseOnOffAttr(anchorEl, "layoutInCell");
|
|
360
364
|
const allowOverlap = parseOnOffAttr(anchorEl, "allowOverlap");
|
|
@@ -405,6 +409,7 @@ function parseAnchor(anchorEl, rels, media) {
|
|
|
405
409
|
if (transform) image.transform = transform;
|
|
406
410
|
if (crop) image.crop = crop;
|
|
407
411
|
if (opacity !== void 0) image.opacity = opacity;
|
|
412
|
+
if (frameLocks) image.frameLocks = frameLocks;
|
|
408
413
|
if (layoutInCell !== void 0) image.layoutInCell = layoutInCell;
|
|
409
414
|
if (allowOverlap !== void 0) image.allowOverlap = allowOverlap;
|
|
410
415
|
if (props.hlinkRId && rels) {
|
|
@@ -1,8 +1,40 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
//#region src/docx/imageRawXml.d.ts
|
|
3
|
+
/** An unclassified drawing is an ordinary editable projection; a classified one is not. */
|
|
4
|
+
declare const allowsDirectDrawingEdit: (mode: document_d_exports.DrawingRawXmlMode | undefined) => boolean;
|
|
5
|
+
/** Narrow an unvalidated value (a ProseMirror attr) to a raw-XML mode. */
|
|
6
|
+
declare const isDrawingRawXmlMode: (value: unknown) => value is document_d_exports.DrawingRawXmlMode;
|
|
7
|
+
/**
|
|
8
|
+
* Stands in for a preview's fingerprint once the editor has changed the image
|
|
9
|
+
* it renders. `canonicalJson` always produces an object literal, so this can
|
|
10
|
+
* never collide with a real fingerprint and the drawing can never replay.
|
|
11
|
+
*/
|
|
12
|
+
declare const EDITED_PREVIEW_FINGERPRINT = "editedPreview";
|
|
3
13
|
/** Fingerprints modeled image fields which make raw DrawingML stale when edited. */
|
|
4
14
|
declare const imageRawXmlFingerprint: (image: document_d_exports.Image) => string;
|
|
5
15
|
/** Editable raw drawing XML can replay only while its modeled projection is unchanged. */
|
|
6
16
|
declare const canReplayEditableImageRawXml: (drawing: document_d_exports.DrawingContent) => boolean;
|
|
17
|
+
/**
|
|
18
|
+
* What a drawing costs the document when Folio writes it back.
|
|
19
|
+
*
|
|
20
|
+
* `native` and `replayable` both survive a save intact, so neither restricts
|
|
21
|
+
* editing; only `opaque` loses content.
|
|
22
|
+
*/
|
|
23
|
+
declare const DRAWING_SAFETY_CLASSES: {
|
|
24
|
+
/** No raw XML: Folio owns the whole drawing and regenerates it from the model. */
|
|
25
|
+
readonly NATIVE: "native";
|
|
26
|
+
/** Raw XML the serializer replays verbatim, so every unmodeled attribute survives. */
|
|
27
|
+
readonly REPLAYABLE: "replayable";
|
|
28
|
+
/** Raw XML the serializer can neither replay nor regenerate without losing the media. */
|
|
29
|
+
readonly OPAQUE: "opaque";
|
|
30
|
+
};
|
|
31
|
+
type DrawingSafetyClass = (typeof DRAWING_SAFETY_CLASSES)[keyof typeof DRAWING_SAFETY_CLASSES];
|
|
32
|
+
/**
|
|
33
|
+
* Classify a drawing by what the run serializer will do with it.
|
|
34
|
+
*
|
|
35
|
+
* Shares {@link canReplayEditableImageRawXml} with the serializer so a
|
|
36
|
+
* compatibility probe and a save can never disagree about the same drawing.
|
|
37
|
+
*/
|
|
38
|
+
declare const classifyDrawingSafety: (drawing: document_d_exports.DrawingContent) => DrawingSafetyClass;
|
|
7
39
|
//#endregion
|
|
8
|
-
export { canReplayEditableImageRawXml, imageRawXmlFingerprint };
|
|
40
|
+
export { DRAWING_SAFETY_CLASSES, DrawingSafetyClass, EDITED_PREVIEW_FINGERPRINT, allowsDirectDrawingEdit, canReplayEditableImageRawXml, classifyDrawingSafety, imageRawXmlFingerprint, isDrawingRawXmlMode };
|
package/dist/docx/imageRawXml.js
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import { canonicalJson } from "../utils/canonicalJson.js";
|
|
2
2
|
import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
|
|
3
3
|
//#region src/docx/imageRawXml.ts
|
|
4
|
+
/**
|
|
5
|
+
* Whether the editor may manipulate the modeled image of a classified drawing.
|
|
6
|
+
*
|
|
7
|
+
* A mode exists precisely because `rawXml` says more than the model does, so
|
|
8
|
+
* both current modes refuse: a resize would either serialize a placeholder
|
|
9
|
+
* (preserve-only) or one child picture in place of a group (preview-only).
|
|
10
|
+
* Totality is the point — a third mode cannot be added without deciding here.
|
|
11
|
+
*/
|
|
12
|
+
const DRAWING_RAW_XML_MODE_ALLOWS_DIRECT_EDIT = {
|
|
13
|
+
[DRAWING_RAW_XML_MODES.PRESERVE_ONLY]: false,
|
|
14
|
+
[DRAWING_RAW_XML_MODES.PREVIEW_ONLY]: false
|
|
15
|
+
};
|
|
16
|
+
/** An unclassified drawing is an ordinary editable projection; a classified one is not. */
|
|
17
|
+
const allowsDirectDrawingEdit = (mode) => mode === void 0 || DRAWING_RAW_XML_MODE_ALLOWS_DIRECT_EDIT[mode];
|
|
18
|
+
/** Narrow an unvalidated value (a ProseMirror attr) to a raw-XML mode. */
|
|
19
|
+
const isDrawingRawXmlMode = (value) => typeof value === "string" && value in DRAWING_RAW_XML_MODE_ALLOWS_DIRECT_EDIT;
|
|
20
|
+
/**
|
|
21
|
+
* Stands in for a preview's fingerprint once the editor has changed the image
|
|
22
|
+
* it renders. `canonicalJson` always produces an object literal, so this can
|
|
23
|
+
* never collide with a real fingerprint and the drawing can never replay.
|
|
24
|
+
*/
|
|
25
|
+
const EDITED_PREVIEW_FINGERPRINT = "editedPreview";
|
|
4
26
|
const editableImageProjection = ({ id: _id, rId: _rId, src: _src, mimeType: _mimeType, filename: _filename, ...image }) => image;
|
|
5
27
|
/** Fingerprints modeled image fields which make raw DrawingML stale when edited. */
|
|
6
28
|
const imageRawXmlFingerprint = (image) => canonicalJson(editableImageProjection(image));
|
|
@@ -9,5 +31,38 @@ const canReplayEditableImageRawXml = (drawing) => {
|
|
|
9
31
|
if (drawing.rawXmlMode === DRAWING_RAW_XML_MODES.PRESERVE_ONLY) return true;
|
|
10
32
|
return drawing.rawImageFingerprint === void 0 || drawing.rawImageFingerprint === imageRawXmlFingerprint(drawing.image);
|
|
11
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* What a drawing costs the document when Folio writes it back.
|
|
36
|
+
*
|
|
37
|
+
* `native` and `replayable` both survive a save intact, so neither restricts
|
|
38
|
+
* editing; only `opaque` loses content.
|
|
39
|
+
*/
|
|
40
|
+
const DRAWING_SAFETY_CLASSES = {
|
|
41
|
+
/** No raw XML: Folio owns the whole drawing and regenerates it from the model. */
|
|
42
|
+
NATIVE: "native",
|
|
43
|
+
/** Raw XML the serializer replays verbatim, so every unmodeled attribute survives. */
|
|
44
|
+
REPLAYABLE: "replayable",
|
|
45
|
+
/** Raw XML the serializer can neither replay nor regenerate without losing the media. */
|
|
46
|
+
OPAQUE: "opaque"
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Regenerated DrawingML points at `image.rId`, and `serializePicGraphic` falls
|
|
50
|
+
* back to `"rId1"` when that id is empty, which rebinds the picture to whichever
|
|
51
|
+
* relationship happens to be first. A drawing with no relationship id therefore
|
|
52
|
+
* has no faithful regeneration.
|
|
53
|
+
*/
|
|
54
|
+
const canRegenerateDrawing = (drawing) => drawing.image.rId !== "";
|
|
55
|
+
/**
|
|
56
|
+
* Classify a drawing by what the run serializer will do with it.
|
|
57
|
+
*
|
|
58
|
+
* Shares {@link canReplayEditableImageRawXml} with the serializer so a
|
|
59
|
+
* compatibility probe and a save can never disagree about the same drawing.
|
|
60
|
+
*/
|
|
61
|
+
const classifyDrawingSafety = (drawing) => {
|
|
62
|
+
if (drawing.rawXml === void 0) return DRAWING_SAFETY_CLASSES.NATIVE;
|
|
63
|
+
if (canReplayEditableImageRawXml(drawing)) return DRAWING_SAFETY_CLASSES.REPLAYABLE;
|
|
64
|
+
if (drawing.rawXmlMode === DRAWING_RAW_XML_MODES.PREVIEW_ONLY) return DRAWING_SAFETY_CLASSES.OPAQUE;
|
|
65
|
+
return canRegenerateDrawing(drawing) ? DRAWING_SAFETY_CLASSES.NATIVE : DRAWING_SAFETY_CLASSES.OPAQUE;
|
|
66
|
+
};
|
|
12
67
|
//#endregion
|
|
13
|
-
export { canReplayEditableImageRawXml, imageRawXmlFingerprint };
|
|
68
|
+
export { DRAWING_SAFETY_CLASSES, EDITED_PREVIEW_FINGERPRINT, allowsDirectDrawingEdit, canReplayEditableImageRawXml, classifyDrawingSafety, imageRawXmlFingerprint, isDrawingRawXmlMode };
|
package/dist/docx/rezip.d.ts
CHANGED
|
@@ -185,18 +185,35 @@ declare const validateDocx: (buffer: ArrayBuffer) => Promise<{
|
|
|
185
185
|
* @returns true if buffer starts with ZIP signature
|
|
186
186
|
*/
|
|
187
187
|
declare function isDocxBuffer(buffer: ArrayBuffer): boolean;
|
|
188
|
+
/**
|
|
189
|
+
* The document properties a package built from scratch states about itself.
|
|
190
|
+
*
|
|
191
|
+
* A property the host does not name is not written at all: the package states
|
|
192
|
+
* an author and an application only when a caller supplies one. Both are
|
|
193
|
+
* ignored for a document that carries a source package, which keeps the
|
|
194
|
+
* properties that package already states.
|
|
195
|
+
*/
|
|
196
|
+
type DocumentPropertiesOptions = {
|
|
197
|
+
/** `dc:creator` in `docProps/core.xml`. Omitted when absent. */
|
|
198
|
+
creator?: string;
|
|
199
|
+
/** `Application` in `docProps/app.xml`. Omitted, with `AppVersion`, when absent. */
|
|
200
|
+
application?: string;
|
|
201
|
+
};
|
|
188
202
|
/**
|
|
189
203
|
* Create a new empty DOCX file
|
|
190
204
|
*
|
|
205
|
+
* @param properties - Document properties the package states about itself
|
|
191
206
|
* @returns Promise resolving to minimal DOCX as ArrayBuffer
|
|
192
207
|
*/
|
|
193
|
-
declare function createEmptyDocx(): Promise<ArrayBuffer>;
|
|
208
|
+
declare function createEmptyDocx(properties?: DocumentPropertiesOptions): Promise<ArrayBuffer>;
|
|
194
209
|
/**
|
|
195
210
|
* Create a new DOCX from a Document (without requiring original buffer)
|
|
196
211
|
*
|
|
197
212
|
* @param doc - Document to serialize
|
|
213
|
+
* @param properties - Document properties a package built from scratch states
|
|
214
|
+
* about itself; ignored when `doc` carries a source package
|
|
198
215
|
* @returns Promise resolving to DOCX as ArrayBuffer
|
|
199
216
|
*/
|
|
200
|
-
declare function createDocx(doc: document_d_exports.Document): Promise<ArrayBuffer>;
|
|
217
|
+
declare function createDocx(doc: document_d_exports.Document, properties?: DocumentPropertiesOptions): Promise<ArrayBuffer>;
|
|
201
218
|
//#endregion
|
|
202
|
-
export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocxPackageFidelityError, RepackOptions, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, collectHyperlinksWithoutRId, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, hasUnmaterializedInlineResources, isDocxBuffer, notePartRelsPath, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx, withoutAttachedTemplate };
|
|
219
|
+
export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocumentPropertiesOptions, DocxPackageFidelityError, RepackOptions, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, collectHyperlinksWithoutRId, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, hasUnmaterializedInlineResources, isDocxBuffer, notePartRelsPath, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx, withoutAttachedTemplate };
|
package/dist/docx/rezip.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { consumeTrackedSectionEndpointRemoval } from "../internal/sectionEndpointResolution.js";
|
|
2
2
|
import { consumeSectionReferenceResolution } from "../internal/sectionReferenceResolution.js";
|
|
3
3
|
import { isAllowedExternalWatermarkImageUrl } from "../watermark/index.js";
|
|
4
|
-
import { normalizeAppVersionInExtendedProperties } from "./appVersionNormalization.js";
|
|
4
|
+
import { appVersionInSchemaForm, normalizeAppVersionInExtendedProperties } from "./appVersionNormalization.js";
|
|
5
5
|
import { withoutOrphanCommentRanges } from "./commentRangeIntegrity.js";
|
|
6
6
|
import { applyReplyThreadMarkers } from "./commentReplyMarkers.js";
|
|
7
7
|
import { normalizeDrawingIds } from "./drawingIdNormalization.js";
|
|
@@ -1524,14 +1524,24 @@ function isDocxBuffer(buffer) {
|
|
|
1524
1524
|
return view[0] === 80 && view[1] === 75;
|
|
1525
1525
|
}
|
|
1526
1526
|
/**
|
|
1527
|
+
* The application version a newly created package states.
|
|
1528
|
+
*
|
|
1529
|
+
* `AppVersion` describes the application, so it is written only alongside one,
|
|
1530
|
+
* and it says nothing about the host beyond the `XX.YYYY` form the schema
|
|
1531
|
+
* fixes. Deriving it from the mapping every save exit already uses keeps the
|
|
1532
|
+
* two from drifting apart.
|
|
1533
|
+
*/
|
|
1534
|
+
const CREATED_APP_VERSION = appVersionInSchemaForm("1");
|
|
1535
|
+
/**
|
|
1527
1536
|
* Create a new empty DOCX file
|
|
1528
1537
|
*
|
|
1538
|
+
* @param properties - Document properties the package states about itself
|
|
1529
1539
|
* @returns Promise resolving to minimal DOCX as ArrayBuffer
|
|
1530
1540
|
*/
|
|
1531
|
-
function createEmptyDocx() {
|
|
1532
|
-
return generateDocxZip(createEmptyDocxZip(), 6);
|
|
1541
|
+
function createEmptyDocx(properties = {}) {
|
|
1542
|
+
return generateDocxZip(createEmptyDocxZip(properties), 6);
|
|
1533
1543
|
}
|
|
1534
|
-
const createEmptyDocxZip = () => {
|
|
1544
|
+
const createEmptyDocxZip = ({ creator, application }) => {
|
|
1535
1545
|
const zip = new JSZip();
|
|
1536
1546
|
zip.file("[Content_Types].xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1537
1547
|
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
@@ -1586,16 +1596,15 @@ const createEmptyDocxZip = () => {
|
|
|
1586
1596
|
</w:style>
|
|
1587
1597
|
</w:styles>`);
|
|
1588
1598
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1599
|
+
const creatorElement = creator === void 0 ? "" : `\n <dc:creator>${escapeXml(creator)}</dc:creator>`;
|
|
1589
1600
|
zip.file("docProps/core.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1590
|
-
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
1591
|
-
<dc:creator>EigenPal DOCX Editor</dc:creator>
|
|
1601
|
+
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">${creatorElement}
|
|
1592
1602
|
<dcterms:created xsi:type="dcterms:W3CDTF">${now}</dcterms:created>
|
|
1593
1603
|
<dcterms:modified xsi:type="dcterms:W3CDTF">${now}</dcterms:modified>
|
|
1594
1604
|
</cp:coreProperties>`);
|
|
1605
|
+
const applicationElements = application === void 0 ? "" : `\n <Application>${escapeXml(application)}</Application>\n <AppVersion>${CREATED_APP_VERSION}</AppVersion>`;
|
|
1595
1606
|
zip.file("docProps/app.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1596
|
-
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"
|
|
1597
|
-
<Application>EigenPal DOCX Editor</Application>
|
|
1598
|
-
<AppVersion>1.0000</AppVersion>
|
|
1607
|
+
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">${applicationElements}
|
|
1599
1608
|
</Properties>`);
|
|
1600
1609
|
return zip;
|
|
1601
1610
|
};
|
|
@@ -1603,9 +1612,11 @@ const createEmptyDocxZip = () => {
|
|
|
1603
1612
|
* Create a new DOCX from a Document (without requiring original buffer)
|
|
1604
1613
|
*
|
|
1605
1614
|
* @param doc - Document to serialize
|
|
1615
|
+
* @param properties - Document properties a package built from scratch states
|
|
1616
|
+
* about itself; ignored when `doc` carries a source package
|
|
1606
1617
|
* @returns Promise resolving to DOCX as ArrayBuffer
|
|
1607
1618
|
*/
|
|
1608
|
-
async function createDocx(doc) {
|
|
1619
|
+
async function createDocx(doc, properties = {}) {
|
|
1609
1620
|
if (doc.originalBuffer) {
|
|
1610
1621
|
const source = await loadParsedZipSource(doc, doc.originalBuffer);
|
|
1611
1622
|
return finishRepack({
|
|
@@ -1618,7 +1629,7 @@ async function createDocx(doc) {
|
|
|
1618
1629
|
updateModifiedDate: true
|
|
1619
1630
|
});
|
|
1620
1631
|
}
|
|
1621
|
-
const zip = await createDocumentSeedZip(doc);
|
|
1632
|
+
const zip = await createDocumentSeedZip(doc, properties);
|
|
1622
1633
|
return finishRepack({
|
|
1623
1634
|
document: withoutOrphanCommentRanges(doc),
|
|
1624
1635
|
originalZip: zip,
|
|
@@ -1629,8 +1640,8 @@ async function createDocx(doc) {
|
|
|
1629
1640
|
updateModifiedDate: true
|
|
1630
1641
|
});
|
|
1631
1642
|
}
|
|
1632
|
-
const createDocumentSeedZip = async (doc) => {
|
|
1633
|
-
const zip = createEmptyDocxZip();
|
|
1643
|
+
const createDocumentSeedZip = async (doc, properties) => {
|
|
1644
|
+
const zip = createEmptyDocxZip(properties);
|
|
1634
1645
|
const relationships = ["<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/>"];
|
|
1635
1646
|
const overrides = [];
|
|
1636
1647
|
let nextRelationshipId = 2;
|
package/dist/docx/runParser.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isValidHexColor } from "../utils/colorResolver.js";
|
|
2
2
|
import { parseHorizontalScalePercent } from "../utils/horizontalScale.js";
|
|
3
3
|
import { parseDiagramPreview } from "./diagramPreview.js";
|
|
4
|
-
import { parseGroupDrawing } from "./groupDrawingParser.js";
|
|
4
|
+
import { isGroupDrawing, parseGroupDrawing } from "./groupDrawingParser.js";
|
|
5
5
|
import { parseImage } from "./imageParser.js";
|
|
6
6
|
import { imageRawXmlFingerprint } from "./imageRawXml.js";
|
|
7
7
|
import { EmphasisMarkSchema, FontHintSchema, FontThemeSchema, HighlightColorSchema, PositionalTabAlignmentSchema, PositionalTabLeaderSchema, PositionalTabRelativeToSchema, ShadingPatternSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum } from "./parserEnums.js";
|
|
@@ -11,7 +11,7 @@ import { requiresXmlSpacePreserve } from "./textWhitespace.js";
|
|
|
11
11
|
import { resolveThemeFontRef } from "./themeParser.js";
|
|
12
12
|
import { parsePropertyChangeInfo } from "./trackedChangeInfo.js";
|
|
13
13
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
14
|
-
import { parseVmlImageContent } from "./vmlImageParser.js";
|
|
14
|
+
import { parseVmlImageContent, shouldPreserveRawVmlPict } from "./vmlImageParser.js";
|
|
15
15
|
import { cloneWithXmlnsDeclarations, findAllDeep, findChild, findChildren, getAttribute, getChildElements, getLocalName, getTextContent, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, selectAlternateContentBranch } from "./xmlParser.js";
|
|
16
16
|
import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
|
|
17
17
|
//#region src/docx/runParser.ts
|
|
@@ -470,6 +470,28 @@ function parseInstrText(element) {
|
|
|
470
470
|
};
|
|
471
471
|
}
|
|
472
472
|
/**
|
|
473
|
+
* Wrap raw XML the model cannot project at all.
|
|
474
|
+
*
|
|
475
|
+
* `DrawingContent` always carries an `Image`, so preservation-only content
|
|
476
|
+
* gets a placeholder one: the empty `rId` marks it as backed by no
|
|
477
|
+
* relationship, which keeps `classifyDrawingSafety` and the serializer on the
|
|
478
|
+
* replay path instead of regenerating DrawingML from the placeholder.
|
|
479
|
+
*/
|
|
480
|
+
const preserveOnlyDrawing = (rawXml) => ({
|
|
481
|
+
type: "drawing",
|
|
482
|
+
image: {
|
|
483
|
+
type: "image",
|
|
484
|
+
rId: "",
|
|
485
|
+
size: {
|
|
486
|
+
width: 0,
|
|
487
|
+
height: 0
|
|
488
|
+
},
|
|
489
|
+
wrap: { type: "inline" }
|
|
490
|
+
},
|
|
491
|
+
rawXml,
|
|
492
|
+
rawXmlMode: DRAWING_RAW_XML_MODES.PRESERVE_ONLY
|
|
493
|
+
});
|
|
494
|
+
/**
|
|
473
495
|
* Parse drawing content (w:drawing).
|
|
474
496
|
*
|
|
475
497
|
* Dispatches by graphicData payload:
|
|
@@ -480,6 +502,8 @@ function parseInstrText(element) {
|
|
|
480
502
|
* context that is only available at the block parser level).
|
|
481
503
|
* - `wps:wsp` without text body → generic shape; parsed via
|
|
482
504
|
* `shapeParser.parseShapeFromDrawing` into a `ShapeContent`.
|
|
505
|
+
* - anything the model cannot project (a group the rasterizer declines,
|
|
506
|
+
* a diagram, a shape with unmodeled properties) → preservation-only raw XML.
|
|
483
507
|
*/
|
|
484
508
|
function parseDrawingContent(element, rels, media) {
|
|
485
509
|
const groupImage = parseGroupDrawing(element, rels ?? void 0, media ?? void 0);
|
|
@@ -487,8 +511,10 @@ function parseDrawingContent(element, rels, media) {
|
|
|
487
511
|
type: "drawing",
|
|
488
512
|
image: groupImage,
|
|
489
513
|
rawXml: captureVerbatimXml(element),
|
|
490
|
-
rawImageFingerprint: imageRawXmlFingerprint(groupImage)
|
|
514
|
+
rawImageFingerprint: imageRawXmlFingerprint(groupImage),
|
|
515
|
+
rawXmlMode: DRAWING_RAW_XML_MODES.PREVIEW_ONLY
|
|
491
516
|
};
|
|
517
|
+
if (isGroupDrawing(element)) return preserveOnlyDrawing(captureVerbatimXml(element));
|
|
492
518
|
const diagramImage = parseDiagramPreview(element, rels ?? void 0, media ?? void 0);
|
|
493
519
|
if (diagramImage) return {
|
|
494
520
|
type: "drawing",
|
|
@@ -496,20 +522,7 @@ function parseDrawingContent(element, rels, media) {
|
|
|
496
522
|
rawXml: captureVerbatimXml(element),
|
|
497
523
|
rawXmlMode: DRAWING_RAW_XML_MODES.PRESERVE_ONLY
|
|
498
524
|
};
|
|
499
|
-
if (shouldPreserveRawShapeDrawing(element)) return
|
|
500
|
-
type: "drawing",
|
|
501
|
-
image: {
|
|
502
|
-
type: "image",
|
|
503
|
-
rId: "",
|
|
504
|
-
size: {
|
|
505
|
-
width: 0,
|
|
506
|
-
height: 0
|
|
507
|
-
},
|
|
508
|
-
wrap: { type: "inline" }
|
|
509
|
-
},
|
|
510
|
-
rawXml: captureVerbatimXml(element),
|
|
511
|
-
rawXmlMode: DRAWING_RAW_XML_MODES.PRESERVE_ONLY
|
|
512
|
-
};
|
|
525
|
+
if (shouldPreserveRawShapeDrawing(element)) return preserveOnlyDrawing(captureVerbatimXml(element));
|
|
513
526
|
const shape = parseShapeFromDrawing(element);
|
|
514
527
|
if (shape) return {
|
|
515
528
|
type: "shape",
|
|
@@ -572,7 +585,11 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
|
|
|
572
585
|
}
|
|
573
586
|
case "pict": {
|
|
574
587
|
const vmlDrawing = parseVmlImageContent(child, rels, media, rootXmlns);
|
|
575
|
-
if (vmlDrawing)
|
|
588
|
+
if (vmlDrawing) {
|
|
589
|
+
contents.push(vmlDrawing);
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
if (shouldPreserveRawVmlPict(child)) contents.push(preserveOnlyDrawing(captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns))));
|
|
576
593
|
break;
|
|
577
594
|
}
|
|
578
595
|
case "object": {
|
|
@@ -594,13 +611,16 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
|
|
|
594
611
|
const alternateChildren = getChildElements(child);
|
|
595
612
|
const choiceEl = alternateChildren.find((el) => getLocalName(el.name) === "Choice");
|
|
596
613
|
const fallbackEl = alternateChildren.find((el) => getLocalName(el.name) === "Fallback");
|
|
614
|
+
const contentsBeforeAlternate = contents.length;
|
|
597
615
|
const choiceTextBoxDrawing = choiceEl ? getChildElements(choiceEl).find((element) => getLocalName(element.name) === "drawing" && isTextBoxDrawing(element)) : void 0;
|
|
598
616
|
const groupedChoiceDrawing = choiceEl ? getChildElements(choiceEl).find((element) => getLocalName(element.name) === "drawing" && findAllDeep(element, "wpg", "wgp").length > 0) : void 0;
|
|
599
617
|
if (groupedChoiceDrawing) {
|
|
600
618
|
const groupedDrawing = parseDrawingContent(groupedChoiceDrawing, rels, media);
|
|
601
|
-
if (groupedDrawing?.type === "drawing" && groupedDrawing.image.src) {
|
|
602
|
-
|
|
603
|
-
|
|
619
|
+
if (groupedDrawing?.type === "drawing" && groupedDrawing.rawXmlMode === DRAWING_RAW_XML_MODES.PREVIEW_ONLY && groupedDrawing.image.src) {
|
|
620
|
+
contents.push({
|
|
621
|
+
...groupedDrawing,
|
|
622
|
+
rawXml: captureVerbatimXml(child)
|
|
623
|
+
});
|
|
604
624
|
break;
|
|
605
625
|
}
|
|
606
626
|
}
|
|
@@ -631,6 +651,8 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
|
|
|
631
651
|
elements: [innerChild]
|
|
632
652
|
}, rels, media, rootXmlns));
|
|
633
653
|
}
|
|
654
|
+
const hasTextBoxBranch = [choiceEl, fallbackEl].some((branch) => getChildElements(branch).some((element) => getLocalName(element.name) === "drawing" && isTextBoxDrawing(element)));
|
|
655
|
+
if (contents.length === contentsBeforeAlternate && !hasTextBoxBranch) contents.push(preserveOnlyDrawing(captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns))));
|
|
634
656
|
break;
|
|
635
657
|
}
|
|
636
658
|
case "footnoteRef":
|