@stll/folio-core 0.28.1 → 0.30.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/controller/folioEditor.d.ts +28 -1
- package/dist/controller/folioEditor.js +37 -26
- package/dist/controller/fontReadiness.js +28 -24
- package/dist/controller/hiddenEditorManager.d.ts +2 -7
- package/dist/controller/layoutPipeline.js +33 -3
- package/dist/controller/noteEditorManager.d.ts +3 -6
- package/dist/docx/metafileSvg.d.ts +12 -0
- package/dist/docx/metafileSvg.js +462 -0
- package/dist/docx/paragraphTextBoxEnrichment.js +55 -4
- package/dist/docx/parser.d.ts +2 -2
- package/dist/docx/parser.js +26 -0
- package/dist/docx/runParser.js +2 -1
- package/dist/docx/serializer/runSerializer.js +14 -2
- package/dist/docx/styleParser.js +2 -1
- package/dist/docx/textBoxParser.js +14 -0
- package/dist/docx/vmlImageParser.js +72 -152
- package/dist/docx/vmlPreview.d.ts +31 -0
- package/dist/docx/vmlPreview.js +535 -0
- package/dist/docx/wrapTypes.d.ts +2 -36
- package/dist/docx/wrapTypes.js +1 -39
- package/dist/fonts/fontAlternates.d.ts +8 -0
- package/dist/fonts/fontAlternates.js +16 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +34 -3
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +5 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +136 -120
- package/dist/layout-bridge/dom/noteStoryDom.d.ts +1 -1
- package/dist/layout-bridge/engine/selectionRects.js +3 -1
- package/dist/layout-engine/index.js +65 -32
- package/dist/layout-engine/justifiedLineFit.d.ts +7 -0
- package/dist/layout-engine/justifiedLineFit.js +6 -0
- package/dist/layout-engine/keep-together.js +15 -10
- package/dist/layout-engine/measure/complexScriptFormatting.d.ts +6 -1
- package/dist/layout-engine/measure/complexScriptFormatting.js +11 -2
- package/dist/layout-engine/measure/floatingTablePosition.d.ts +16 -1
- package/dist/layout-engine/measure/floatingTablePosition.js +29 -6
- package/dist/layout-engine/measure/lineBreakProvider.js +67 -8
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +1 -0
- package/dist/layout-engine/measure/listMarkerWidth.js +23 -10
- package/dist/layout-engine/measure/measureBlocks.d.ts +15 -0
- package/dist/layout-engine/measure/measureBlocks.js +197 -30
- package/dist/layout-engine/measure/measureContainer.js +20 -24
- package/dist/layout-engine/measure/measureHelpers.d.ts +1 -1
- package/dist/layout-engine/measure/measureHelpers.js +14 -8
- package/dist/layout-engine/measure/measureParagraph.js +111 -37
- package/dist/layout-engine/measure/measureTypes.d.ts +3 -0
- package/dist/layout-engine/measure/tabCalculator.d.ts +123 -0
- package/dist/layout-engine/measure/tabCalculator.js +144 -0
- package/dist/layout-engine/measure/tableCellFlow.d.ts +21 -1
- package/dist/layout-engine/measure/tableCellFlow.js +45 -8
- package/dist/layout-engine/measure/tableInlinePlacement.d.ts +10 -2
- package/dist/layout-engine/measure/tableInlinePlacement.js +17 -15
- package/dist/layout-engine/paginator.d.ts +26 -2
- package/dist/layout-engine/paginator.js +76 -14
- package/dist/layout-engine/paragraphSpacing.d.ts +3 -1
- package/dist/layout-engine/paragraphSpacing.js +4 -3
- package/dist/layout-engine/renderedBreakReconciliation.d.ts +14 -3
- package/dist/layout-engine/renderedBreakReconciliation.js +45 -14
- package/dist/layout-engine/section-breaks.d.ts +3 -1
- package/dist/layout-engine/section-breaks.js +6 -2
- package/dist/layout-engine/types.d.ts +18 -1
- package/dist/layout-engine/types.js +1 -1
- package/dist/layout-painter/imageLayout.d.ts +1 -1
- package/dist/layout-painter/renderPage.js +6 -1
- package/dist/layout-painter/renderParagraph.js +169 -82
- package/dist/layout-painter/renderTable.d.ts +1 -1
- package/dist/layout-painter/renderTable.js +18 -7
- package/dist/layout-painter/renderTextBox.d.ts +1 -1
- package/dist/layout-painter/renderTextBox.js +6 -1
- package/dist/markdown/renderParagraph.js +7 -2
- package/dist/model.d.ts +3 -3
- package/dist/model.js +2 -2
- package/dist/paged-layout/sectionBlockWidths.d.ts +14 -0
- package/dist/paged-layout/sectionBlockWidths.js +103 -14
- package/dist/prosemirror/attrs/index.js +14 -2
- package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +8 -0
- package/dist/prosemirror/bookmarkBoundaryAttrs.js +66 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +176 -54
- package/dist/prosemirror/conversion/toProseDoc.js +179 -64
- package/dist/prosemirror/extensions/StarterKit.js +11 -3
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +8 -4
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.d.ts +4 -1
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +9 -5
- package/dist/prosemirror/extensions/features/pasteCleanup.d.ts +10 -23
- package/dist/prosemirror/extensions/features/pasteCleanup.js +77 -22
- package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +9 -5
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +67 -0
- package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +10 -4
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +77 -59
- package/dist/prosemirror/extensions/nodes/TableExtension.js +2 -0
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +4 -1
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +4 -3
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +6 -2
- package/dist/prosemirror/listMarker.d.ts +58 -0
- package/dist/prosemirror/listMarker.js +185 -0
- package/dist/prosemirror/numberedRefFields.d.ts +24 -0
- package/dist/prosemirror/numberedRefFields.js +276 -0
- package/dist/prosemirror/paraText.js +56 -17
- package/dist/prosemirror/plugins/anonymizationDecorations.js +1 -1
- package/dist/prosemirror/plugins/pmTextScan.d.ts +3 -1
- package/dist/prosemirror/plugins/pmTextScan.js +28 -7
- package/dist/prosemirror/plugins/templateDirectives.js +2 -2
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/nodes.d.ts +17 -1
- package/dist/prosemirror/utils/tabCalculator.d.ts +2 -123
- package/dist/prosemirror/utils/tabCalculator.js +1 -140
- package/dist/prosemirror/validation.js +93 -0
- package/dist/render-dom/RemoteSelectionOverlay.d.ts +4 -4
- package/dist/types/documentEnumValues.d.ts +4 -1
- package/dist/types/documentEnumValues.js +12 -1
- package/dist/types/editor-story.d.ts +8 -0
- package/dist/types/editor-story.js +0 -0
- package/dist/types/remote-selection.d.ts +11 -0
- package/dist/types/remote-selection.js +0 -0
- package/dist/types/wrap.d.ts +36 -0
- package/dist/types/wrap.js +40 -0
- package/dist/utils/fontResolver.d.ts +1 -1
- package/dist/utils/fontResolver.js +42 -2
- package/dist/utils/formatToStyle.js +4 -2
- package/dist/utils/horizontalScale.d.ts +28 -0
- package/dist/utils/horizontalScale.js +42 -0
- package/package.json +2 -2
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
//#region src/docx/metafileSvg.ts
|
|
2
|
+
/**
|
|
3
|
+
* Render a deliberately small, browser-safe EMF subset to generated SVG.
|
|
4
|
+
*
|
|
5
|
+
* This covers vector logos built from filled paths. Unsupported records fail
|
|
6
|
+
* closed: a partial metafile is more misleading than the existing placeholder.
|
|
7
|
+
* Original EMF bytes remain the source of truth for DOCX round-trip.
|
|
8
|
+
*/
|
|
9
|
+
const EMF_SIGNATURE = 1179469088;
|
|
10
|
+
const MAX_EMF_RECORDS = 1e4;
|
|
11
|
+
const MAX_EMF_POINTS = 2e4;
|
|
12
|
+
const MAX_EMF_DIMENSION = 1e5;
|
|
13
|
+
const MAX_EMF_SVG_CHARACTERS = 1e6;
|
|
14
|
+
const MAX_EMF_STATE_DEPTH = 256;
|
|
15
|
+
const EMR = {
|
|
16
|
+
header: 1,
|
|
17
|
+
setWindowExtEx: 9,
|
|
18
|
+
setWindowOrgEx: 10,
|
|
19
|
+
setViewportExtEx: 11,
|
|
20
|
+
setViewportOrgEx: 12,
|
|
21
|
+
eof: 14,
|
|
22
|
+
setMapMode: 17,
|
|
23
|
+
setBackgroundMode: 18,
|
|
24
|
+
setPolyFillMode: 19,
|
|
25
|
+
moveToEx: 27,
|
|
26
|
+
saveDc: 33,
|
|
27
|
+
restoreDc: 34,
|
|
28
|
+
selectObject: 37,
|
|
29
|
+
createBrushIndirect: 39,
|
|
30
|
+
deleteObject: 40,
|
|
31
|
+
beginPath: 59,
|
|
32
|
+
endPath: 60,
|
|
33
|
+
closeFigure: 61,
|
|
34
|
+
fillPath: 62,
|
|
35
|
+
polyBezierTo16: 88,
|
|
36
|
+
polyPolygon16: 91,
|
|
37
|
+
extCreatePen: 95
|
|
38
|
+
};
|
|
39
|
+
const MAP_MODE_TEXT = 1;
|
|
40
|
+
const MAP_MODE_ANISOTROPIC = 8;
|
|
41
|
+
const POLY_FILL_ALTERNATE = 1;
|
|
42
|
+
const POLY_FILL_WINDING = 2;
|
|
43
|
+
const STOCK_OBJECT_FLAG = 2147483648;
|
|
44
|
+
const initialState = () => ({
|
|
45
|
+
mapMode: MAP_MODE_TEXT,
|
|
46
|
+
windowOrigin: {
|
|
47
|
+
x: 0,
|
|
48
|
+
y: 0
|
|
49
|
+
},
|
|
50
|
+
windowExtent: {
|
|
51
|
+
x: 1,
|
|
52
|
+
y: 1
|
|
53
|
+
},
|
|
54
|
+
viewportOrigin: {
|
|
55
|
+
x: 0,
|
|
56
|
+
y: 0
|
|
57
|
+
},
|
|
58
|
+
viewportExtent: {
|
|
59
|
+
x: 1,
|
|
60
|
+
y: 1
|
|
61
|
+
},
|
|
62
|
+
fillRule: "nonzero",
|
|
63
|
+
brush: null,
|
|
64
|
+
pen: null,
|
|
65
|
+
currentPoint: {
|
|
66
|
+
x: 0,
|
|
67
|
+
y: 0
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
const cloneState = (state) => ({
|
|
71
|
+
...state,
|
|
72
|
+
windowOrigin: { ...state.windowOrigin },
|
|
73
|
+
windowExtent: { ...state.windowExtent },
|
|
74
|
+
viewportOrigin: { ...state.viewportOrigin },
|
|
75
|
+
viewportExtent: { ...state.viewportExtent },
|
|
76
|
+
currentPoint: { ...state.currentPoint }
|
|
77
|
+
});
|
|
78
|
+
const int16 = (view, offset, end) => offset >= 0 && offset + 2 <= end ? view.getInt16(offset, true) : void 0;
|
|
79
|
+
const uint32 = (view, offset, end) => offset >= 0 && offset + 4 <= end ? view.getUint32(offset, true) : void 0;
|
|
80
|
+
const int32 = (view, offset, end) => offset >= 0 && offset + 4 <= end ? view.getInt32(offset, true) : void 0;
|
|
81
|
+
const numberText = (value) => {
|
|
82
|
+
const rounded = Math.round(value * 1e4) / 1e4;
|
|
83
|
+
return Object.is(rounded, -0) ? "0" : String(rounded);
|
|
84
|
+
};
|
|
85
|
+
const colorRefToCss = (color) => {
|
|
86
|
+
const red = color & 255;
|
|
87
|
+
const green = color >>> 8 & 255;
|
|
88
|
+
const blue = color >>> 16 & 255;
|
|
89
|
+
return `#${red.toString(16).padStart(2, "0")}${green.toString(16).padStart(2, "0")}${blue.toString(16).padStart(2, "0")}`;
|
|
90
|
+
};
|
|
91
|
+
const stockObject = (handle) => {
|
|
92
|
+
switch (handle) {
|
|
93
|
+
case 2147483648: return {
|
|
94
|
+
type: "brush",
|
|
95
|
+
fill: "#ffffff"
|
|
96
|
+
};
|
|
97
|
+
case 2147483649: return {
|
|
98
|
+
type: "brush",
|
|
99
|
+
fill: "#c0c0c0"
|
|
100
|
+
};
|
|
101
|
+
case 2147483650: return {
|
|
102
|
+
type: "brush",
|
|
103
|
+
fill: "#808080"
|
|
104
|
+
};
|
|
105
|
+
case 2147483651: return {
|
|
106
|
+
type: "brush",
|
|
107
|
+
fill: "#404040"
|
|
108
|
+
};
|
|
109
|
+
case 2147483652: return {
|
|
110
|
+
type: "brush",
|
|
111
|
+
fill: "#000000"
|
|
112
|
+
};
|
|
113
|
+
case 2147483653: return {
|
|
114
|
+
type: "brush",
|
|
115
|
+
fill: null
|
|
116
|
+
};
|
|
117
|
+
case 2147483656: return {
|
|
118
|
+
type: "pen",
|
|
119
|
+
stroke: null
|
|
120
|
+
};
|
|
121
|
+
default: return;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const mappedPoint = (state, bounds, x, y) => {
|
|
125
|
+
let mappedX;
|
|
126
|
+
let mappedY;
|
|
127
|
+
if (state.mapMode === MAP_MODE_TEXT) {
|
|
128
|
+
mappedX = x - state.windowOrigin.x + state.viewportOrigin.x - bounds.left;
|
|
129
|
+
mappedY = y - state.windowOrigin.y + state.viewportOrigin.y - bounds.top;
|
|
130
|
+
} else {
|
|
131
|
+
const { windowExtent, windowOrigin, viewportExtent, viewportOrigin } = state;
|
|
132
|
+
if (windowExtent.x === 0 || windowExtent.y === 0) return;
|
|
133
|
+
mappedX = (x - windowOrigin.x) * viewportExtent.x / windowExtent.x + viewportOrigin.x - bounds.left;
|
|
134
|
+
mappedY = (y - windowOrigin.y) * viewportExtent.y / windowExtent.y + viewportOrigin.y - bounds.top;
|
|
135
|
+
}
|
|
136
|
+
if (!Number.isFinite(mappedX) || !Number.isFinite(mappedY)) return;
|
|
137
|
+
if (Math.abs(mappedX) > MAX_EMF_DIMENSION * 100 || Math.abs(mappedY) > MAX_EMF_DIMENSION * 100) return;
|
|
138
|
+
return {
|
|
139
|
+
x: mappedX,
|
|
140
|
+
y: mappedY
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
const pathPoint = (command, point) => `${command}${numberText(point.x)} ${numberText(point.y)}`;
|
|
144
|
+
const selectGraphicsObject = (state, objects, handle) => {
|
|
145
|
+
const object = handle >= STOCK_OBJECT_FLAG ? stockObject(handle) : objects.get(handle);
|
|
146
|
+
if (!object) return false;
|
|
147
|
+
if (object.type === "brush") state.brush = object;
|
|
148
|
+
else state.pen = object;
|
|
149
|
+
return true;
|
|
150
|
+
};
|
|
151
|
+
/** Convert the supported filled-path EMF subset to inert generated SVG. */
|
|
152
|
+
function renderEmfSvg(data) {
|
|
153
|
+
const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
154
|
+
if (bytes.byteLength < 88) return null;
|
|
155
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
156
|
+
const headerType = uint32(view, 0, bytes.byteLength);
|
|
157
|
+
const headerSize = uint32(view, 4, bytes.byteLength);
|
|
158
|
+
const signature = uint32(view, 40, bytes.byteLength);
|
|
159
|
+
const declaredBytes = uint32(view, 48, bytes.byteLength);
|
|
160
|
+
const declaredRecords = uint32(view, 52, bytes.byteLength);
|
|
161
|
+
const left = int32(view, 8, bytes.byteLength);
|
|
162
|
+
const top = int32(view, 12, bytes.byteLength);
|
|
163
|
+
const right = int32(view, 16, bytes.byteLength);
|
|
164
|
+
const bottom = int32(view, 20, bytes.byteLength);
|
|
165
|
+
if (headerType !== EMR.header || headerSize === void 0 || headerSize < 88 || headerSize % 4 !== 0 || headerSize > bytes.byteLength || signature !== EMF_SIGNATURE || declaredBytes !== bytes.byteLength || declaredRecords === void 0 || declaredRecords < 2 || declaredRecords > MAX_EMF_RECORDS || left === void 0 || top === void 0 || right === void 0 || bottom === void 0) return null;
|
|
166
|
+
const width = right - left;
|
|
167
|
+
const height = bottom - top;
|
|
168
|
+
if (width <= 0 || height <= 0 || width > MAX_EMF_DIMENSION || height > MAX_EMF_DIMENSION) return null;
|
|
169
|
+
const bounds = {
|
|
170
|
+
left,
|
|
171
|
+
top,
|
|
172
|
+
width,
|
|
173
|
+
height
|
|
174
|
+
};
|
|
175
|
+
const objects = /* @__PURE__ */ new Map();
|
|
176
|
+
const stack = [];
|
|
177
|
+
let state = initialState();
|
|
178
|
+
let offset = headerSize;
|
|
179
|
+
let recordCount = 1;
|
|
180
|
+
let pointCount = 0;
|
|
181
|
+
let pathState = { type: "idle" };
|
|
182
|
+
const renderedPaths = [];
|
|
183
|
+
let sawEof = false;
|
|
184
|
+
while (offset + 8 <= bytes.byteLength && recordCount < MAX_EMF_RECORDS) {
|
|
185
|
+
const type = uint32(view, offset, bytes.byteLength);
|
|
186
|
+
const size = uint32(view, offset + 4, bytes.byteLength);
|
|
187
|
+
if (type === void 0 || size === void 0 || size < 8 || size % 4 !== 0 || size > bytes.byteLength - offset) return null;
|
|
188
|
+
const end = offset + size;
|
|
189
|
+
recordCount += 1;
|
|
190
|
+
switch (type) {
|
|
191
|
+
case EMR.eof:
|
|
192
|
+
if (size !== 20 || end !== bytes.byteLength || recordCount !== declaredRecords) return null;
|
|
193
|
+
sawEof = true;
|
|
194
|
+
break;
|
|
195
|
+
case EMR.setMapMode: {
|
|
196
|
+
const mode = int32(view, offset + 8, end);
|
|
197
|
+
if (size !== 12 || mode !== MAP_MODE_TEXT && mode !== MAP_MODE_ANISOTROPIC) return null;
|
|
198
|
+
state.mapMode = mode;
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
case EMR.setBackgroundMode:
|
|
202
|
+
if (size !== 12 || uint32(view, offset + 8, end) === void 0) return null;
|
|
203
|
+
break;
|
|
204
|
+
case EMR.setPolyFillMode: {
|
|
205
|
+
const mode = uint32(view, offset + 8, end);
|
|
206
|
+
if (size !== 12 || mode !== POLY_FILL_ALTERNATE && mode !== POLY_FILL_WINDING) return null;
|
|
207
|
+
state.fillRule = mode === POLY_FILL_ALTERNATE ? "evenodd" : "nonzero";
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
case EMR.setWindowOrgEx:
|
|
211
|
+
case EMR.setWindowExtEx:
|
|
212
|
+
case EMR.setViewportOrgEx:
|
|
213
|
+
case EMR.setViewportExtEx: {
|
|
214
|
+
const x = int32(view, offset + 8, end);
|
|
215
|
+
const y = int32(view, offset + 12, end);
|
|
216
|
+
if (size !== 16 || x === void 0 || y === void 0) return null;
|
|
217
|
+
if (type === EMR.setWindowOrgEx) state.windowOrigin = {
|
|
218
|
+
x,
|
|
219
|
+
y
|
|
220
|
+
};
|
|
221
|
+
else if (type === EMR.setWindowExtEx) {
|
|
222
|
+
if (state.mapMode === MAP_MODE_ANISOTROPIC) {
|
|
223
|
+
if (x === 0 || y === 0) return null;
|
|
224
|
+
state.windowExtent = {
|
|
225
|
+
x,
|
|
226
|
+
y
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
} else if (type === EMR.setViewportOrgEx) state.viewportOrigin = {
|
|
230
|
+
x,
|
|
231
|
+
y
|
|
232
|
+
};
|
|
233
|
+
else if (state.mapMode === MAP_MODE_ANISOTROPIC) {
|
|
234
|
+
if (x === 0 || y === 0) return null;
|
|
235
|
+
state.viewportExtent = {
|
|
236
|
+
x,
|
|
237
|
+
y
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
case EMR.saveDc:
|
|
243
|
+
if (size !== 8 || stack.length >= MAX_EMF_STATE_DEPTH) return null;
|
|
244
|
+
stack.push(cloneState(state));
|
|
245
|
+
break;
|
|
246
|
+
case EMR.restoreDc: {
|
|
247
|
+
const relative = int32(view, offset + 8, end);
|
|
248
|
+
if (size !== 12 || relative === void 0 || relative >= 0 || -relative > stack.length) return null;
|
|
249
|
+
let restored;
|
|
250
|
+
for (let index = 0; index < -relative; index += 1) restored = stack.pop();
|
|
251
|
+
if (!restored) return null;
|
|
252
|
+
state = restored;
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
case EMR.createBrushIndirect: {
|
|
256
|
+
const handle = uint32(view, offset + 8, end);
|
|
257
|
+
const style = uint32(view, offset + 12, end);
|
|
258
|
+
const color = uint32(view, offset + 16, end);
|
|
259
|
+
if (size !== 24 || handle === void 0 || handle >= STOCK_OBJECT_FLAG || objects.has(handle) || style === void 0 || color === void 0) return null;
|
|
260
|
+
if (style !== 0 && style !== 1) return null;
|
|
261
|
+
objects.set(handle, {
|
|
262
|
+
type: "brush",
|
|
263
|
+
fill: style === 1 ? null : colorRefToCss(color)
|
|
264
|
+
});
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
case EMR.extCreatePen: {
|
|
268
|
+
const handle = uint32(view, offset + 8, end);
|
|
269
|
+
const style = uint32(view, offset + 28, end);
|
|
270
|
+
const bmiBytes = uint32(view, offset + 16, end);
|
|
271
|
+
const bitmapBytes = uint32(view, offset + 24, end);
|
|
272
|
+
const styleEntries = uint32(view, offset + 48, end);
|
|
273
|
+
if (size !== 52 || handle === void 0 || handle >= STOCK_OBJECT_FLAG || objects.has(handle) || style === void 0 || bmiBytes !== 0 || bitmapBytes !== 0 || styleEntries !== 0 || (style & 15) !== 5) return null;
|
|
274
|
+
objects.set(handle, {
|
|
275
|
+
type: "pen",
|
|
276
|
+
stroke: null
|
|
277
|
+
});
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
case EMR.selectObject: {
|
|
281
|
+
const handle = uint32(view, offset + 8, end);
|
|
282
|
+
if (size !== 12 || handle === void 0 || !selectGraphicsObject(state, objects, handle)) return null;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
case EMR.deleteObject: {
|
|
286
|
+
const handle = uint32(view, offset + 8, end);
|
|
287
|
+
if (size !== 12 || handle === void 0) return null;
|
|
288
|
+
const object = objects.get(handle);
|
|
289
|
+
if (object === state.brush) state.brush = null;
|
|
290
|
+
else if (object === state.pen) state.pen = null;
|
|
291
|
+
for (const savedState of stack) if (object === savedState.brush) savedState.brush = null;
|
|
292
|
+
else if (object === savedState.pen) savedState.pen = null;
|
|
293
|
+
objects.delete(handle);
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case EMR.beginPath:
|
|
297
|
+
if (size !== 8) return null;
|
|
298
|
+
switch (pathState.type) {
|
|
299
|
+
case "idle":
|
|
300
|
+
pathState = {
|
|
301
|
+
type: "building",
|
|
302
|
+
commands: []
|
|
303
|
+
};
|
|
304
|
+
break;
|
|
305
|
+
case "building":
|
|
306
|
+
case "completed": return null;
|
|
307
|
+
default:
|
|
308
|
+
}
|
|
309
|
+
break;
|
|
310
|
+
case EMR.moveToEx: {
|
|
311
|
+
const x = int32(view, offset + 8, end);
|
|
312
|
+
const y = int32(view, offset + 12, end);
|
|
313
|
+
if (size !== 16 || x === void 0 || y === void 0) return null;
|
|
314
|
+
state.currentPoint = {
|
|
315
|
+
x,
|
|
316
|
+
y
|
|
317
|
+
};
|
|
318
|
+
switch (pathState.type) {
|
|
319
|
+
case "idle":
|
|
320
|
+
case "completed": break;
|
|
321
|
+
case "building": {
|
|
322
|
+
const mapped = mappedPoint(state, bounds, x, y);
|
|
323
|
+
if (!mapped) return null;
|
|
324
|
+
pathState.commands.push(pathPoint("M", mapped));
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
default:
|
|
328
|
+
}
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
case EMR.polyBezierTo16: {
|
|
332
|
+
let commands;
|
|
333
|
+
switch (pathState.type) {
|
|
334
|
+
case "building":
|
|
335
|
+
commands = pathState.commands;
|
|
336
|
+
break;
|
|
337
|
+
case "idle":
|
|
338
|
+
case "completed": return null;
|
|
339
|
+
default: return null;
|
|
340
|
+
}
|
|
341
|
+
const count = uint32(view, offset + 24, end);
|
|
342
|
+
if (count === void 0 || count === 0 || count % 3 !== 0 || count > MAX_EMF_POINTS - pointCount || size !== 28 + count * 4) return null;
|
|
343
|
+
pointCount += count;
|
|
344
|
+
if (commands.length === 0) {
|
|
345
|
+
const mapped = mappedPoint(state, bounds, state.currentPoint.x, state.currentPoint.y);
|
|
346
|
+
if (!mapped) return null;
|
|
347
|
+
commands.push(pathPoint("M", mapped));
|
|
348
|
+
}
|
|
349
|
+
for (let index = 0; index < count; index += 3) {
|
|
350
|
+
const points = [];
|
|
351
|
+
for (let controlIndex = 0; controlIndex < 3; controlIndex += 1) {
|
|
352
|
+
const pointOffset = offset + 28 + (index + controlIndex) * 4;
|
|
353
|
+
const x = int16(view, pointOffset, end);
|
|
354
|
+
const y = int16(view, pointOffset + 2, end);
|
|
355
|
+
if (x === void 0 || y === void 0) return null;
|
|
356
|
+
const mapped = mappedPoint(state, bounds, x, y);
|
|
357
|
+
if (!mapped) return null;
|
|
358
|
+
points.push(mapped);
|
|
359
|
+
state.currentPoint = {
|
|
360
|
+
x,
|
|
361
|
+
y
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
const [first, second, third] = points;
|
|
365
|
+
if (!first || !second || !third) return null;
|
|
366
|
+
commands.push(`C${numberText(first.x)} ${numberText(first.y)} ${numberText(second.x)} ${numberText(second.y)} ${numberText(third.x)} ${numberText(third.y)}`);
|
|
367
|
+
}
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
case EMR.polyPolygon16: {
|
|
371
|
+
const polygonCount = uint32(view, offset + 24, end);
|
|
372
|
+
const totalPoints = uint32(view, offset + 28, end);
|
|
373
|
+
if (polygonCount === void 0 || totalPoints === void 0 || polygonCount === 0 || totalPoints === 0 || polygonCount > totalPoints || totalPoints > MAX_EMF_POINTS - pointCount || polygonCount > Math.floor((end - (offset + 32)) / 4)) return null;
|
|
374
|
+
const pointsOffset = offset + 32 + polygonCount * 4;
|
|
375
|
+
if (size !== 32 + polygonCount * 4 + totalPoints * 4) return null;
|
|
376
|
+
pointCount += totalPoints;
|
|
377
|
+
let consumedPoints = 0;
|
|
378
|
+
const polygonPath = [];
|
|
379
|
+
for (let polygonIndex = 0; polygonIndex < polygonCount; polygonIndex += 1) {
|
|
380
|
+
const count = uint32(view, offset + 32 + polygonIndex * 4, end);
|
|
381
|
+
if (count === void 0 || count < 2 || count > totalPoints - consumedPoints) return null;
|
|
382
|
+
for (let index = 0; index < count; index += 1) {
|
|
383
|
+
const pointOffset = pointsOffset + (consumedPoints + index) * 4;
|
|
384
|
+
const x = int16(view, pointOffset, end);
|
|
385
|
+
const y = int16(view, pointOffset + 2, end);
|
|
386
|
+
if (x === void 0 || y === void 0) return null;
|
|
387
|
+
const mapped = mappedPoint(state, bounds, x, y);
|
|
388
|
+
if (!mapped) return null;
|
|
389
|
+
polygonPath.push(pathPoint(index === 0 ? "M" : "L", mapped));
|
|
390
|
+
}
|
|
391
|
+
polygonPath.push("Z");
|
|
392
|
+
consumedPoints += count;
|
|
393
|
+
}
|
|
394
|
+
if (consumedPoints !== totalPoints) return null;
|
|
395
|
+
switch (pathState.type) {
|
|
396
|
+
case "building":
|
|
397
|
+
pathState.commands.push(...polygonPath);
|
|
398
|
+
break;
|
|
399
|
+
case "idle":
|
|
400
|
+
case "completed":
|
|
401
|
+
if (!state.brush?.fill) return null;
|
|
402
|
+
renderedPaths.push(`<path d="${polygonPath.join(" ")}" fill="${state.brush.fill}" fill-rule="${state.fillRule}"/>`);
|
|
403
|
+
break;
|
|
404
|
+
default:
|
|
405
|
+
}
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
case EMR.closeFigure:
|
|
409
|
+
if (size !== 8) return null;
|
|
410
|
+
switch (pathState.type) {
|
|
411
|
+
case "building":
|
|
412
|
+
pathState.commands.push("Z");
|
|
413
|
+
break;
|
|
414
|
+
case "idle":
|
|
415
|
+
case "completed": return null;
|
|
416
|
+
default:
|
|
417
|
+
}
|
|
418
|
+
break;
|
|
419
|
+
case EMR.endPath:
|
|
420
|
+
if (size !== 8) return null;
|
|
421
|
+
switch (pathState.type) {
|
|
422
|
+
case "building":
|
|
423
|
+
if (pathState.commands.length === 0) return null;
|
|
424
|
+
pathState = {
|
|
425
|
+
type: "completed",
|
|
426
|
+
commands: pathState.commands
|
|
427
|
+
};
|
|
428
|
+
break;
|
|
429
|
+
case "idle":
|
|
430
|
+
case "completed": return null;
|
|
431
|
+
default:
|
|
432
|
+
}
|
|
433
|
+
break;
|
|
434
|
+
case EMR.fillPath:
|
|
435
|
+
if (size !== 24 || !state.brush?.fill) return null;
|
|
436
|
+
switch (pathState.type) {
|
|
437
|
+
case "completed":
|
|
438
|
+
renderedPaths.push(`<path d="${pathState.commands.join(" ")}" fill="${state.brush.fill}" fill-rule="${state.fillRule}"/>`);
|
|
439
|
+
pathState = { type: "idle" };
|
|
440
|
+
break;
|
|
441
|
+
case "idle":
|
|
442
|
+
case "building": return null;
|
|
443
|
+
default:
|
|
444
|
+
}
|
|
445
|
+
break;
|
|
446
|
+
default: return null;
|
|
447
|
+
}
|
|
448
|
+
offset = end;
|
|
449
|
+
if (sawEof) break;
|
|
450
|
+
}
|
|
451
|
+
if (!sawEof || offset !== bytes.byteLength || recordCount !== declaredRecords || renderedPaths.length === 0) return null;
|
|
452
|
+
switch (pathState.type) {
|
|
453
|
+
case "idle": break;
|
|
454
|
+
case "building":
|
|
455
|
+
case "completed": return null;
|
|
456
|
+
default:
|
|
457
|
+
}
|
|
458
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${numberText(width)} ${numberText(height)}">${renderedPaths.join("")}</svg>`;
|
|
459
|
+
return svg.length <= MAX_EMF_SVG_CHARACTERS ? svg : null;
|
|
460
|
+
}
|
|
461
|
+
//#endregion
|
|
462
|
+
export { renderEmfSvg };
|
|
@@ -23,9 +23,18 @@ const VML_VERTICAL_RELATIVES = /* @__PURE__ */ new Set([
|
|
|
23
23
|
"topMargin",
|
|
24
24
|
"bottomMargin"
|
|
25
25
|
]);
|
|
26
|
+
const VML_TEXTBOX_INSET_DEFAULTS = [
|
|
27
|
+
"0.1in",
|
|
28
|
+
"0.05in",
|
|
29
|
+
"0.1in",
|
|
30
|
+
"0.05in"
|
|
31
|
+
];
|
|
32
|
+
const MAX_VML_TEXTBOX_INSET_TOKEN_CHARACTERS = 64;
|
|
33
|
+
const MAX_VML_STYLE_CHARACTERS = 16384;
|
|
26
34
|
const parseVmlStyle = (value) => {
|
|
27
35
|
const declarations = {};
|
|
28
|
-
|
|
36
|
+
if (!value || value.length > MAX_VML_STYLE_CHARACTERS) return declarations;
|
|
37
|
+
for (const declaration of value.split(";")) {
|
|
29
38
|
const separator = declaration.indexOf(":");
|
|
30
39
|
if (separator < 0) continue;
|
|
31
40
|
const key = declaration.slice(0, separator).trim().toLowerCase();
|
|
@@ -56,9 +65,39 @@ const verticalRelativeTo = (value) => {
|
|
|
56
65
|
for (const relative of VML_VERTICAL_RELATIVES) if (relative.toLowerCase() === value?.toLowerCase()) return relative;
|
|
57
66
|
return "paragraph";
|
|
58
67
|
};
|
|
68
|
+
const parseVmlInsetFields = (rawInset) => {
|
|
69
|
+
const fields = [];
|
|
70
|
+
let offset = 0;
|
|
71
|
+
let segmentHasField = false;
|
|
72
|
+
while (offset <= rawInset.length) {
|
|
73
|
+
while (/\s/u.test(rawInset.charAt(offset))) offset += 1;
|
|
74
|
+
if (offset >= rawInset.length) {
|
|
75
|
+
if (!segmentHasField) fields.push(void 0);
|
|
76
|
+
return fields.length <= VML_TEXTBOX_INSET_DEFAULTS.length ? fields : void 0;
|
|
77
|
+
}
|
|
78
|
+
if (rawInset.charAt(offset) === ",") {
|
|
79
|
+
if (!segmentHasField) fields.push(void 0);
|
|
80
|
+
if (fields.length > VML_TEXTBOX_INSET_DEFAULTS.length) return;
|
|
81
|
+
segmentHasField = false;
|
|
82
|
+
offset += 1;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const tokenStart = offset;
|
|
86
|
+
while (offset < rawInset.length && !/[,\s]/u.test(rawInset.charAt(offset))) {
|
|
87
|
+
offset += 1;
|
|
88
|
+
if (offset - tokenStart > MAX_VML_TEXTBOX_INSET_TOKEN_CHARACTERS) return;
|
|
89
|
+
}
|
|
90
|
+
fields.push(rawInset.slice(tokenStart, offset));
|
|
91
|
+
if (fields.length > VML_TEXTBOX_INSET_DEFAULTS.length) return;
|
|
92
|
+
segmentHasField = true;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
59
95
|
const parseVmlInsets = (textBoxEl) => {
|
|
60
|
-
const
|
|
61
|
-
|
|
96
|
+
const rawInset = getAttribute(textBoxEl, null, "inset") ?? "";
|
|
97
|
+
const fields = parseVmlInsetFields(rawInset);
|
|
98
|
+
if (!fields) return;
|
|
99
|
+
const [left, top, right, bottom] = VML_TEXTBOX_INSET_DEFAULTS.map((fallback, index) => vmlLengthToPixels(fields.at(index) ?? fallback));
|
|
100
|
+
if (left === void 0 || top === void 0 || right === void 0 || bottom === void 0) return;
|
|
62
101
|
return {
|
|
63
102
|
left: pixelsToEmu(left),
|
|
64
103
|
top: pixelsToEmu(top),
|
|
@@ -67,6 +106,14 @@ const parseVmlInsets = (textBoxEl) => {
|
|
|
67
106
|
};
|
|
68
107
|
};
|
|
69
108
|
const parseVmlFill = (shapeEl) => getAttribute(shapeEl, null, "filled")?.toLowerCase() === "f" ? { type: "none" } : void 0;
|
|
109
|
+
const parseVmlTextAnchor = (value) => {
|
|
110
|
+
switch (value?.toLowerCase()) {
|
|
111
|
+
case "top": return "top";
|
|
112
|
+
case "middle": return "middle";
|
|
113
|
+
case "bottom": return "bottom";
|
|
114
|
+
default: return;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
70
117
|
const vmlWrapType = (positioned, zIndex) => {
|
|
71
118
|
if (!positioned) return "inline";
|
|
72
119
|
if (Number.isFinite(zIndex) && zIndex < 0) return "behind";
|
|
@@ -159,6 +206,7 @@ const enrichTextBoxRuns = ({ content, xmlChildren, styles, theme, numbering, rel
|
|
|
159
206
|
content: textBox.content,
|
|
160
207
|
...textBox.autoFit !== void 0 ? { autoFit: textBox.autoFit } : {},
|
|
161
208
|
...textBox.textWrap !== void 0 ? { textWrap: textBox.textWrap } : {},
|
|
209
|
+
...textBox.verticalAlign !== void 0 ? { anchor: textBox.verticalAlign } : {},
|
|
162
210
|
...textBox.margins !== void 0 ? { margins: textBox.margins } : {}
|
|
163
211
|
}
|
|
164
212
|
};
|
|
@@ -258,6 +306,7 @@ const parseVmlTextBoxShape = (pictEl, styles, theme, numbering, rels, media, par
|
|
|
258
306
|
const contentEl = textBoxEl ? findDeep(textBoxEl, "w", "txbxContent") : null;
|
|
259
307
|
if (!shapeEl || !textBoxEl || !contentEl) return null;
|
|
260
308
|
const style = parseVmlStyle(getAttribute(shapeEl, null, "style"));
|
|
309
|
+
const textBoxStyle = parseVmlStyle(getAttribute(textBoxEl, null, "style"));
|
|
261
310
|
const width = vmlLengthToPixels(style["width"]);
|
|
262
311
|
const height = vmlLengthToPixels(style["height"]);
|
|
263
312
|
if (width === void 0 || height === void 0 || width <= 0 || height <= 0) return null;
|
|
@@ -266,6 +315,7 @@ const parseVmlTextBoxShape = (pictEl, styles, theme, numbering, rels, media, par
|
|
|
266
315
|
const positioned = style["position"]?.toLowerCase() === "absolute";
|
|
267
316
|
const zIndex = Number.parseInt(style["z-index"] ?? "", 10);
|
|
268
317
|
const margins = parseVmlInsets(textBoxEl);
|
|
318
|
+
const anchor = parseVmlTextAnchor(textBoxStyle["v-text-anchor"]);
|
|
269
319
|
const fill = parseVmlFill(shapeEl);
|
|
270
320
|
const shape = {
|
|
271
321
|
type: "shape",
|
|
@@ -278,7 +328,8 @@ const parseVmlTextBoxShape = (pictEl, styles, theme, numbering, rels, media, par
|
|
|
278
328
|
wrap: { type: vmlWrapType(positioned, zIndex) },
|
|
279
329
|
textBody: {
|
|
280
330
|
content: parseTextBoxContent(contentEl, parseParagraph, parseTable, styles, theme, numbering, rels, media),
|
|
281
|
-
...margins === void 0 ? {} : { margins }
|
|
331
|
+
...margins === void 0 ? {} : { margins },
|
|
332
|
+
...anchor === void 0 ? {} : { anchor }
|
|
282
333
|
}
|
|
283
334
|
};
|
|
284
335
|
const id = getAttribute(shapeEl, null, "id");
|
package/dist/docx/parser.d.ts
CHANGED
|
@@ -11,8 +11,8 @@ type ProgressCallback = (stage: string, percent: number) => void;
|
|
|
11
11
|
* (EMF/WMF/TIFF) into a displayable `data:` or `blob:` URL. Receives the
|
|
12
12
|
* parsed {@link MediaFile} (original bytes on `.data`); return the replacement
|
|
13
13
|
* URL, or `null`/`undefined` to keep the built-in handling. Built-in handling
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* extracts embedded PNG/JPEG previews and renders a bounded filled-path EMF
|
|
15
|
+
* subset; this hook covers remaining formats and unsupported vector records.
|
|
16
16
|
*/
|
|
17
17
|
type MediaResolver = (file: document_d_exports.MediaFile) => Promise<string | null | undefined>;
|
|
18
18
|
/**
|
package/dist/docx/parser.js
CHANGED
|
@@ -14,6 +14,7 @@ import { parseFooter, parseHeader } from "./headerFooterParser.js";
|
|
|
14
14
|
import { normalizeHeaderFooterReferences } from "./headerFooterReferenceNormalization.js";
|
|
15
15
|
import { assignHeaderFooterVerbatimXml, refreshHeaderFooterVerbatimFingerprint } from "./headerFooterVerbatim.js";
|
|
16
16
|
import { extractMetafileRaster, isMetafileMimeType } from "./metafileRaster.js";
|
|
17
|
+
import { renderEmfSvg } from "./metafileSvg.js";
|
|
17
18
|
import { DocxModelValidationError, formatDocumentModelIssues, validateFolioDocumentModel } from "./modelValidation.js";
|
|
18
19
|
import { parseNumbering } from "./numberingParser.js";
|
|
19
20
|
import { normalizeNumberingReferences } from "./numberingReferenceNormalization.js";
|
|
@@ -24,6 +25,7 @@ import { parseStylesPackage } from "./styleParser.js";
|
|
|
24
25
|
import { applyThemeFontLang, parseTheme } from "./themeParser.js";
|
|
25
26
|
import { normalizeTrackedMoveRanges } from "./trackedMoveRangeNormalization.js";
|
|
26
27
|
import { getMediaMimeType, mediaToDataUrl, unzipDocx } from "./unzip.js";
|
|
28
|
+
import { enforcePackageVmlPreviewBudget } from "./vmlPreview.js";
|
|
27
29
|
import { TaggedError } from "better-result";
|
|
28
30
|
//#region src/docx/parser.ts
|
|
29
31
|
/**
|
|
@@ -45,6 +47,9 @@ import { TaggedError } from "better-result";
|
|
|
45
47
|
* 10. Build media file map
|
|
46
48
|
* 11. Assemble final Document
|
|
47
49
|
*/
|
|
50
|
+
const EMF_MIME_TYPE = "image/x-emf";
|
|
51
|
+
const SVG_MIME_TYPE = "image/svg+xml";
|
|
52
|
+
const MAX_PACKAGE_EMF_PREVIEW_BYTES = 8 * 1024 * 1024;
|
|
48
53
|
/**
|
|
49
54
|
* Parse a DOCX file into a complete Document model
|
|
50
55
|
*
|
|
@@ -211,6 +216,7 @@ async function parseDocx(input, options = {}) {
|
|
|
211
216
|
...templateVariables !== void 0 ? { templateVariables } : {},
|
|
212
217
|
...requiredFonts.length > 0 ? { requiredFonts } : {}
|
|
213
218
|
};
|
|
219
|
+
enforcePackageVmlPreviewBudget(document.package);
|
|
214
220
|
const validation = validateFolioDocumentModel(document);
|
|
215
221
|
const parsedCompleteModel = parseHeadersFooters && parseNotes;
|
|
216
222
|
if (!validation.valid && parsedCompleteModel) throw new DocxModelValidationError("Parsed DOCX produced an invalid document model", validation.issues);
|
|
@@ -260,6 +266,7 @@ async function buildMediaMap(raw, rels) {
|
|
|
260
266
|
const media = /* @__PURE__ */ new Map();
|
|
261
267
|
const referenced = collectReferencedMediaPaths(raw, rels);
|
|
262
268
|
let remainingTiffPixels = MAX_PACKAGE_TIFF_PIXELS;
|
|
269
|
+
let remainingEmfPreviewBytes = MAX_PACKAGE_EMF_PREVIEW_BYTES;
|
|
263
270
|
for (const [path, data] of raw.media.entries()) {
|
|
264
271
|
const filename = path.split("/").pop() || path;
|
|
265
272
|
const mimeType = getMediaMimeType(path);
|
|
@@ -295,6 +302,25 @@ async function buildMediaMap(raw, rels) {
|
|
|
295
302
|
if (normalizedPath !== path) media.set(normalizedPath, mediaFile);
|
|
296
303
|
continue;
|
|
297
304
|
}
|
|
305
|
+
const emfSvg = isReferenced && mimeType === EMF_MIME_TYPE && remainingEmfPreviewBytes > 0 ? renderEmfSvg(data) : null;
|
|
306
|
+
if (emfSvg) {
|
|
307
|
+
const svgBytes = new TextEncoder().encode(emfSvg);
|
|
308
|
+
if (svgBytes.byteLength <= remainingEmfPreviewBytes) {
|
|
309
|
+
remainingEmfPreviewBytes -= svgBytes.byteLength;
|
|
310
|
+
const mediaFile = {
|
|
311
|
+
path,
|
|
312
|
+
filename,
|
|
313
|
+
mimeType,
|
|
314
|
+
data,
|
|
315
|
+
dataUrl: mediaToDataUrl(copyBytesToArrayBuffer(svgBytes), SVG_MIME_TYPE)
|
|
316
|
+
};
|
|
317
|
+
media.set(path, mediaFile);
|
|
318
|
+
const normalizedPath = path.replace(/^word\//u, "");
|
|
319
|
+
if (normalizedPath !== path) media.set(normalizedPath, mediaFile);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
remainingEmfPreviewBytes = 0;
|
|
323
|
+
}
|
|
298
324
|
const mediaFile = {
|
|
299
325
|
path,
|
|
300
326
|
filename,
|
package/dist/docx/runParser.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isValidHexColor } from "../utils/colorResolver.js";
|
|
2
|
+
import { parseHorizontalScalePercent } from "../utils/horizontalScale.js";
|
|
2
3
|
import { parseGroupDrawing } from "./groupDrawingParser.js";
|
|
3
4
|
import { parseImage } from "./imageParser.js";
|
|
4
5
|
import { EmphasisMarkSchema, FontHintSchema, FontThemeSchema, HighlightColorSchema, PositionalTabAlignmentSchema, PositionalTabLeaderSchema, PositionalTabRelativeToSchema, ShadingPatternSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum } from "./parserEnums.js";
|
|
@@ -302,7 +303,7 @@ function parseRunProperties(rPr, theme, _styles) {
|
|
|
302
303
|
}
|
|
303
304
|
const w = propertyChildren.w;
|
|
304
305
|
if (w) {
|
|
305
|
-
const val =
|
|
306
|
+
const val = parseHorizontalScalePercent(getAttribute(w, "w", "val"));
|
|
306
307
|
if (val !== void 0) formatting.scale = val;
|
|
307
308
|
}
|
|
308
309
|
const kern = propertyChildren.kern;
|