@stll/folio-core 0.43.0 → 0.45.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/dist/ai-edits/__fixtures__/paragraphs.js +2 -2
- package/dist/ai-edits/headless.js +7 -5
- package/dist/ai-edits/index.d.ts +2 -2
- package/dist/ai-edits/index.js +2 -2
- package/dist/ai-edits/snapshot.js +13 -9
- package/dist/compare/content-alignment.js +94 -54
- package/dist/compare/inline-atoms.js +34 -20
- package/dist/compare/style-resources.js +6 -0
- package/dist/content-controls/mutateContentControls.js +4 -2
- package/dist/display-list/dom/renderDisplayListToDom.js +8 -8
- package/dist/document-operations.js +14 -3
- package/dist/docx/appVersionNormalization.d.ts +0 -18
- package/dist/docx/blockContentParser.js +8 -0
- package/dist/docx/blockRangeMarkers.d.ts +36 -0
- package/dist/docx/blockRangeMarkers.js +59 -0
- package/dist/docx/bookmarkParser.d.ts +2 -20
- package/dist/docx/bookmarkParser.js +6 -30
- package/dist/docx/borderParser.d.ts +13 -0
- package/dist/docx/borderParser.js +71 -0
- package/dist/docx/builtInStyles.d.ts +165 -0
- package/dist/docx/builtInStyles.js +239 -0
- package/dist/docx/commentIdNormalization.d.ts +3 -1
- package/dist/docx/commentIdNormalization.js +18 -1
- package/dist/docx/commentParser.d.ts +2 -1
- package/dist/docx/commentParser.js +80 -42
- package/dist/docx/commentReferenceNormalization.d.ts +4 -1
- package/dist/docx/commentReferenceNormalization.js +23 -14
- package/dist/docx/commentThreadKey.d.ts +18 -0
- package/dist/docx/commentThreadKey.js +22 -0
- package/dist/docx/danglingRelationshipReferences.d.ts +15 -0
- package/dist/docx/danglingRelationshipReferences.js +30 -0
- package/dist/docx/defaultParagraphStyle.d.ts +18 -1
- package/dist/docx/defaultParagraphStyle.js +23 -1
- package/dist/docx/diagramPreview.js +87 -27
- package/dist/docx/documentParser.d.ts +2 -1
- package/dist/docx/documentParser.js +2 -2
- package/dist/docx/drawingUtils.d.ts +8 -1
- package/dist/docx/drawingUtils.js +12 -3
- package/dist/docx/fieldParser.js +3 -5
- package/dist/docx/footnoteParser.d.ts +3 -2
- package/dist/docx/footnoteParser.js +19 -4
- package/dist/docx/groupDrawingParser.js +4 -4
- package/dist/docx/headerFooterRefParser.d.ts +4 -3
- package/dist/docx/headerFooterRefParser.js +42 -12
- package/dist/docx/headerFooterReferenceNormalization.d.ts +4 -1
- package/dist/docx/headerFooterReferenceNormalization.js +5 -1
- package/dist/docx/hyperlinkParser.js +13 -17
- package/dist/docx/imageParser.d.ts +10 -2
- package/dist/docx/imageParser.js +80 -30
- package/dist/docx/imageRawXml.d.ts +14 -1
- package/dist/docx/imageRawXml.js +35 -11
- package/dist/docx/markupRangeMarker.d.ts +15 -0
- package/dist/docx/markupRangeMarker.js +44 -0
- package/dist/docx/mathToMathml.js +12 -14
- package/dist/docx/nonVisualDrawingProps.d.ts +34 -0
- package/dist/docx/nonVisualDrawingProps.js +46 -0
- package/dist/docx/noteReferenceStyles.d.ts +29 -0
- package/dist/docx/noteReferenceStyles.js +70 -0
- package/dist/docx/numberingReferenceNormalization.d.ts +4 -1
- package/dist/docx/numberingReferenceNormalization.js +20 -1
- package/dist/docx/paraIdRangeNormalization.d.ts +0 -19
- package/dist/docx/paragraphParser.js +66 -99
- package/dist/docx/paragraphPropertySource.js +1 -0
- package/dist/docx/paragraphTextBoxEnrichment.js +3 -0
- package/dist/docx/paragraphTraversal.d.ts +37 -1
- package/dist/docx/paragraphTraversal.js +84 -1
- package/dist/docx/parseContext.d.ts +37 -0
- package/dist/docx/parseContext.js +67 -0
- package/dist/docx/parseWarningMessage.d.ts +6 -0
- package/dist/docx/parseWarningMessage.js +44 -0
- package/dist/docx/parser.js +83 -29
- package/dist/docx/previewBudget.d.ts +64 -0
- package/dist/docx/previewBudget.js +88 -0
- package/dist/docx/relsParser.d.ts +28 -11
- package/dist/docx/relsParser.js +26 -13
- package/dist/docx/revisionIdNormalization.js +96 -10
- package/dist/docx/rezip.js +80 -40
- package/dist/docx/runConsolidator.js +1 -2
- package/dist/docx/runParser.d.ts +8 -1
- package/dist/docx/runParser.js +30 -48
- package/dist/docx/sdtPropertiesPatch.js +24 -18
- package/dist/docx/sectionParser.d.ts +2 -1
- package/dist/docx/sectionParser.js +21 -65
- package/dist/docx/sectionReferenceHistory.js +2 -2
- package/dist/docx/selectiveSave.js +6 -6
- package/dist/docx/serializer/blockSdtSerializer.js +38 -26
- package/dist/docx/serializer/borderSerializer.d.ts +2 -3
- package/dist/docx/serializer/borderSerializer.js +13 -12
- package/dist/docx/serializer/commentSerializer.d.ts +41 -16
- package/dist/docx/serializer/commentSerializer.js +82 -72
- package/dist/docx/serializer/documentSerializer.d.ts +1 -5
- package/dist/docx/serializer/documentSerializer.js +6 -16
- package/dist/docx/serializer/fontTableSerializer.js +6 -6
- package/dist/docx/serializer/headerFooterSerializer.js +10 -5
- package/dist/docx/serializer/markupRangeAttributes.d.ts +8 -0
- package/dist/docx/serializer/markupRangeAttributes.js +24 -0
- package/dist/docx/serializer/noteSerializer.js +5 -0
- package/dist/docx/serializer/numberingSerializer.js +7 -6
- package/dist/docx/serializer/paragraphSerializer.d.ts +1 -5
- package/dist/docx/serializer/paragraphSerializer.js +47 -52
- package/dist/docx/serializer/partNamespaces.js +2 -2
- package/dist/docx/serializer/runSerializer.js +57 -31
- package/dist/docx/serializer/sectionPropertiesSerializer.js +11 -10
- package/dist/docx/serializer/settingsSerializer.js +4 -3
- package/dist/docx/serializer/stylesSerializer.js +6 -6
- package/dist/docx/serializer/tableSerializer.js +37 -21
- package/dist/docx/serializer/textFormattingSerializer.d.ts +2 -3
- package/dist/docx/serializer/textFormattingSerializer.js +29 -28
- package/dist/docx/serializer/themeSerializer.js +6 -6
- package/dist/docx/serializer/trackedChangeAttributes.js +2 -2
- package/dist/docx/serializer/xmlUtils.d.ts +1 -2
- package/dist/docx/serializer/xmlUtils.js +1 -13
- package/dist/docx/server/boundedArchive.d.ts +12 -0
- package/dist/docx/server/boundedArchive.js +20 -1
- package/dist/docx/server/build.js +8 -1
- package/dist/docx/server/createBilingualDocument.js +10 -18
- package/dist/docx/server/extractDocxText.js +3 -4
- package/dist/docx/server/validateDocxConformance.js +22 -1
- package/dist/docx/shadingParser.d.ts +6 -0
- package/dist/docx/shadingParser.js +32 -0
- package/dist/docx/shapeParser.js +10 -8
- package/dist/docx/styleParser.js +13 -87
- package/dist/docx/styleReferenceResolution.d.ts +36 -0
- package/dist/docx/styleReferenceResolution.js +51 -0
- package/dist/docx/tableLook.d.ts +57 -0
- package/dist/docx/tableLook.js +63 -0
- package/dist/docx/tableParser.d.ts +7 -9
- package/dist/docx/tableParser.js +64 -110
- package/dist/docx/textBoxParser.js +11 -6
- package/dist/docx/trackedMoveRangeNormalization.d.ts +3 -1
- package/dist/docx/trackedMoveRangeNormalization.js +11 -21
- package/dist/docx/transitionalSpelling.d.ts +13 -2
- package/dist/docx/transitionalSpelling.js +23 -1
- package/dist/docx/unzip.d.ts +23 -0
- package/dist/docx/unzip.js +32 -22
- package/dist/docx/verbatimCapture.js +5 -12
- package/dist/docx/vmlImageParser.js +5 -4
- package/dist/docx/vmlPreview.d.ts +1 -3
- package/dist/docx/vmlPreview.js +2 -30
- package/dist/docx/watermarkParser.js +2 -2
- package/dist/docx/xmlParser.d.ts +38 -33
- package/dist/docx/xmlParser.js +92 -47
- package/dist/docx/xmlResourceLimits.d.ts +89 -9
- package/dist/docx/xmlResourceLimits.js +105 -24
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
- package/dist/internal/paragraphFormattingSerialization.d.ts +2 -3
- package/dist/internal/paragraphFormattingSerialization.js +29 -8
- package/dist/layout-bridge/convert/footnoteLayout.js +2 -7
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +2 -2
- package/dist/layout-engine/measure/measureBlocks.js +1 -6
- package/dist/layout-engine/types.d.ts +8 -2
- package/dist/layout-engine/types.js +35 -2
- package/dist/layout-painter/renderImage.js +4 -3
- package/dist/layout-painter/renderParagraph.js +4 -3
- package/dist/managers/autoSaveCodec.js +2 -8
- package/dist/markdown/images.js +1 -4
- package/dist/markdown/index.js +1 -1
- package/dist/markdown/internals.d.ts +6 -1
- package/dist/markdown/internals.js +14 -1
- package/dist/markdown/renderBlock.js +35 -21
- package/dist/markdown/renderParagraph.js +14 -5
- package/dist/markdown/renderRuns.js +4 -3
- package/dist/markdown/renderTable.js +4 -3
- package/dist/markdown/trailers.js +41 -7
- package/dist/markdown/types.d.ts +3 -7
- package/dist/prosemirror/attrs/index.js +71 -5
- package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +11 -1
- package/dist/prosemirror/bookmarkBoundaryAttrs.js +18 -3
- package/dist/prosemirror/commands/image.js +1 -0
- package/dist/prosemirror/commands/index.d.ts +3 -3
- package/dist/prosemirror/commands/index.js +2 -2
- package/dist/prosemirror/commands/paragraph.d.ts +3 -3
- package/dist/prosemirror/commands/paragraph.js +2 -2
- package/dist/prosemirror/commentIdAllocator.js +2 -7
- package/dist/prosemirror/conversion/fromProseDoc.js +197 -68
- package/dist/prosemirror/conversion/toProseDoc.d.ts +1 -14
- package/dist/prosemirror/conversion/toProseDoc.js +458 -335
- package/dist/prosemirror/extensions/core/ParagraphExtension.d.ts +14 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +11 -6
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -3
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.d.ts +4 -1
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +6 -2
- package/dist/prosemirror/extensions/features/pastedHeadingStyles.d.ts +7 -0
- package/dist/prosemirror/extensions/features/pastedHeadingStyles.js +74 -0
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +2 -3
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +11 -3
- package/dist/prosemirror/extensions/marks/markUtils.js +98 -19
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +7 -3
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +6 -1
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +8 -2
- package/dist/prosemirror/extensions/nodes/TableExtension.js +15 -1
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +8 -4
- package/dist/prosemirror/extensions/types.d.ts +2 -2
- package/dist/prosemirror/index.d.ts +3 -3
- package/dist/prosemirror/index.js +3 -3
- package/dist/prosemirror/insertOperations.d.ts +9 -2
- package/dist/prosemirror/insertOperations.js +9 -4
- package/dist/prosemirror/paragraphFormattingProvenance.d.ts +162 -0
- package/dist/prosemirror/paragraphFormattingProvenance.js +115 -0
- package/dist/prosemirror/plugins/documentStyles.d.ts +9 -1
- package/dist/prosemirror/plugins/documentStyles.js +11 -1
- package/dist/prosemirror/plugins/index.d.ts +2 -2
- package/dist/prosemirror/plugins/index.js +2 -2
- package/dist/prosemirror/plugins/revisionIds.d.ts +11 -2
- package/dist/prosemirror/plugins/revisionIds.js +21 -6
- package/dist/prosemirror/runFormattingReconciliation.js +3 -2
- package/dist/prosemirror/runStyleFormatting.d.ts +1 -1
- package/dist/prosemirror/schema/nodes.d.ts +81 -1
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -0
- package/dist/prosemirror/styles/styleResolver.d.ts +9 -0
- package/dist/prosemirror/styles/styleResolver.js +12 -0
- package/dist/style-engine/styleEngine.d.ts +3 -0
- package/dist/style-engine/styleEngine.js +3 -0
- package/dist/style-sets/extract.js +1 -23
- package/dist/style-sets/stellaStyle.js +46 -39
- package/dist/style-sets/styleSetNormalization.d.ts +19 -0
- package/dist/style-sets/styleSetNormalization.js +99 -0
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/base64.d.ts +36 -0
- package/dist/utils/base64.js +40 -0
- package/dist/utils/clipboard.js +2 -1
- package/dist/utils/createDocument.js +145 -20
- package/dist/utils/headingCollector.d.ts +8 -5
- package/dist/utils/headingCollector.js +23 -25
- package/dist/utils/tableOfContentsStyle.js +9 -2
- package/dist/utils/units.d.ts +10 -1
- package/dist/utils/units.js +12 -1
- package/dist/utils/urlSecurity.d.ts +8 -2
- package/dist/utils/urlSecurity.js +21 -3
- package/package.json +2 -2
- package/dist/docx/textWhitespace.d.ts +0 -4
- package/dist/docx/textWhitespace.js +0 -4
- package/dist/layout-bridge/engine/tableWidthUtils.d.ts +0 -6
- package/dist/layout-bridge/engine/tableWidthUtils.js +0 -25
- package/dist/markdown/headings.d.ts +0 -13
- package/dist/markdown/headings.js +0 -20
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { sanitizeExternalUrl
|
|
1
|
+
import { sanitizeExternalUrl } from "../utils/urlSecurity.js";
|
|
2
|
+
import { RELATIONSHIP_TYPES, resolveRelationshipIdOfType } from "./relsParser.js";
|
|
2
3
|
import { parseRun } from "./runParser.js";
|
|
3
|
-
import { getAttribute, getChildElements, getLocalName, mergeXmlnsDeclarations, parseNumericAttribute } from "./xmlParser.js";
|
|
4
|
+
import { getAttribute, getChildElements, getLocalName, mergeXmlnsDeclarations, parseNumericAttribute, parseOnOffAttribute } from "./xmlParser.js";
|
|
4
5
|
//#region src/docx/hyperlinkParser.ts
|
|
5
6
|
/**
|
|
6
7
|
* Parse bookmark start (w:bookmarkStart)
|
|
@@ -49,11 +50,9 @@ function parseHyperlink(node, rels, styles = null, theme = null, media = null, r
|
|
|
49
50
|
const rId = getAttribute(node, "r", "id");
|
|
50
51
|
if (rId) {
|
|
51
52
|
hyperlink.rId = rId;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
if (sanitizeExternalUrl(rel.target)) hyperlink.href = rel.target;
|
|
56
|
-
}
|
|
53
|
+
const resolved = resolveRelationshipIdOfType(rels, rId, RELATIONSHIP_TYPES.hyperlink);
|
|
54
|
+
if (resolved.status === "resolved") {
|
|
55
|
+
if (sanitizeExternalUrl(resolved.relationship.target)) hyperlink.href = resolved.relationship.target;
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
58
|
const anchor = getAttribute(node, "w", "anchor");
|
|
@@ -64,9 +63,8 @@ function parseHyperlink(node, rels, styles = null, theme = null, media = null, r
|
|
|
64
63
|
const tooltip = getAttribute(node, "w", "tooltip");
|
|
65
64
|
if (tooltip) hyperlink.tooltip = tooltip;
|
|
66
65
|
const tgtFrame = getAttribute(node, "w", "tgtFrame");
|
|
67
|
-
if (tgtFrame) hyperlink.target =
|
|
68
|
-
|
|
69
|
-
if (history === "1" || history === "true") hyperlink.history = true;
|
|
66
|
+
if (tgtFrame) hyperlink.target = tgtFrame;
|
|
67
|
+
if (parseOnOffAttribute(node, "w", "history") === true) hyperlink.history = true;
|
|
70
68
|
const docLocation = getAttribute(node, "w", "docLocation");
|
|
71
69
|
if (docLocation) hyperlink.docLocation = docLocation;
|
|
72
70
|
const inScopeXmlns = mergeXmlnsDeclarations(rootXmlns, node);
|
|
@@ -168,13 +166,11 @@ function getHyperlinkRuns(hyperlink) {
|
|
|
168
166
|
* @returns The resolved URL or undefined
|
|
169
167
|
*/
|
|
170
168
|
function resolveHyperlinkUrl(hyperlink, rels) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return hyperlink.href;
|
|
177
|
-
}
|
|
169
|
+
const resolved = resolveRelationshipIdOfType(rels, hyperlink.rId, RELATIONSHIP_TYPES.hyperlink);
|
|
170
|
+
if (resolved.status === "resolved") {
|
|
171
|
+
if (sanitizeExternalUrl(resolved.relationship.target)) {
|
|
172
|
+
hyperlink.href = resolved.relationship.target;
|
|
173
|
+
return hyperlink.href;
|
|
178
174
|
}
|
|
179
175
|
}
|
|
180
176
|
if (hyperlink.anchor && !hyperlink.href) {
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { XmlElement } from "./xmlParser.js";
|
|
3
3
|
//#region src/docx/imageParser.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The `a:ext` uri under which Word records that a drawing is decorative. The
|
|
6
|
+
* extension holds `<adec:decorative val="…"/>`; `CT_NonVisualDrawingProps` has
|
|
7
|
+
* no `@decorative` attribute for it to be written as.
|
|
8
|
+
*/
|
|
9
|
+
declare const DECORATIVE_EXTENSION_URI = "{C183D7F6-B498-43B3-948B-1728B52AA6E4}";
|
|
10
|
+
/** The namespace the decorative extension's element is bound to. */
|
|
11
|
+
declare const DECORATIVE_NAMESPACE = "http://schemas.microsoft.com/office/drawing/2017/decorative";
|
|
4
12
|
/**
|
|
5
13
|
* Resolve image data from relationships and media map
|
|
6
14
|
*
|
|
@@ -9,7 +17,7 @@ import { XmlElement } from "./xmlParser.js";
|
|
|
9
17
|
* @param media - Media files map
|
|
10
18
|
* @returns Object with src (data URL or blob), mimeType, and filename
|
|
11
19
|
*/
|
|
12
|
-
declare function resolveImageData(rId: string, rels: document_d_exports.RelationshipMap | undefined, media: Map<string, document_d_exports.MediaFile> | undefined): {
|
|
20
|
+
declare function resolveImageData(rId: string | undefined, rels: document_d_exports.RelationshipMap | undefined, media: Map<string, document_d_exports.MediaFile> | undefined): {
|
|
13
21
|
src?: string;
|
|
14
22
|
mimeType?: string;
|
|
15
23
|
filename?: string;
|
|
@@ -89,4 +97,4 @@ declare function getWrapDistancesPx(image: document_d_exports.Image): {
|
|
|
89
97
|
*/
|
|
90
98
|
declare function needsTextWrapping(image: document_d_exports.Image): boolean;
|
|
91
99
|
//#endregion
|
|
92
|
-
export { getImageDimensionsPx, getImageHeightPx, getImageWidthPx, getWrapDistancesPx, hasAltText, isBehindText, isDecorativeImage, isFloatingImage, isInFrontOfText, isInlineImage, needsTextWrapping, parseDrawing, parseImage, resolveImageData };
|
|
100
|
+
export { DECORATIVE_EXTENSION_URI, DECORATIVE_NAMESPACE, getImageDimensionsPx, getImageHeightPx, getImageWidthPx, getWrapDistancesPx, hasAltText, isBehindText, isDecorativeImage, isFloatingImage, isInFrontOfText, isInlineImage, needsTextWrapping, parseDrawing, parseImage, resolveImageData };
|
package/dist/docx/imageParser.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { sanitizeImageSrc } from "../utils/sanitizeImageSrc.js";
|
|
2
2
|
import { emuToPixels } from "../utils/units.js";
|
|
3
3
|
import { sanitizeExternalUrl } from "../utils/urlSecurity.js";
|
|
4
|
-
import { WRAP_ELEMENT_NAMES, parsePositionH, parsePositionV, parseWrapElement } from "./drawingUtils.js";
|
|
4
|
+
import { WRAP_ELEMENT_NAMES, parseAnchorBehindDoc, parsePositionH, parsePositionV, parseWrapElement } from "./drawingUtils.js";
|
|
5
5
|
import { parseGraphicFrameLocks } from "./graphicFrameLocks.js";
|
|
6
|
-
import {
|
|
6
|
+
import { parseNonVisualDrawingNames } from "./nonVisualDrawingProps.js";
|
|
7
|
+
import { RELATIONSHIP_TYPES, resolveRelationshipIdOfType } from "./relsParser.js";
|
|
7
8
|
import { isTextBoxDrawing } from "./textBoxParser.js";
|
|
8
|
-
import {
|
|
9
|
+
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
10
|
+
import { findByFullName, findChild, findChildByNamespaceUri, findChildrenByNamespaceUri, getAttribute, getChildElements, getLocalName, getNamespaceUri, parseNumericAttribute, parseOnOffAttribute, parseOnOffValue } from "./xmlParser.js";
|
|
9
11
|
//#region src/docx/imageParser.ts
|
|
10
12
|
/**
|
|
11
13
|
* Convert rotation value (1/60000 of a degree) to degrees
|
|
@@ -64,6 +66,47 @@ function parseEffectExtent(effectExtent) {
|
|
|
64
66
|
};
|
|
65
67
|
}
|
|
66
68
|
/**
|
|
69
|
+
* The `a:ext` uri under which Word records that a drawing is decorative. The
|
|
70
|
+
* extension holds `<adec:decorative val="…"/>`; `CT_NonVisualDrawingProps` has
|
|
71
|
+
* no `@decorative` attribute for it to be written as.
|
|
72
|
+
*/
|
|
73
|
+
const DECORATIVE_EXTENSION_URI = "{C183D7F6-B498-43B3-948B-1728B52AA6E4}";
|
|
74
|
+
/** The namespace the decorative extension's element is bound to. */
|
|
75
|
+
const DECORATIVE_NAMESPACE = "http://schemas.microsoft.com/office/drawing/2017/decorative";
|
|
76
|
+
/**
|
|
77
|
+
* `wp:docPr`'s extension list is DrawingML's (`a:extLst` holding `a:ext`), in
|
|
78
|
+
* the Transitional or the Strict namespace. Matching on the local name alone
|
|
79
|
+
* would take an `extLst` some other namespace owns and replay its children
|
|
80
|
+
* inside an `a:extLst`, which is a different container than the source wrote.
|
|
81
|
+
*/
|
|
82
|
+
const DRAWINGML_NAMESPACE_URIS = /* @__PURE__ */ new Set(["http://schemas.openxmlformats.org/drawingml/2006/main", "http://purl.oclc.org/ooxml/drawingml/main"]);
|
|
83
|
+
/**
|
|
84
|
+
* Read the `wp:docPr` extension list.
|
|
85
|
+
*
|
|
86
|
+
* Only the decorative extension is modeled. The rest — a creation id, a
|
|
87
|
+
* local-DPI hint, whatever a later Word writes — are captured as they were
|
|
88
|
+
* written so the save path can put them back: an extension folio drops is a
|
|
89
|
+
* fact the document had and no longer does.
|
|
90
|
+
*/
|
|
91
|
+
const parseDocPropsExtensions = (docPr) => {
|
|
92
|
+
const extLst = findChildByNamespaceUri(docPr, DRAWINGML_NAMESPACE_URIS, "extLst");
|
|
93
|
+
if (!extLst) return { other: [] };
|
|
94
|
+
const result = { other: [] };
|
|
95
|
+
for (const ext of findChildrenByNamespaceUri(extLst, DRAWINGML_NAMESPACE_URIS, "ext")) {
|
|
96
|
+
if (getAttribute(ext, null, "uri")?.toUpperCase() !== "{C183D7F6-B498-43B3-948B-1728B52AA6E4}") {
|
|
97
|
+
result.other.push(captureVerbatimXml(ext));
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const flag = getChildElements(ext).find((child) => getLocalName(child.name) === "decorative" && getNamespaceUri(child) === "http://schemas.microsoft.com/office/drawing/2017/decorative");
|
|
101
|
+
if (!flag) {
|
|
102
|
+
result.other.push(captureVerbatimXml(ext));
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
result.decorative = parseOnOffValue(getAttribute(flag, null, "val")) ?? true;
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
67
110
|
* Parse document properties (wp:docPr)
|
|
68
111
|
*
|
|
69
112
|
* @param docPr - wp:docPr element
|
|
@@ -72,18 +115,17 @@ function parseEffectExtent(effectExtent) {
|
|
|
72
115
|
function parseDocProps(docPr) {
|
|
73
116
|
if (!docPr) return {};
|
|
74
117
|
const id = getAttribute(docPr, null, "id");
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const decorative = parseOnOffValue(getAttribute(docPr, null, "decorative")) === true;
|
|
118
|
+
const names = parseNonVisualDrawingNames(docPr);
|
|
119
|
+
const hidden = parseOnOffAttribute(docPr, null, "hidden");
|
|
120
|
+
const { decorative, other: docPrExtensions } = parseDocPropsExtensions(docPr);
|
|
79
121
|
const hlinkClickEl = findChild(docPr, "a", "hlinkClick");
|
|
80
122
|
const hlinkRId = hlinkClickEl ? getAttribute(hlinkClickEl, "r", "id") : null;
|
|
81
123
|
return {
|
|
82
124
|
...id != null ? { id } : {},
|
|
83
|
-
...
|
|
84
|
-
...
|
|
85
|
-
...
|
|
86
|
-
...
|
|
125
|
+
...names,
|
|
126
|
+
...decorative === void 0 ? {} : { decorative },
|
|
127
|
+
...hidden === void 0 ? {} : { hidden },
|
|
128
|
+
...docPrExtensions.length > 0 ? { docPrExtensions } : {},
|
|
87
129
|
...hlinkRId != null ? { hlinkRId } : {}
|
|
88
130
|
};
|
|
89
131
|
}
|
|
@@ -178,17 +220,20 @@ function parseImageOpacity(blip) {
|
|
|
178
220
|
return Math.max(0, amt / 1e5);
|
|
179
221
|
}
|
|
180
222
|
/**
|
|
181
|
-
* Extract rId from a:blip element
|
|
223
|
+
* Extract rId from a:blip element.
|
|
224
|
+
*
|
|
225
|
+
* Undefined when the drawing has no blip to read one from: a chart, a diagram
|
|
226
|
+
* or an OLE frame carries an `a:graphic` that is not a picture, and a
|
|
227
|
+
* `wp:inline` may carry no graphic at all.
|
|
182
228
|
*/
|
|
183
229
|
function extractBlipRId(blip) {
|
|
184
|
-
if (!blip) return
|
|
230
|
+
if (!blip) return;
|
|
185
231
|
const rEmbed = getAttribute(blip, "r", "embed");
|
|
186
232
|
if (rEmbed) return rEmbed;
|
|
187
233
|
const embed = getAttribute(blip, null, "embed");
|
|
188
234
|
if (embed) return embed;
|
|
189
235
|
const rLink = getAttribute(blip, "r", "link");
|
|
190
236
|
if (rLink) return rLink;
|
|
191
|
-
return "";
|
|
192
237
|
}
|
|
193
238
|
/**
|
|
194
239
|
* Find transform (a:xfrm) from picture shape properties
|
|
@@ -242,10 +287,9 @@ function getMimeType(path) {
|
|
|
242
287
|
* @returns Object with src (data URL or blob), mimeType, and filename
|
|
243
288
|
*/
|
|
244
289
|
function resolveImageData(rId, rels, media) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const targetPath = rel.target;
|
|
290
|
+
const resolved = resolveRelationshipIdOfType(rels, rId, RELATIONSHIP_TYPES.image);
|
|
291
|
+
if (resolved.status !== "resolved") return {};
|
|
292
|
+
const targetPath = resolved.relationship.target;
|
|
249
293
|
if (!targetPath) return {};
|
|
250
294
|
const normalizedPath = normalizeMediaPath(targetPath);
|
|
251
295
|
const filename = targetPath.split("/").pop();
|
|
@@ -319,7 +363,7 @@ function parseInline(inlineEl, rels, media) {
|
|
|
319
363
|
if (distR !== void 0) wrap.distR = distR;
|
|
320
364
|
const image = {
|
|
321
365
|
type: "image",
|
|
322
|
-
rId,
|
|
366
|
+
...rId === void 0 ? {} : { rId },
|
|
323
367
|
size,
|
|
324
368
|
wrap
|
|
325
369
|
};
|
|
@@ -327,7 +371,9 @@ function parseInline(inlineEl, rels, media) {
|
|
|
327
371
|
if (props.name !== void 0) image.docPrName = props.name;
|
|
328
372
|
if (props.alt !== void 0) image.alt = props.alt;
|
|
329
373
|
if (props.title !== void 0) image.title = props.title;
|
|
330
|
-
if (props.decorative) image.decorative =
|
|
374
|
+
if (props.decorative !== void 0) image.decorative = props.decorative;
|
|
375
|
+
if (props.hidden !== void 0) image.hidden = props.hidden;
|
|
376
|
+
if (props.docPrExtensions !== void 0) image.docPrExtensions = props.docPrExtensions;
|
|
331
377
|
const safeSrc = sanitizeImageSrc(imageData.src);
|
|
332
378
|
if (safeSrc) image.src = safeSrc;
|
|
333
379
|
if (imageData.mimeType) image.mimeType = imageData.mimeType;
|
|
@@ -337,11 +383,12 @@ function parseInline(inlineEl, rels, media) {
|
|
|
337
383
|
if (crop) image.crop = crop;
|
|
338
384
|
if (opacity !== void 0) image.opacity = opacity;
|
|
339
385
|
if (frameLocks) image.frameLocks = frameLocks;
|
|
340
|
-
|
|
341
|
-
|
|
386
|
+
const hlink = resolveRelationshipIdOfType(rels, props.hlinkRId, RELATIONSHIP_TYPES.hyperlink);
|
|
387
|
+
if (hlink.status === "resolved") {
|
|
388
|
+
const safeHref = sanitizeExternalUrl(hlink.relationship.target);
|
|
342
389
|
if (safeHref) {
|
|
343
390
|
image.hlinkHref = safeHref;
|
|
344
|
-
image.hlinkRId =
|
|
391
|
+
image.hlinkRId = hlink.relationship.id;
|
|
345
392
|
}
|
|
346
393
|
}
|
|
347
394
|
return image;
|
|
@@ -359,7 +406,7 @@ function parseAnchor(anchorEl, rels, media) {
|
|
|
359
406
|
const padding = parseEffectExtent(findByFullName(anchorEl, "wp:effectExtent"));
|
|
360
407
|
const props = parseDocProps(findByFullName(anchorEl, "wp:docPr"));
|
|
361
408
|
const frameLocks = parseGraphicFrameLocks(anchorEl);
|
|
362
|
-
const behindDoc =
|
|
409
|
+
const behindDoc = parseAnchorBehindDoc(anchorEl);
|
|
363
410
|
const layoutInCell = parseOnOffAttr(anchorEl, "layoutInCell");
|
|
364
411
|
const allowOverlap = parseOnOffAttr(anchorEl, "allowOverlap");
|
|
365
412
|
const anchorDistT = parseNumericAttribute(anchorEl, null, "distT");
|
|
@@ -391,7 +438,7 @@ function parseAnchor(anchorEl, rels, media) {
|
|
|
391
438
|
const transform = parseTransform(findPictureTransform(anchorEl));
|
|
392
439
|
const image = {
|
|
393
440
|
type: "image",
|
|
394
|
-
rId,
|
|
441
|
+
...rId === void 0 ? {} : { rId },
|
|
395
442
|
size,
|
|
396
443
|
wrap
|
|
397
444
|
};
|
|
@@ -399,7 +446,9 @@ function parseAnchor(anchorEl, rels, media) {
|
|
|
399
446
|
if (props.name !== void 0) image.docPrName = props.name;
|
|
400
447
|
if (props.alt !== void 0) image.alt = props.alt;
|
|
401
448
|
if (props.title !== void 0) image.title = props.title;
|
|
402
|
-
if (props.decorative) image.decorative =
|
|
449
|
+
if (props.decorative !== void 0) image.decorative = props.decorative;
|
|
450
|
+
if (props.hidden !== void 0) image.hidden = props.hidden;
|
|
451
|
+
if (props.docPrExtensions !== void 0) image.docPrExtensions = props.docPrExtensions;
|
|
403
452
|
const safeSrc = sanitizeImageSrc(imageData.src);
|
|
404
453
|
if (safeSrc) image.src = safeSrc;
|
|
405
454
|
if (imageData.mimeType) image.mimeType = imageData.mimeType;
|
|
@@ -412,11 +461,12 @@ function parseAnchor(anchorEl, rels, media) {
|
|
|
412
461
|
if (frameLocks) image.frameLocks = frameLocks;
|
|
413
462
|
if (layoutInCell !== void 0) image.layoutInCell = layoutInCell;
|
|
414
463
|
if (allowOverlap !== void 0) image.allowOverlap = allowOverlap;
|
|
415
|
-
|
|
416
|
-
|
|
464
|
+
const hlink = resolveRelationshipIdOfType(rels, props.hlinkRId, RELATIONSHIP_TYPES.hyperlink);
|
|
465
|
+
if (hlink.status === "resolved") {
|
|
466
|
+
const safeHref = sanitizeExternalUrl(hlink.relationship.target);
|
|
417
467
|
if (safeHref) {
|
|
418
468
|
image.hlinkHref = safeHref;
|
|
419
|
-
image.hlinkRId =
|
|
469
|
+
image.hlinkRId = hlink.relationship.id;
|
|
420
470
|
}
|
|
421
471
|
}
|
|
422
472
|
return image;
|
|
@@ -533,4 +583,4 @@ function needsTextWrapping(image) {
|
|
|
533
583
|
].includes(image.wrap.type);
|
|
534
584
|
}
|
|
535
585
|
//#endregion
|
|
536
|
-
export { getImageDimensionsPx, getImageHeightPx, getImageWidthPx, getWrapDistancesPx, hasAltText, isBehindText, isDecorativeImage, isFloatingImage, isInFrontOfText, isInlineImage, needsTextWrapping, parseDrawing, parseImage, resolveImageData };
|
|
586
|
+
export { DECORATIVE_EXTENSION_URI, DECORATIVE_NAMESPACE, getImageDimensionsPx, getImageHeightPx, getImageWidthPx, getWrapDistancesPx, hasAltText, isBehindText, isDecorativeImage, isFloatingImage, isInFrontOfText, isInlineImage, needsTextWrapping, parseDrawing, parseImage, resolveImageData };
|
|
@@ -12,7 +12,20 @@ declare const isDrawingRawXmlMode: (value: unknown) => value is document_d_expor
|
|
|
12
12
|
declare const EDITED_PREVIEW_FINGERPRINT = "editedPreview";
|
|
13
13
|
/** Fingerprints modeled image fields which make raw DrawingML stale when edited. */
|
|
14
14
|
declare const imageRawXmlFingerprint: (image: document_d_exports.Image) => string;
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Whether the serializer writes the captured raw XML back.
|
|
17
|
+
*
|
|
18
|
+
* For an unclassified drawing the capture is a cache of the model, so it
|
|
19
|
+
* replays only while the two agree. For a classified one the capture *is* the
|
|
20
|
+
* drawing: a preserve-only drawing has a placeholder image, and a preview-only
|
|
21
|
+
* group has a raster render of shapes the model cannot hold. Regenerating
|
|
22
|
+
* either writes something the source never contained — for the group, at best
|
|
23
|
+
* the one child picture the rasterizer saw first under that child's
|
|
24
|
+
* relationship, in place of the whole group. So a classified capture is always
|
|
25
|
+
* written back; a stale fingerprint means the edit that made it stale is lost,
|
|
26
|
+
* which {@link classifyDrawingSafety} reports as `opaque`, and never that
|
|
27
|
+
* folio may build a replacement.
|
|
28
|
+
*/
|
|
16
29
|
declare const canReplayEditableImageRawXml: (drawing: document_d_exports.DrawingContent) => boolean;
|
|
17
30
|
/**
|
|
18
31
|
* What a drawing costs the document when Folio writes it back.
|
package/dist/docx/imageRawXml.js
CHANGED
|
@@ -26,10 +26,29 @@ const EDITED_PREVIEW_FINGERPRINT = "editedPreview";
|
|
|
26
26
|
const editableImageProjection = ({ id: _id, rId: _rId, src: _src, mimeType: _mimeType, filename: _filename, ...image }) => image;
|
|
27
27
|
/** Fingerprints modeled image fields which make raw DrawingML stale when edited. */
|
|
28
28
|
const imageRawXmlFingerprint = (image) => canonicalJson(editableImageProjection(image));
|
|
29
|
-
/**
|
|
29
|
+
/** Whether the modeled image still says what the captured raw drawing says. */
|
|
30
|
+
const modelAgreesWithRawXml = (drawing) => drawing.rawImageFingerprint === void 0 || drawing.rawImageFingerprint === imageRawXmlFingerprint(drawing.image);
|
|
31
|
+
/**
|
|
32
|
+
* Whether the serializer writes the captured raw XML back.
|
|
33
|
+
*
|
|
34
|
+
* For an unclassified drawing the capture is a cache of the model, so it
|
|
35
|
+
* replays only while the two agree. For a classified one the capture *is* the
|
|
36
|
+
* drawing: a preserve-only drawing has a placeholder image, and a preview-only
|
|
37
|
+
* group has a raster render of shapes the model cannot hold. Regenerating
|
|
38
|
+
* either writes something the source never contained — for the group, at best
|
|
39
|
+
* the one child picture the rasterizer saw first under that child's
|
|
40
|
+
* relationship, in place of the whole group. So a classified capture is always
|
|
41
|
+
* written back; a stale fingerprint means the edit that made it stale is lost,
|
|
42
|
+
* which {@link classifyDrawingSafety} reports as `opaque`, and never that
|
|
43
|
+
* folio may build a replacement.
|
|
44
|
+
*/
|
|
30
45
|
const canReplayEditableImageRawXml = (drawing) => {
|
|
31
|
-
|
|
32
|
-
|
|
46
|
+
switch (drawing.rawXmlMode) {
|
|
47
|
+
case DRAWING_RAW_XML_MODES.PRESERVE_ONLY:
|
|
48
|
+
case DRAWING_RAW_XML_MODES.PREVIEW_ONLY: return true;
|
|
49
|
+
case void 0: return modelAgreesWithRawXml(drawing);
|
|
50
|
+
default: return drawing;
|
|
51
|
+
}
|
|
33
52
|
};
|
|
34
53
|
/**
|
|
35
54
|
* What a drawing costs the document when Folio writes it back.
|
|
@@ -46,12 +65,12 @@ const DRAWING_SAFETY_CLASSES = {
|
|
|
46
65
|
OPAQUE: "opaque"
|
|
47
66
|
};
|
|
48
67
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
68
|
+
* A drawing with no picture relationship has no picture to regenerate. The
|
|
69
|
+
* serializer writes the anchor back without a graphic, which is faithful for an
|
|
70
|
+
* anchor that never had one and lossy for a chart or an OLE frame, so the
|
|
71
|
+
* drawing counts as opaque and an edit must block the save.
|
|
53
72
|
*/
|
|
54
|
-
const canRegenerateDrawing = (drawing) => drawing.image.rId !==
|
|
73
|
+
const canRegenerateDrawing = (drawing) => drawing.image.rId !== void 0;
|
|
55
74
|
/**
|
|
56
75
|
* Classify a drawing by what the run serializer will do with it.
|
|
57
76
|
*
|
|
@@ -60,9 +79,14 @@ const canRegenerateDrawing = (drawing) => drawing.image.rId !== "";
|
|
|
60
79
|
*/
|
|
61
80
|
const classifyDrawingSafety = (drawing) => {
|
|
62
81
|
if (drawing.rawXml === void 0) return DRAWING_SAFETY_CLASSES.NATIVE;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
82
|
+
switch (drawing.rawXmlMode) {
|
|
83
|
+
case DRAWING_RAW_XML_MODES.PRESERVE_ONLY: return DRAWING_SAFETY_CLASSES.REPLAYABLE;
|
|
84
|
+
case DRAWING_RAW_XML_MODES.PREVIEW_ONLY: return modelAgreesWithRawXml(drawing) ? DRAWING_SAFETY_CLASSES.REPLAYABLE : DRAWING_SAFETY_CLASSES.OPAQUE;
|
|
85
|
+
case void 0:
|
|
86
|
+
if (modelAgreesWithRawXml(drawing)) return DRAWING_SAFETY_CLASSES.REPLAYABLE;
|
|
87
|
+
return canRegenerateDrawing(drawing) ? DRAWING_SAFETY_CLASSES.NATIVE : DRAWING_SAFETY_CLASSES.OPAQUE;
|
|
88
|
+
default: return drawing;
|
|
89
|
+
}
|
|
66
90
|
};
|
|
67
91
|
//#endregion
|
|
68
92
|
export { DRAWING_SAFETY_CLASSES, EDITED_PREVIEW_FINGERPRINT, allowsDirectDrawingEdit, canReplayEditableImageRawXml, classifyDrawingSafety, imageRawXmlFingerprint, isDrawingRawXmlMode };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { XmlElement } from "./xmlParser.js";
|
|
3
|
+
//#region src/docx/markupRangeMarker.d.ts
|
|
4
|
+
declare const parseMarkupRangeMarker: (node: XmlElement) => document_d_exports.MarkupRangeMarker;
|
|
5
|
+
declare const parseBookmarkRangeMarker: (node: XmlElement) => document_d_exports.BookmarkRangeMarker;
|
|
6
|
+
/**
|
|
7
|
+
* `w:author` is required on `CT_MoveBookmark`, so an absent one takes the same
|
|
8
|
+
* `Unknown` fallback a tracked change takes rather than leaving the saved
|
|
9
|
+
* package short of an attribute the schema demands. `w:date` is required too,
|
|
10
|
+
* but inventing a timestamp would state a fact about the document that is not
|
|
11
|
+
* true, so an absent date stays absent.
|
|
12
|
+
*/
|
|
13
|
+
declare const parseMoveBookmarkMarker: (node: XmlElement) => document_d_exports.MoveBookmarkMarker;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { parseBookmarkRangeMarker, parseMarkupRangeMarker, parseMoveBookmarkMarker };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getAttribute, parseNumericAttribute } from "./xmlParser.js";
|
|
2
|
+
//#region src/docx/markupRangeMarker.ts
|
|
3
|
+
/** ST_DisplacedByCustomXml. A value outside it is not a placement folio can honour. */
|
|
4
|
+
const DISPLACED_BY_CUSTOM_XML = /* @__PURE__ */ new Set(["next", "prev"]);
|
|
5
|
+
const parseDisplacedByCustomXml = (node) => {
|
|
6
|
+
const value = getAttribute(node, "w", "displacedByCustomXml") ?? "";
|
|
7
|
+
return DISPLACED_BY_CUSTOM_XML.has(value) ? value : void 0;
|
|
8
|
+
};
|
|
9
|
+
const parseMarkupRangeMarker = (node) => {
|
|
10
|
+
const marker = { id: parseNumericAttribute(node, "w", "id") ?? 0 };
|
|
11
|
+
const displacedByCustomXml = parseDisplacedByCustomXml(node);
|
|
12
|
+
if (displacedByCustomXml !== void 0) marker.displacedByCustomXml = displacedByCustomXml;
|
|
13
|
+
return marker;
|
|
14
|
+
};
|
|
15
|
+
const parseBookmarkRangeMarker = (node) => {
|
|
16
|
+
const marker = {
|
|
17
|
+
...parseMarkupRangeMarker(node),
|
|
18
|
+
name: getAttribute(node, "w", "name") ?? ""
|
|
19
|
+
};
|
|
20
|
+
const colFirst = parseNumericAttribute(node, "w", "colFirst");
|
|
21
|
+
if (colFirst !== void 0) marker.colFirst = colFirst;
|
|
22
|
+
const colLast = parseNumericAttribute(node, "w", "colLast");
|
|
23
|
+
if (colLast !== void 0) marker.colLast = colLast;
|
|
24
|
+
return marker;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* `w:author` is required on `CT_MoveBookmark`, so an absent one takes the same
|
|
28
|
+
* `Unknown` fallback a tracked change takes rather than leaving the saved
|
|
29
|
+
* package short of an attribute the schema demands. `w:date` is required too,
|
|
30
|
+
* but inventing a timestamp would state a fact about the document that is not
|
|
31
|
+
* true, so an absent date stays absent.
|
|
32
|
+
*/
|
|
33
|
+
const parseMoveBookmarkMarker = (node) => {
|
|
34
|
+
const author = (getAttribute(node, "w", "author") ?? "").trim();
|
|
35
|
+
const marker = {
|
|
36
|
+
...parseBookmarkRangeMarker(node),
|
|
37
|
+
author: author.length > 0 ? author : "Unknown"
|
|
38
|
+
};
|
|
39
|
+
const date = (getAttribute(node, "w", "date") ?? "").trim();
|
|
40
|
+
if (date.length > 0) marker.date = date;
|
|
41
|
+
return marker;
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
export { parseBookmarkRangeMarker, parseMarkupRangeMarker, parseMoveBookmarkMarker };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { findChildByLocalName, getLocalName } from "./xmlParser.js";
|
|
2
|
+
import { escapeXmlText } from "@stll/docx-core";
|
|
2
3
|
//#region src/docx/mathToMathml.ts
|
|
3
4
|
/**
|
|
4
5
|
* OMML → MathML conversion.
|
|
@@ -95,13 +96,13 @@ const MATH_OPERATOR_HINT = /* @__PURE__ */ new Set([
|
|
|
95
96
|
"…"
|
|
96
97
|
]);
|
|
97
98
|
function renderElement(el) {
|
|
98
|
-
if (el.type === "text") return
|
|
99
|
+
if (el.type === "text") return escapeXmlText(textValue(el));
|
|
99
100
|
if (el.type !== "element") return "";
|
|
100
101
|
switch (el.name ? getLocalName(el.name) : "") {
|
|
101
102
|
case "oMath":
|
|
102
103
|
case "oMathPara": return renderChildren(el);
|
|
103
104
|
case "r": return renderRun(el);
|
|
104
|
-
case "t": return
|
|
105
|
+
case "t": return escapeXmlText(extractText(el));
|
|
105
106
|
case "f": return renderFraction(el);
|
|
106
107
|
case "num":
|
|
107
108
|
case "den": return wrapMrow(renderChildren(el));
|
|
@@ -179,8 +180,8 @@ function tokenizeMathText(text) {
|
|
|
179
180
|
let bufKind = null;
|
|
180
181
|
const flush = () => {
|
|
181
182
|
if (!buf) return;
|
|
182
|
-
if (bufKind === "digit") out += `<mn>${
|
|
183
|
-
else out += `<mi>${
|
|
183
|
+
if (bufKind === "digit") out += `<mn>${escapeXmlText(buf)}</mn>`;
|
|
184
|
+
else out += `<mi>${escapeXmlText(buf)}</mi>`;
|
|
184
185
|
buf = "";
|
|
185
186
|
bufKind = null;
|
|
186
187
|
};
|
|
@@ -193,7 +194,7 @@ function tokenizeMathText(text) {
|
|
|
193
194
|
const kind = classifyChar(ch);
|
|
194
195
|
if (kind === "operator") {
|
|
195
196
|
flush();
|
|
196
|
-
out += `<mo>${
|
|
197
|
+
out += `<mo>${escapeXmlText(ch)}</mo>`;
|
|
197
198
|
continue;
|
|
198
199
|
}
|
|
199
200
|
if (kind === "digit") {
|
|
@@ -202,7 +203,7 @@ function tokenizeMathText(text) {
|
|
|
202
203
|
continue;
|
|
203
204
|
}
|
|
204
205
|
flush();
|
|
205
|
-
out += `<mi>${
|
|
206
|
+
out += `<mi>${escapeXmlText(ch)}</mi>`;
|
|
206
207
|
}
|
|
207
208
|
flush();
|
|
208
209
|
return out;
|
|
@@ -258,7 +259,7 @@ function renderNary(el) {
|
|
|
258
259
|
const chr = (chrEl ? getAttrValue(chrEl, "val") : null) || "∫";
|
|
259
260
|
const limLocEl = naryPr ? findChildByLocalName(naryPr, "limLoc") : null;
|
|
260
261
|
const limLoc = (limLocEl ? getAttrValue(limLocEl, "val") : null) || "subSup";
|
|
261
|
-
const opMml = `<mo>${
|
|
262
|
+
const opMml = `<mo>${escapeXmlText(chr)}</mo>`;
|
|
262
263
|
const subMml = sub ? wrapMrow(renderChildren(sub)) : "";
|
|
263
264
|
const supMml = sup ? wrapMrow(renderChildren(sup)) : "";
|
|
264
265
|
const bodyMml = body ? wrapMrow(renderChildren(body)) : "<mrow/>";
|
|
@@ -277,8 +278,8 @@ function renderDelimiter(el) {
|
|
|
277
278
|
const begChr = begChrEl ? getAttrValue(begChrEl, "val") ?? "(" : "(";
|
|
278
279
|
const endChr = endChrEl ? getAttrValue(endChrEl, "val") ?? ")" : ")";
|
|
279
280
|
const sepChr = sepChrEl ? getAttrValue(sepChrEl, "val") ?? "|" : "|";
|
|
280
|
-
const inner = (el.elements ?? []).filter((c) => c.type === "element" && getLocalName(c.name ?? "") === "e").map((c) => wrapMrow(renderChildren(c))).join(`<mo>${
|
|
281
|
-
return `<mrow><mo>${
|
|
281
|
+
const inner = (el.elements ?? []).filter((c) => c.type === "element" && getLocalName(c.name ?? "") === "e").map((c) => wrapMrow(renderChildren(c))).join(`<mo>${escapeXmlText(sepChr)}</mo>`);
|
|
282
|
+
return `<mrow><mo>${escapeXmlText(begChr)}</mo>${inner}<mo>${escapeXmlText(endChr)}</mo></mrow>`;
|
|
282
283
|
}
|
|
283
284
|
function renderMatrix(el) {
|
|
284
285
|
return `<mtable>${(el.elements ?? []).filter((c) => c.type === "element" && getLocalName(c.name ?? "") === "mr").map(renderMatrixRow).join("")}</mtable>`;
|
|
@@ -291,7 +292,7 @@ function renderAccent(el) {
|
|
|
291
292
|
const chrEl = accPr ? findChildByLocalName(accPr, "chr") : null;
|
|
292
293
|
const chr = chrEl ? getAttrValue(chrEl, "val") ?? "̂" : "̂";
|
|
293
294
|
const base = findChildByLocalName(el, "e");
|
|
294
|
-
return `<mover accent="true">${base ? wrapMrow(renderChildren(base)) : "<mrow/>"}<mo>${
|
|
295
|
+
return `<mover accent="true">${base ? wrapMrow(renderChildren(base)) : "<mrow/>"}<mo>${escapeXmlText(chr)}</mo></mover>`;
|
|
295
296
|
}
|
|
296
297
|
function renderBar(el) {
|
|
297
298
|
const barPr = findChildByLocalName(el, "barPr");
|
|
@@ -311,7 +312,7 @@ function renderGroupChr(el) {
|
|
|
311
312
|
const chr = chrEl ? getAttrValue(chrEl, "val") ?? defaultChr : defaultChr;
|
|
312
313
|
const base = findChildByLocalName(el, "e");
|
|
313
314
|
const baseMml = base ? wrapMrow(renderChildren(base)) : "<mrow/>";
|
|
314
|
-
const grouper = `<mo stretchy="true">${
|
|
315
|
+
const grouper = `<mo stretchy="true">${escapeXmlText(chr)}</mo>`;
|
|
315
316
|
return pos === "top" ? `<mover>${baseMml}${grouper}</mover>` : `<munder>${baseMml}${grouper}</munder>`;
|
|
316
317
|
}
|
|
317
318
|
function renderLimLow(el) {
|
|
@@ -350,8 +351,5 @@ function getAttrValue(el, localAttr) {
|
|
|
350
351
|
return String(v);
|
|
351
352
|
}
|
|
352
353
|
}
|
|
353
|
-
function escapeXml(s) {
|
|
354
|
-
return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
355
|
-
}
|
|
356
354
|
//#endregion
|
|
357
355
|
export { ommlToMathml };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { XmlElement } from "./xmlParser.js";
|
|
2
|
+
//#region src/docx/nonVisualDrawingProps.d.ts
|
|
3
|
+
/** The authored name, alt text and title of a drawing object. */
|
|
4
|
+
type NonVisualDrawingNames = {
|
|
5
|
+
/** `@name`, schema-required, so `""` means the object was never named. */
|
|
6
|
+
name?: string;
|
|
7
|
+
/** `@descr`: alt text, accessibility content. */
|
|
8
|
+
alt?: string;
|
|
9
|
+
/** `@title`. */
|
|
10
|
+
title?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Read `@name`, `@descr` and `@title` off a `CT_NonVisualDrawingProps` element.
|
|
14
|
+
*
|
|
15
|
+
* An attribute that is not there stays absent. `@name` is schema-required, so
|
|
16
|
+
* an unnamed object still writes one, and `name=""` is the marker the writer
|
|
17
|
+
* below mints for exactly that: reading it back as an authored name would make
|
|
18
|
+
* `absent → save → parse` land on `""` instead of absent, and no reader can
|
|
19
|
+
* tell the two apart. `@descr` and `@title` are optional and written only when
|
|
20
|
+
* authored, so `""` in either is a string someone wrote.
|
|
21
|
+
*/
|
|
22
|
+
declare const parseNonVisualDrawingNames: (element: XmlElement | null | undefined) => NonVisualDrawingNames;
|
|
23
|
+
/**
|
|
24
|
+
* The `name`, `descr` and `title` attributes of a `CT_NonVisualDrawingProps`
|
|
25
|
+
* element, leading space included.
|
|
26
|
+
*
|
|
27
|
+
* `@name` is schema-required, so an object carrying none writes the empty
|
|
28
|
+
* string. folio does not name an object it did not author: a generated
|
|
29
|
+
* "Shape 3" would overwrite the author's own name for every drawing whose
|
|
30
|
+
* name did not survive the model, and read back as authored content.
|
|
31
|
+
*/
|
|
32
|
+
declare const serializeNonVisualDrawingNames: (names: NonVisualDrawingNames) => string;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { NonVisualDrawingNames, parseNonVisualDrawingNames, serializeNonVisualDrawingNames };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { getAttribute } from "./xmlParser.js";
|
|
2
|
+
import { escapeXmlAttribute } from "@stll/docx-core";
|
|
3
|
+
//#region src/docx/nonVisualDrawingProps.ts
|
|
4
|
+
/**
|
|
5
|
+
* `CT_NonVisualDrawingProps` (`wp:docPr`, `wps:cNvPr`, `pic:cNvPr`) carries the
|
|
6
|
+
* three authored strings every drawing object has: its name, its alt text
|
|
7
|
+
* (`descr`) and its title. They are authored content, not folio's to mint, so
|
|
8
|
+
* one reader and one writer own them for every drawing kind.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Read `@name`, `@descr` and `@title` off a `CT_NonVisualDrawingProps` element.
|
|
12
|
+
*
|
|
13
|
+
* An attribute that is not there stays absent. `@name` is schema-required, so
|
|
14
|
+
* an unnamed object still writes one, and `name=""` is the marker the writer
|
|
15
|
+
* below mints for exactly that: reading it back as an authored name would make
|
|
16
|
+
* `absent → save → parse` land on `""` instead of absent, and no reader can
|
|
17
|
+
* tell the two apart. `@descr` and `@title` are optional and written only when
|
|
18
|
+
* authored, so `""` in either is a string someone wrote.
|
|
19
|
+
*/
|
|
20
|
+
const parseNonVisualDrawingNames = (element) => {
|
|
21
|
+
if (!element) return {};
|
|
22
|
+
const name = getAttribute(element, null, "name");
|
|
23
|
+
const descr = getAttribute(element, null, "descr");
|
|
24
|
+
const title = getAttribute(element, null, "title");
|
|
25
|
+
return {
|
|
26
|
+
...name !== null && name !== "" ? { name } : {},
|
|
27
|
+
...descr !== null ? { alt: descr } : {},
|
|
28
|
+
...title !== null ? { title } : {}
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The `name`, `descr` and `title` attributes of a `CT_NonVisualDrawingProps`
|
|
33
|
+
* element, leading space included.
|
|
34
|
+
*
|
|
35
|
+
* `@name` is schema-required, so an object carrying none writes the empty
|
|
36
|
+
* string. folio does not name an object it did not author: a generated
|
|
37
|
+
* "Shape 3" would overwrite the author's own name for every drawing whose
|
|
38
|
+
* name did not survive the model, and read back as authored content.
|
|
39
|
+
*/
|
|
40
|
+
const serializeNonVisualDrawingNames = (names) => {
|
|
41
|
+
const descr = names.alt === void 0 ? "" : ` descr="${escapeXmlAttribute(names.alt)}"`;
|
|
42
|
+
const title = names.title === void 0 ? "" : ` title="${escapeXmlAttribute(names.title)}"`;
|
|
43
|
+
return ` name="${escapeXmlAttribute(names.name ?? "")}"${descr}${title}`;
|
|
44
|
+
};
|
|
45
|
+
//#endregion
|
|
46
|
+
export { parseNonVisualDrawingNames, serializeNonVisualDrawingNames };
|