@stll/folio-core 0.60.0 → 0.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/ai-edits/apply.js +139 -32
- package/dist/ai-edits/headless.d.ts +3 -0
- package/dist/ai-edits/headless.js +70 -75
- package/dist/ai-edits/note-story-inventory.d.ts +7 -0
- package/dist/ai-edits/note-story-inventory.js +11 -0
- package/dist/ai-edits/read.js +16 -9
- package/dist/ai-edits/revisionStretches.js +52 -1
- package/dist/ai-edits/table-row-column-mutations.js +6 -1
- package/dist/compare/reproducible-package.js +2 -1
- package/dist/compare/verification.js +1 -0
- package/dist/compat/eigenpal.d.ts +3 -1
- package/dist/controller/canonicalClipboard.js +2 -1
- package/dist/controller/canonicalListRendering.d.ts +4 -6
- package/dist/controller/canonicalListRendering.js +3 -157
- package/dist/controller/headerFooterEditorManager.d.ts +1 -0
- package/dist/controller/headerFooterEditorManager.js +11 -0
- package/dist/controller/hiddenEditorManager.d.ts +1 -1
- package/dist/controller/hiddenEditorManager.js +3 -1
- package/dist/controller/layoutPipeline.d.ts +3 -1
- package/dist/controller/layoutPipeline.js +13 -4
- package/dist/controller/layoutSession.d.ts +2 -1
- package/dist/controller/noteEditorManager.d.ts +1 -1
- package/dist/display-list/build/imagePrimitives.js +28 -12
- package/dist/display-list/build/paragraphPrimitives.js +15 -2
- package/dist/display-list/dom/renderDisplayListToDom.js +8 -5
- package/dist/display-list/types.d.ts +7 -3
- package/dist/docx/canonicalStoryRepack.js +3 -2
- package/dist/docx/commentTextParagraph.d.ts +6 -0
- package/dist/docx/commentTextParagraph.js +15 -0
- package/dist/docx/conformance.js +6 -1
- package/dist/docx/diagramPreview.js +2 -1
- package/dist/docx/documentParser.d.ts +13 -1
- package/dist/docx/documentParser.js +1 -1
- package/dist/docx/encryption/compoundFile.d.ts +0 -8
- package/dist/docx/encryption/compoundFile.js +2 -1
- package/dist/docx/encryption/cryptoBytes.d.ts +2 -2
- package/dist/docx/encryption/cryptoBytes.js +3 -18
- package/dist/docx/encryption/encryptionInfo.d.ts +0 -5
- package/dist/docx/encryption/encryptionInfo.js +2 -1
- package/dist/docx/ensureParaIds.js +2 -1
- package/dist/docx/groupDrawingParser.js +28 -13
- package/dist/docx/listModelNormalization.d.ts +10 -0
- package/dist/docx/listModelNormalization.js +166 -0
- package/dist/docx/metadataPrivacy.js +3 -2
- package/dist/docx/metafileRaster.js +2 -1
- package/dist/docx/packageParts.js +6 -5
- package/dist/docx/paragraphParser.js +6 -4
- package/dist/docx/parser.js +5 -4
- package/dist/docx/preservedRunContent.js +11 -2
- package/dist/docx/removeHeaderFooterParts.js +8 -7
- package/dist/docx/replyToComment.js +3 -13
- package/dist/docx/rezip.js +88 -89
- package/dist/docx/runParser.js +12 -6
- package/dist/docx/runPropertyPresence.d.ts +11 -0
- package/dist/docx/runPropertyPresence.js +14 -0
- package/dist/docx/selectiveSave.js +15 -14
- package/dist/docx/serializer/runSerializer.js +3 -2
- package/dist/docx/serializer/tableSerializer.js +2 -1
- package/dist/docx/server/applyDocxXmlPatchProposal.js +2 -2
- package/dist/docx/server/boundedArchive.js +2 -1
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +2 -1
- package/dist/docx/server/inspectDocxPackage.js +5 -1
- package/dist/docx/server/validateDocxConformance.js +2 -1
- package/dist/docx/tableParser.js +7 -4
- package/dist/docx/tableRowPropertyPresence.d.ts +9 -0
- package/dist/docx/tableRowPropertyPresence.js +15 -0
- package/dist/docx/unzip.js +2 -1
- package/dist/docx/xmlEncoding.js +6 -5
- package/dist/fonts/fontsourceFaces.js +2 -1
- package/dist/fonts/sfnt/parse.js +6 -9
- package/dist/fonts/sfnt/subset.js +2 -1
- package/dist/fonts/sfnt/tables.js +2 -3
- package/dist/fonts/sfnt/woff.js +2 -1
- package/dist/i18n/messages/ar.gen.js +1 -0
- package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
- package/dist/i18n/messages/catalogs.gen.js +17 -0
- package/dist/i18n/messages/cs.gen.js +1 -0
- package/dist/i18n/messages/de.gen.js +1 -0
- package/dist/i18n/messages/en.gen.js +1 -0
- package/dist/i18n/messages/es.gen.js +1 -0
- package/dist/i18n/messages/et.gen.js +1 -0
- package/dist/i18n/messages/fr.gen.js +1 -0
- package/dist/i18n/messages/he.gen.js +1 -0
- package/dist/i18n/messages/hi.gen.js +1 -0
- package/dist/i18n/messages/hu.gen.js +1 -0
- package/dist/i18n/messages/lt.gen.js +1 -0
- package/dist/i18n/messages/lv.gen.js +1 -0
- package/dist/i18n/messages/messages.gen.d.ts +1 -0
- package/dist/i18n/messages/pl.gen.js +1 -0
- package/dist/i18n/messages/pt-BR.gen.js +1 -0
- package/dist/i18n/messages/sk.gen.js +1 -0
- package/dist/i18n/messages/tr.gen.js +1 -0
- package/dist/i18n/messages/zh-CN.gen.js +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/internal/revisionResolutionInline.d.ts +2 -1
- package/dist/internal/revisionResolutionInline.js +22 -1
- package/dist/internal/wholeStoryRevisionResolution.js +24 -7
- package/dist/layout-bridge/convert/endnoteLayout.d.ts +1 -1
- package/dist/layout-bridge/convert/endnoteLayout.js +1 -3
- package/dist/layout-bridge/convert/flowConversionShared.d.ts +2 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +4 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +13 -3
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +5 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +12 -15
- package/dist/layout-bridge/convert/paragraphRuns.js +2 -1
- package/dist/layout-engine/index.js +2 -2
- package/dist/layout-engine/measure/crossRunWords.js +12 -2
- package/dist/layout-engine/noteAreaFlow.d.ts +4 -4
- package/dist/layout-engine/noteAreaFlow.js +12 -6
- package/dist/layout-engine/types.d.ts +5 -2
- package/dist/layout-painter/index.d.ts +1 -1
- package/dist/managers/AutoSaveManager.js +2 -1
- package/dist/managers/EditorModeManager.d.ts +10 -1
- package/dist/managers/EditorModeManager.js +15 -1
- package/dist/managers/FindReplaceManager.d.ts +13 -6
- package/dist/managers/FindReplaceManager.js +15 -9
- package/dist/managers/TableSelectionManager.d.ts +1 -1
- package/dist/managers/autoSaveCodec.js +2 -6
- package/dist/managers/reviewDisplayMode.d.ts +8 -0
- package/dist/managers/reviewDisplayMode.js +15 -0
- package/dist/paged-layout/editorScrollRoot.d.ts +3 -1
- package/dist/paged-layout/editorScrollRoot.js +2 -2
- package/dist/paged-layout/pageText.d.ts +11 -2
- package/dist/paged-layout/pageText.js +8 -3
- package/dist/pdf/fonts.js +5 -14
- package/dist/pdf/images.js +4 -2
- package/dist/pdf/objects.d.ts +0 -11
- package/dist/pdf/objects.js +7 -9
- package/dist/pdf/writePdf.js +2 -1
- package/dist/prosemirror/commands/comments.js +51 -13
- package/dist/prosemirror/commentRangeInlineAtoms.d.ts +40 -0
- package/dist/prosemirror/commentRangeInlineAtoms.js +33 -0
- package/dist/prosemirror/containerFinalParagraph.d.ts +8 -8
- package/dist/prosemirror/containerFinalParagraph.js +8 -8
- package/dist/prosemirror/conversion/fromProseDoc.js +14 -8
- package/dist/prosemirror/conversion/toProseDoc.js +16 -23
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +16 -2
- package/dist/prosemirror/fieldRepresentation.js +4 -2
- package/dist/prosemirror/findReplaceSelection.d.ts +48 -2
- package/dist/prosemirror/findReplaceSelection.js +153 -38
- package/dist/prosemirror/inlineDisplayText.d.ts +10 -0
- package/dist/prosemirror/inlineDisplayText.js +29 -0
- package/dist/prosemirror/markupViewProjection.d.ts +10 -1
- package/dist/prosemirror/markupViewProjection.js +21 -3
- package/dist/prosemirror/plugins/transactionInvariants.js +6 -1
- package/dist/prosemirror/replacedAnnotations.d.ts +15 -1
- package/dist/prosemirror/replacedAnnotations.js +71 -12
- package/dist/prosemirror/runFormattingReconciliation.d.ts +3 -1
- package/dist/prosemirror/runFormattingReconciliation.js +10 -2
- package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +5 -7
- package/dist/prosemirror/runIdentityAcrossRevisions.js +66 -38
- package/dist/prosemirror/runPropertyChangeResolution.js +13 -1
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -1
- package/dist/prosemirror/trackedRunInlineAtoms.js +1 -1
- package/dist/utils/base64.d.ts +1 -31
- package/dist/utils/base64.js +19 -33
- package/dist/utils/clipboard.js +5 -4
- package/dist/utils/colorResolver.js +11 -2
- package/dist/utils/findMatchView.d.ts +32 -0
- package/dist/utils/findMatchView.js +406 -0
- package/dist/utils/findReplace.d.ts +67 -4
- package/dist/utils/findReplace.js +359 -37
- package/dist/utils/findSearchKey.d.ts +29 -0
- package/dist/utils/findSearchKey.js +34 -0
- package/dist/utils/replaceText.js +138 -94
- package/dist/utils/sliceText.d.ts +2 -0
- package/dist/utils/sliceText.js +2 -0
- package/dist/utils/textEncoding.d.ts +2 -0
- package/dist/utils/textEncoding.js +2 -0
- package/dist/utils/tiffConverter.d.ts +0 -9
- package/dist/utils/tiffConverter.js +2 -4
- package/package.json +51 -25
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
//#region src/docx/encryption/compoundFile.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Read-only MS-CFB compound file access for encrypted OOXML packages.
|
|
4
|
-
*
|
|
5
|
-
* Encrypted `.docx` files store `EncryptionInfo` and `EncryptedPackage` streams
|
|
6
|
-
* at the compound-file root. This reader implements only what MS-OFFCRYPTO needs.
|
|
7
|
-
*
|
|
8
|
-
* @see https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/
|
|
9
|
-
*/
|
|
10
2
|
type CompoundFileReader = {
|
|
11
3
|
readStream: (absolutePath: string) => Uint8Array | null;
|
|
12
4
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeText } from "../../utils/textEncoding.js";
|
|
1
2
|
import { joinBytes } from "./cryptoBytes.js";
|
|
2
3
|
//#region src/docx/encryption/compoundFile.ts
|
|
3
4
|
/**
|
|
@@ -39,7 +40,7 @@ const u64 = (view, offset) => {
|
|
|
39
40
|
return BigInt(view.getUint32(offset + 4, true)) << 32n | low;
|
|
40
41
|
};
|
|
41
42
|
const decodeName = (nameBytes) => {
|
|
42
|
-
const decoded =
|
|
43
|
+
const decoded = decodeText(nameBytes, { encoding: "utf-16le" });
|
|
43
44
|
let end = decoded.length;
|
|
44
45
|
while (end > 0 && decoded.charCodeAt(end - 1) === 0) end--;
|
|
45
46
|
return decoded.slice(0, end);
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
/** Byte helpers shared by MS-CFB and MS-OFFCRYPTO agile decryption. */
|
|
3
3
|
declare const joinBytes: (parts: readonly Uint8Array[]) => Uint8Array;
|
|
4
4
|
declare const writeUint32Le: (value: number) => Uint8Array;
|
|
5
|
-
declare const passwordToUtf16Le: (
|
|
5
|
+
declare const passwordToUtf16Le: (text: string) => Uint8Array<ArrayBuffer>;
|
|
6
6
|
declare const bytesEqual: (left: Uint8Array, right: Uint8Array) => boolean;
|
|
7
7
|
declare const toArrayBuffer: (bytes: Uint8Array) => ArrayBuffer;
|
|
8
|
-
declare const decodeBase64: (
|
|
8
|
+
declare const decodeBase64: (text: string) => Uint8Array<ArrayBuffer>;
|
|
9
9
|
declare const padToBlock: (bytes: Uint8Array, blockSize: number, padByte?: number) => Uint8Array;
|
|
10
10
|
//#endregion
|
|
11
11
|
export { bytesEqual, decodeBase64, joinBytes, padToBlock, passwordToUtf16Le, toArrayBuffer, writeUint32Le };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { base64ToBytes, encodeUtf16LE } from "../../utils/textEncoding.js";
|
|
1
2
|
//#region src/docx/encryption/cryptoBytes.ts
|
|
2
3
|
/** Byte helpers shared by MS-CFB and MS-OFFCRYPTO agile decryption. */
|
|
3
4
|
const joinBytes = (parts) => {
|
|
@@ -19,15 +20,7 @@ const writeUint32Le = (value) => {
|
|
|
19
20
|
out[3] = value >>> 24 & 255;
|
|
20
21
|
return out;
|
|
21
22
|
};
|
|
22
|
-
const passwordToUtf16Le =
|
|
23
|
-
const out = new Uint8Array(password.length * 2);
|
|
24
|
-
for (let i = 0; i < password.length; i++) {
|
|
25
|
-
const code = password.charCodeAt(i);
|
|
26
|
-
out[i * 2] = code & 255;
|
|
27
|
-
out[i * 2 + 1] = code >> 8;
|
|
28
|
-
}
|
|
29
|
-
return out;
|
|
30
|
-
};
|
|
23
|
+
const passwordToUtf16Le = encodeUtf16LE;
|
|
31
24
|
const bytesEqual = (left, right) => {
|
|
32
25
|
if (left.length !== right.length) return false;
|
|
33
26
|
let mismatch = 0;
|
|
@@ -38,15 +31,7 @@ const toArrayBuffer = (bytes) => {
|
|
|
38
31
|
if (bytes.buffer instanceof ArrayBuffer && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength) return bytes.buffer;
|
|
39
32
|
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
40
33
|
};
|
|
41
|
-
const decodeBase64 =
|
|
42
|
-
if (typeof atob === "function") {
|
|
43
|
-
const binary = atob(encoded);
|
|
44
|
-
const out = new Uint8Array(binary.length);
|
|
45
|
-
for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
|
|
46
|
-
return out;
|
|
47
|
-
}
|
|
48
|
-
return new Uint8Array(Buffer.from(encoded, "base64"));
|
|
49
|
-
};
|
|
34
|
+
const decodeBase64 = base64ToBytes;
|
|
50
35
|
const padToBlock = (bytes, blockSize, padByte = 54) => {
|
|
51
36
|
if (bytes.length >= blockSize) return bytes.subarray(0, blockSize);
|
|
52
37
|
const out = new Uint8Array(blockSize);
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
//#region src/docx/encryption/encryptionInfo.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Parse the EncryptionInfo stream for Agile Encryption (Office 2010+).
|
|
4
|
-
*
|
|
5
|
-
* @see https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/87020a34-e73f-4139-99bc-bbdf6cf6fa55
|
|
6
|
-
*/
|
|
7
2
|
type AgileKeyMaterial = {
|
|
8
3
|
packageSalt: Uint8Array;
|
|
9
4
|
packageKeyBits: number;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeText } from "../../utils/textEncoding.js";
|
|
1
2
|
import { decodeBase64 } from "./cryptoBytes.js";
|
|
2
3
|
import { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError } from "./errors.js";
|
|
3
4
|
//#region src/docx/encryption/encryptionInfo.ts
|
|
@@ -85,7 +86,7 @@ const requireSpinCount = (attrs, element) => {
|
|
|
85
86
|
return spinCount;
|
|
86
87
|
};
|
|
87
88
|
const parseAgileXml = (xmlBytes) => {
|
|
88
|
-
const xml =
|
|
89
|
+
const xml = decodeText(xmlBytes, { encoding: "utf-8" });
|
|
89
90
|
const keyDataTag = findElement(xml, "keyData");
|
|
90
91
|
const integrityTag = findElement(xml, "dataIntegrity");
|
|
91
92
|
const encryptedKeyTag = findElement(xml, "encryptedKey");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { deterministicHexId } from "../utils/hexId.js";
|
|
2
|
+
import { encodeUtf8 } from "../utils/textEncoding.js";
|
|
2
3
|
import { resolvePackageRelationshipTarget } from "./packageParts.js";
|
|
3
4
|
import { spliceXml } from "./selectiveXmlPatch.js";
|
|
4
5
|
import { loadDocxArchive } from "./server/boundedArchive.js";
|
|
@@ -390,7 +391,7 @@ const ensureParaIdsInternal = async (docx, options) => {
|
|
|
390
391
|
writeZipPart({
|
|
391
392
|
zip,
|
|
392
393
|
path: partPath,
|
|
393
|
-
data: content,
|
|
394
|
+
data: encodeUtf8(content),
|
|
394
395
|
options: {
|
|
395
396
|
compression: "DEFLATE",
|
|
396
397
|
compressionOptions: { level: 6 },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sliceText } from "../utils/sliceText.js";
|
|
1
2
|
import { emuToPixels } from "../utils/units.js";
|
|
2
3
|
import { collectGroupTextBoxes, readTransform } from "./drawingGroupChildren.js";
|
|
3
4
|
import { parseImage, resolveImageData } from "./imageParser.js";
|
|
@@ -15,6 +16,11 @@ const CROP_SCALE = 1e5;
|
|
|
15
16
|
const MAX_PATH_COMMANDS = 1e4;
|
|
16
17
|
const MAX_TEXT_CHARACTERS = 2e4;
|
|
17
18
|
const MAX_SVG_CHARACTERS = 1e6;
|
|
19
|
+
const NON_RENDERING_GROUP_CHILDREN = /* @__PURE__ */ new Set([
|
|
20
|
+
"cNvGrpSpPr",
|
|
21
|
+
"grpSpPr",
|
|
22
|
+
"extLst"
|
|
23
|
+
]);
|
|
18
24
|
const numericAttr = (element, name) => {
|
|
19
25
|
const direct = parseNumericAttribute(element, null, name);
|
|
20
26
|
if (direct !== void 0) return direct;
|
|
@@ -142,7 +148,11 @@ const renderTextBox = (wsp, unitScale) => {
|
|
|
142
148
|
const color = colorFrom(findAllDeep(wsp, "w", "color").at(0) ?? null, DEFAULT_TEXT_COLOR);
|
|
143
149
|
const fontSize = halfPoints * HALF_POINT_TO_EMU / unitScale;
|
|
144
150
|
const maxCharacters = Math.max(1, Math.floor(width / (fontSize * .38)));
|
|
145
|
-
const lines = paragraphs.flatMap((paragraph) => wrapLine(
|
|
151
|
+
const lines = paragraphs.flatMap((paragraph) => wrapLine(sliceText({
|
|
152
|
+
text: getTextContent(paragraph),
|
|
153
|
+
units: "codeUnits",
|
|
154
|
+
end: MAX_TEXT_CHARACTERS
|
|
155
|
+
}), maxCharacters)).map(escapeXmlText);
|
|
146
156
|
if (lines.length === 0) return "";
|
|
147
157
|
const lineHeight = fontSize * 1.15;
|
|
148
158
|
const svgFontSize = 1e3;
|
|
@@ -170,13 +180,14 @@ const renderPicture = (picture, index, rels, media) => {
|
|
|
170
180
|
const clipId = `group-picture-${index}`;
|
|
171
181
|
return `<defs><clipPath id="${clipId}"><rect x="${x}" y="${y}" width="${width}" height="${height}"/></clipPath></defs><g clip-path="url(#${clipId})">${image}</g>`;
|
|
172
182
|
};
|
|
173
|
-
const
|
|
174
|
-
const
|
|
183
|
+
const effectiveRootTransform = (group, width, height) => {
|
|
184
|
+
const authored = readTransform(findChildByLocalName(findChildByLocalName(group, "grpSpPr"), "xfrm"));
|
|
175
185
|
return {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
186
|
+
...authored,
|
|
187
|
+
width: authored.width > 0 ? authored.width : width,
|
|
188
|
+
height: authored.height > 0 ? authored.height : height,
|
|
189
|
+
childWidth: authored.childWidth > 0 ? authored.childWidth : width,
|
|
190
|
+
childHeight: authored.childHeight > 0 ? authored.childHeight : height
|
|
180
191
|
};
|
|
181
192
|
};
|
|
182
193
|
/**
|
|
@@ -221,20 +232,22 @@ const createSvg = (group, width, height, rels, media, hostedTextBoxes) => {
|
|
|
221
232
|
if (nested === null) return null;
|
|
222
233
|
piece = nested ? wrapTransformed(transform, `<g transform="${nestedGroupTransform(transform)}">${nested}</g>`) : "";
|
|
223
234
|
total -= nested.length;
|
|
224
|
-
} else continue;
|
|
235
|
+
} else if (NON_RENDERING_GROUP_CHILDREN.has(localName)) continue;
|
|
236
|
+
else return null;
|
|
225
237
|
content += piece;
|
|
226
238
|
total += piece.length;
|
|
227
239
|
if (total > MAX_SVG_CHARACTERS) return null;
|
|
228
240
|
}
|
|
229
241
|
return content;
|
|
230
242
|
};
|
|
231
|
-
const
|
|
243
|
+
const root = effectiveRootTransform(group, width, height);
|
|
232
244
|
const content = render(group, {
|
|
233
|
-
x: width /
|
|
234
|
-
y: height /
|
|
245
|
+
x: root.width / root.childWidth,
|
|
246
|
+
y: root.height / root.childHeight
|
|
235
247
|
}, 0);
|
|
236
248
|
if (content === null || !content && hostedTextBoxes.size === 0) return null;
|
|
237
|
-
|
|
249
|
+
const transformed = content ? wrapTransformed(root, `<g transform="${nestedGroupTransform(root)}">${content}</g>`) : "";
|
|
250
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" data-folio-preview="wpGroup" viewBox="0 0 ${width} ${height}" width="${emuToPixels(width)}" height="${emuToPixels(height)}">${transformed}</svg>`;
|
|
238
251
|
};
|
|
239
252
|
const groupElement = (drawing) => {
|
|
240
253
|
return findChildByLocalName(findAllDeep(drawing, "a", "graphicData").at(0) ?? null, "wgp");
|
|
@@ -268,7 +281,9 @@ const parseGroupDrawing = (drawing, previews, rels, media) => {
|
|
|
268
281
|
if (!group) return null;
|
|
269
282
|
const image = parseImage(drawing, void 0, void 0);
|
|
270
283
|
if (!image || image.size.width <= 0 || image.size.height <= 0) return null;
|
|
271
|
-
const
|
|
284
|
+
const rootTransform = effectiveRootTransform(group, image.size.width, image.size.height);
|
|
285
|
+
const rootTurnsChildren = rootTransform.rotation !== 0 || rootTransform.flipH || rootTransform.flipV;
|
|
286
|
+
const hosted = hostsTextBoxes(image) && !rootTurnsChildren ? collectGroupTextBoxes(group, image.size.width, image.size.height) : [];
|
|
272
287
|
const svg = createSvg(group, image.size.width, image.size.height, rels, media, new Set(hosted.map(({ wsp }) => wsp)));
|
|
273
288
|
if (!svg || svg.length > MAX_SVG_CHARACTERS) return null;
|
|
274
289
|
const { rId: _rId, src: _src, mimeType: _mimeType, filename: _filename, ...frame } = image;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { DocumentOp } from "@stll/docx-core/ops";
|
|
3
|
+
//#region src/docx/listModelNormalization.d.ts
|
|
4
|
+
type CanonicalListNormalization = {
|
|
5
|
+
document: document_d_exports.Document;
|
|
6
|
+
inverse: DocumentOp[];
|
|
7
|
+
};
|
|
8
|
+
declare const normalizeListModelRendering: (document: document_d_exports.Document) => CanonicalListNormalization;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { normalizeListModelRendering };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { effectiveParagraphNumberingReference, paragraphNumberingSourcesFromBoundary } from "../prosemirror/numberingAttr.js";
|
|
2
|
+
import { paragraphIndentationFromFormatting } from "../prosemirror/paragraphIndentation.js";
|
|
3
|
+
import { listIndentationProvenancePatch } from "../prosemirror/styles/resolvedStyleAttrs.js";
|
|
4
|
+
import { createStyleResolver } from "../prosemirror/styles/styleResolver.js";
|
|
5
|
+
import { canonicalJson } from "../utils/canonicalJson.js";
|
|
6
|
+
import { foldListNumberFields, planParagraphListNumberFold, unfoldedListNumberContent } from "./foldedListNumberFields.js";
|
|
7
|
+
import { computeListMarker } from "./listMarkerComputation.js";
|
|
8
|
+
import { listRenderingDefinitionsMatch } from "./listRendering.js";
|
|
9
|
+
import { computeListRendering, getCachedNumberingMap } from "./numberingParser.js";
|
|
10
|
+
import { assignParagraphIndentationProjection, cloneParagraphWithPropertySource, paragraphFormattingWithAuthoredIndentation, sameParagraphIndentationProjection } from "./paragraphPropertySource.js";
|
|
11
|
+
import { panic } from "better-result";
|
|
12
|
+
import { DOCUMENT_OP_TYPES, EMPTY_PROPERTY_SETS, OP_STORIES, registerParagraphPropertySnapshot, withBodyContent } from "@stll/docx-core/ops";
|
|
13
|
+
//#region src/docx/listModelNormalization.ts
|
|
14
|
+
/** Canonical edits retain authored fields; list rendering follows the current numbering. */
|
|
15
|
+
const normalizeListModelRendering = (document) => {
|
|
16
|
+
const inverse = [];
|
|
17
|
+
const styles = createStyleResolver(document.package.styles);
|
|
18
|
+
const numbering = document.package.numbering === void 0 ? null : getCachedNumberingMap(document.package.numbering);
|
|
19
|
+
const normalizeBody = (body, story) => {
|
|
20
|
+
const counters = {
|
|
21
|
+
numbering,
|
|
22
|
+
listCounters: /* @__PURE__ */ new Map(),
|
|
23
|
+
abstractCounters: /* @__PURE__ */ new Map(),
|
|
24
|
+
restartedNumIds: /* @__PURE__ */ new Set(),
|
|
25
|
+
previousList: {
|
|
26
|
+
abstractNumId: null,
|
|
27
|
+
fromStyle: false,
|
|
28
|
+
numId: null
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const content = body.content.map((paragraph) => {
|
|
32
|
+
if (paragraph.type !== "paragraph") return paragraph;
|
|
33
|
+
registerParagraphPropertySnapshot({
|
|
34
|
+
paragraph,
|
|
35
|
+
snapshot: paragraphFormattingWithAuthoredIndentation(paragraph)
|
|
36
|
+
});
|
|
37
|
+
const formatting = paragraph.formatting;
|
|
38
|
+
const next = cloneParagraphWithPropertySource(paragraph, {});
|
|
39
|
+
const styleNumbering = formatting?.styleId ? styles.resolveParagraphStyle(formatting.styleId).paragraphFormatting?.numPr : void 0;
|
|
40
|
+
if (styleNumbering !== void 0 && (formatting?.numPr === void 0 || formatting.numPr.kind === "levelOnly")) next.formatting = {
|
|
41
|
+
...formatting,
|
|
42
|
+
numPrFromStyle: styleNumbering
|
|
43
|
+
};
|
|
44
|
+
if (formatting?.numPrFromStyle !== void 0 && (formatting.styleId !== void 0 && styleNumbering === void 0 || formatting.numPr?.kind === "reference" || formatting.numPr?.kind === "none")) {
|
|
45
|
+
next.formatting = { ...formatting };
|
|
46
|
+
delete next.formatting.numPrFromStyle;
|
|
47
|
+
}
|
|
48
|
+
if (canonicalJson(next.formatting?.numPrFromStyle) !== canonicalJson(formatting?.numPrFromStyle)) inverse.unshift({
|
|
49
|
+
type: DOCUMENT_OP_TYPES.SET_PARAGRAPH_PROPS,
|
|
50
|
+
story,
|
|
51
|
+
blockId: paragraph.paraId ?? panic("List normalization requires paragraph identities."),
|
|
52
|
+
patch: { numPrFromStyle: formatting?.numPrFromStyle ?? null },
|
|
53
|
+
whenEmpty: formatting === void 0 ? EMPTY_PROPERTY_SETS.OMIT : EMPTY_PROPERTY_SETS.KEEP
|
|
54
|
+
});
|
|
55
|
+
const numPr = effectiveParagraphNumberingReference(paragraphNumberingSourcesFromBoundary({
|
|
56
|
+
numPr: next.formatting?.numPr,
|
|
57
|
+
numPrFromStyle: next.formatting?.numPrFromStyle
|
|
58
|
+
}));
|
|
59
|
+
if (paragraph.listRendering === void 0 && formatting?.numPrFromStyle === void 0 && next.formatting?.numPrFromStyle === void 0 && (numPr?.kind !== "reference" || numbering === null)) {
|
|
60
|
+
computeListMarker(paragraph, counters);
|
|
61
|
+
return paragraph;
|
|
62
|
+
}
|
|
63
|
+
const definition = numPr?.kind === "reference" && numbering !== null ? computeListRendering(numPr, numbering) : null;
|
|
64
|
+
const cached = paragraph.listRendering;
|
|
65
|
+
const rendering = definition !== null && cached !== void 0 && listRenderingDefinitionsMatch(cached, definition) ? { ...cached } : definition;
|
|
66
|
+
const authoredFormatting = paragraphFormattingWithAuthoredIndentation(next);
|
|
67
|
+
assignParagraphIndentationProjection({
|
|
68
|
+
paragraph: next,
|
|
69
|
+
authored: authoredFormatting,
|
|
70
|
+
inherited: paragraphIndentationFromFormatting(listIndentationProvenancePatch({
|
|
71
|
+
direct: paragraphIndentationFromFormatting(authoredFormatting),
|
|
72
|
+
styleFormatting: styles.resolveParagraphStyle(next.formatting?.styleId).paragraphFormatting,
|
|
73
|
+
numberingSource: next.formatting?.numPr?.kind === "reference" || next.formatting?.numPrFromStyle === void 0 ? "paragraph" : "style",
|
|
74
|
+
numPr: numPr?.kind === "reference" ? {
|
|
75
|
+
numId: numPr.numId,
|
|
76
|
+
ilvl: numPr.ilvl ?? 0
|
|
77
|
+
} : void 0,
|
|
78
|
+
numbering
|
|
79
|
+
})._resolvedFormatting) ?? {}
|
|
80
|
+
});
|
|
81
|
+
if (canonicalJson(paragraphIndentationFromFormatting(next.formatting)) !== canonicalJson(paragraphIndentationFromFormatting(formatting))) inverse.unshift({
|
|
82
|
+
type: DOCUMENT_OP_TYPES.SET_PARAGRAPH_PROPS,
|
|
83
|
+
story,
|
|
84
|
+
blockId: paragraph.paraId ?? panic("Canonical numbering normalization requires paragraph identities."),
|
|
85
|
+
patch: {
|
|
86
|
+
indentLeft: formatting?.indentLeft ?? null,
|
|
87
|
+
indentRight: formatting?.indentRight ?? null,
|
|
88
|
+
indentFirstLine: formatting?.indentFirstLine ?? null,
|
|
89
|
+
hangingIndent: formatting?.hangingIndent ?? null
|
|
90
|
+
},
|
|
91
|
+
whenEmpty: formatting === void 0 ? EMPTY_PROPERTY_SETS.OMIT : EMPTY_PROPERTY_SETS.KEEP
|
|
92
|
+
});
|
|
93
|
+
if (rendering === null) delete next.listRendering;
|
|
94
|
+
else {
|
|
95
|
+
const fields = foldListNumberFields(paragraph.content.map(unfoldedListNumberContent), () => void 0).fieldCount;
|
|
96
|
+
if (fields === 0) delete rendering.implicitChildLevelAdvances;
|
|
97
|
+
else rendering.implicitChildLevelAdvances = fields;
|
|
98
|
+
const template = rendering.markerTemplate ?? rendering.marker;
|
|
99
|
+
const fold = planParagraphListNumberFold(paragraph.content, !rendering.isBullet && template !== "" && !template.includes(" "));
|
|
100
|
+
if (fold.suffix === void 0) delete rendering.foldedMarkerSuffix;
|
|
101
|
+
else rendering.foldedMarkerSuffix = fold.suffix;
|
|
102
|
+
const child = numbering?.getLevel(rendering.numId, rendering.level + 1)?.pPr;
|
|
103
|
+
if (fold.suffix !== void 0 && child?.hangingIndent === true && child.indentFirstLine !== void 0 && child.indentFirstLine < 0) rendering.markerSecondSlotOffsetTwips = -child.indentFirstLine;
|
|
104
|
+
else delete rendering.markerSecondSlotOffsetTwips;
|
|
105
|
+
rendering.marker = rendering.foldedMarkerSuffix !== void 0 && !template.includes(" ") ? `${template}\t${rendering.foldedMarkerSuffix}` : template;
|
|
106
|
+
next.listRendering = rendering;
|
|
107
|
+
}
|
|
108
|
+
computeListMarker(next, counters);
|
|
109
|
+
registerParagraphPropertySnapshot({
|
|
110
|
+
paragraph: next,
|
|
111
|
+
snapshot: authoredFormatting
|
|
112
|
+
});
|
|
113
|
+
if (sameParagraphIndentationProjection({
|
|
114
|
+
left: next,
|
|
115
|
+
right: paragraph
|
|
116
|
+
}) && canonicalJson(next.formatting) === canonicalJson(formatting) && canonicalJson(next.listRendering) === canonicalJson(cached)) return paragraph;
|
|
117
|
+
return next;
|
|
118
|
+
});
|
|
119
|
+
return content.every((paragraph, index) => paragraph === body.content[index]) ? body : {
|
|
120
|
+
...body,
|
|
121
|
+
...withBodyContent(body, content)
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
const pkg = document.package;
|
|
125
|
+
const body = normalizeBody(pkg.document, OP_STORIES.MAIN);
|
|
126
|
+
const normalizeParts = (parts, kind) => {
|
|
127
|
+
if (parts === void 0) return parts;
|
|
128
|
+
const normalized = new Map([...parts].map(([id, part]) => [id, normalizeBody(part, {
|
|
129
|
+
kind,
|
|
130
|
+
rId: id
|
|
131
|
+
})]));
|
|
132
|
+
return [...normalized].every(([id, part]) => part === parts.get(id)) ? parts : normalized;
|
|
133
|
+
};
|
|
134
|
+
const normalizeNotes = (notes, kind) => {
|
|
135
|
+
if (notes === void 0) return notes;
|
|
136
|
+
const normalized = notes.map((note) => normalizeBody(note, {
|
|
137
|
+
kind,
|
|
138
|
+
id: note.id
|
|
139
|
+
}));
|
|
140
|
+
return normalized.every((note, index) => note === notes[index]) ? notes : normalized;
|
|
141
|
+
};
|
|
142
|
+
const headers = normalizeParts(pkg.headers, "header");
|
|
143
|
+
const footers = normalizeParts(pkg.footers, "footer");
|
|
144
|
+
const footnotes = normalizeNotes(pkg.footnotes, "footnote");
|
|
145
|
+
const endnotes = normalizeNotes(pkg.endnotes, "endnote");
|
|
146
|
+
if (body === pkg.document && headers === pkg.headers && footers === pkg.footers && footnotes === pkg.footnotes && endnotes === pkg.endnotes) return {
|
|
147
|
+
document,
|
|
148
|
+
inverse
|
|
149
|
+
};
|
|
150
|
+
return {
|
|
151
|
+
inverse,
|
|
152
|
+
document: {
|
|
153
|
+
...document,
|
|
154
|
+
package: {
|
|
155
|
+
...pkg,
|
|
156
|
+
document: body,
|
|
157
|
+
...headers === void 0 ? {} : { headers },
|
|
158
|
+
...footers === void 0 ? {} : { footers },
|
|
159
|
+
...footnotes === void 0 ? {} : { footnotes },
|
|
160
|
+
...endnotes === void 0 ? {} : { endnotes }
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
//#endregion
|
|
166
|
+
export { normalizeListModelRendering };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeText, encodeUtf8 } from "../utils/textEncoding.js";
|
|
1
2
|
import { countCentralDirectoryRecords, createInflationBudget, inflateEntryWithinLimits } from "./archiveInflation.js";
|
|
2
3
|
import { elementToXml, getLocalName, parseXmlDocument } from "./xmlParser.js";
|
|
3
4
|
import { TaggedError } from "better-result";
|
|
@@ -131,7 +132,7 @@ const readCoreProperties = async (entry) => {
|
|
|
131
132
|
message: "Document privacy core properties exceeded the part-size limit",
|
|
132
133
|
reason: "core-properties-too-large"
|
|
133
134
|
});
|
|
134
|
-
return
|
|
135
|
+
return decodeText(result.bytes, { ignoreBOM: true });
|
|
135
136
|
};
|
|
136
137
|
/** Rewrite selected package metadata fields without changing other package parts. */
|
|
137
138
|
const rewriteDocxMetadataPrivacy = async (buffer, { transforms }) => {
|
|
@@ -156,7 +157,7 @@ const rewriteDocxMetadataPrivacy = async (buffer, { transforms }) => {
|
|
|
156
157
|
writeZipPart({
|
|
157
158
|
zip,
|
|
158
159
|
path: coreProperties.name,
|
|
159
|
-
data: rewritten.xml
|
|
160
|
+
data: encodeUtf8(rewritten.xml)
|
|
160
161
|
});
|
|
161
162
|
return {
|
|
162
163
|
buffer: await zip.generateAsync({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeText } from "../utils/textEncoding.js";
|
|
1
2
|
//#region src/docx/metafileRaster.ts
|
|
2
3
|
function indexOfBytes(haystack, needle, from = 0) {
|
|
3
4
|
outer: for (let i = from; i + needle.length <= haystack.length; i++) {
|
|
@@ -25,7 +26,7 @@ function extractPng(bytes) {
|
|
|
25
26
|
const b2 = bytes[off + 2];
|
|
26
27
|
const b3 = bytes[off + 3];
|
|
27
28
|
const len = b0 << 24 | b1 << 16 | b2 << 8 | b3;
|
|
28
|
-
const type =
|
|
29
|
+
const type = decodeText(bytes.subarray(off + 4, off + 8), { encoding: "binary" });
|
|
29
30
|
const next = off + 12 + (len >>> 0);
|
|
30
31
|
if (type === "IEND") return bytes.slice(start, off + 12);
|
|
31
32
|
if (next <= off || next > bytes.length) break;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeText, encodeUtf8 } from "../utils/textEncoding.js";
|
|
1
2
|
import { Result } from "better-result";
|
|
2
3
|
import { writeZipPart } from "@stll/docx-core/zip";
|
|
3
4
|
//#region src/docx/packageParts.ts
|
|
@@ -124,7 +125,7 @@ const readRelationshipParts = async (zip) => {
|
|
|
124
125
|
if (file.dir || !normalizePartPath(path).endsWith(".rels")) continue;
|
|
125
126
|
parts.push({
|
|
126
127
|
path,
|
|
127
|
-
xml: await file.async("
|
|
128
|
+
xml: await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }))
|
|
128
129
|
});
|
|
129
130
|
}
|
|
130
131
|
return parts;
|
|
@@ -133,7 +134,7 @@ const writePart = ({ zip, path, xml, compressionLevel }) => {
|
|
|
133
134
|
writeZipPart({
|
|
134
135
|
zip,
|
|
135
136
|
path,
|
|
136
|
-
data: xml,
|
|
137
|
+
data: encodeUtf8(xml),
|
|
137
138
|
options: {
|
|
138
139
|
compression: "DEFLATE",
|
|
139
140
|
compressionOptions: { level: compressionLevel }
|
|
@@ -186,7 +187,7 @@ const reconcilePackageReferences = async (zip, compressionLevel) => {
|
|
|
186
187
|
if (!file) continue;
|
|
187
188
|
const idPattern = [...ids].map((id) => escapeRegExp(id)).join("|");
|
|
188
189
|
const referencePattern = new RegExp(`\\s+r:[A-Za-z]+\\s*=\\s*(?<quote>["'])(?<id>${idPattern})\\k<quote>`, "gu");
|
|
189
|
-
const xml = await file.async("
|
|
190
|
+
const xml = await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
190
191
|
const removedIds = /* @__PURE__ */ new Set();
|
|
191
192
|
const scrubbed = xml.replaceAll(referencePattern, (...args) => {
|
|
192
193
|
const groups = args.at(-1);
|
|
@@ -206,7 +207,7 @@ const reconcilePackageReferences = async (zip, compressionLevel) => {
|
|
|
206
207
|
const contentTypes = zip.file(CONTENT_TYPES_PATTERN)[0];
|
|
207
208
|
if (!contentTypes) return repair;
|
|
208
209
|
let droppedOverride = false;
|
|
209
|
-
const prunedContentTypes = (await contentTypes.async("
|
|
210
|
+
const prunedContentTypes = (await contentTypes.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }))).replaceAll(CONTENT_TYPE_ELEMENT, (element) => {
|
|
210
211
|
const partName = PART_NAME_ATTRIBUTE.exec(element)?.groups?.["value"];
|
|
211
212
|
if (partName === void 0 || isPresent(present, partName)) return element;
|
|
212
213
|
repair.danglingOverrides.push(normalizePartPath(decodePartName(partName) ?? partName));
|
|
@@ -256,7 +257,7 @@ const checkPackageIntegrity = async (zip) => {
|
|
|
256
257
|
}
|
|
257
258
|
for (const [path, file] of Object.entries(zip.files)) {
|
|
258
259
|
if (file.dir || !normalizePartPath(path).endsWith(".xml")) continue;
|
|
259
|
-
const xml = await file.async("
|
|
260
|
+
const xml = await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
260
261
|
const ids = idsByOwningPart.get(path);
|
|
261
262
|
for (const match of xml.matchAll(REFERENCE_ATTRIBUTE)) {
|
|
262
263
|
const id = match.groups?.["id"];
|
|
@@ -52,16 +52,18 @@ function extractMathText(el) {
|
|
|
52
52
|
* Capture the authored paragraph properties separately from structural and
|
|
53
53
|
* revision children, which have their own model fields and lifecycles.
|
|
54
54
|
*/
|
|
55
|
-
const captureParagraphPropertySource = (pPr) => captureVerbatimXml(cloneElement(pPr, { elements: (pPr.elements ?? []).flatMap((child) => {
|
|
55
|
+
const captureParagraphPropertySource = (pPr, paragraph) => captureVerbatimXml(cloneElement(pPr, { elements: (pPr.elements ?? []).flatMap((child) => {
|
|
56
56
|
if (child.type !== "element") return [child];
|
|
57
57
|
const localName = getLocalName(child.name);
|
|
58
58
|
if (WORDPROCESSINGML_NAMESPACE_URIS.has(child.namespaceUri ?? "") && (localName === "sectPr" || localName === "pPrChange")) return [];
|
|
59
59
|
if (localName !== "rPr" || !WORDPROCESSINGML_NAMESPACE_URIS.has(child.namespaceUri ?? "")) return [child];
|
|
60
|
-
|
|
60
|
+
const elements = (child.elements ?? []).filter((runProperty) => {
|
|
61
61
|
if (runProperty.type !== "element") return true;
|
|
62
62
|
const runPropertyName = getLocalName(runProperty.name);
|
|
63
63
|
return !WORDPROCESSINGML_NAMESPACE_URIS.has(runProperty.namespaceUri ?? "") || !PARAGRAPH_MARK_CHANGE_KINDS.some((kind) => kind === runPropertyName);
|
|
64
|
-
})
|
|
64
|
+
});
|
|
65
|
+
if (paragraph.formatting?.runProperties === void 0 && elements.every((element) => element.type === "text" && (element.text === void 0 || typeof element.text === "string" && element.text.trim() === ""))) return [];
|
|
66
|
+
return [cloneElement(child, { elements })];
|
|
65
67
|
}) }));
|
|
66
68
|
const RENDERED_BREAK_INLINE_WRAPPERS = /* @__PURE__ */ new Set([
|
|
67
69
|
"hyperlink",
|
|
@@ -1328,7 +1330,7 @@ function parseParagraph(node, styles, theme, numbering, rels = null, media = nul
|
|
|
1328
1330
|
}
|
|
1329
1331
|
}
|
|
1330
1332
|
}
|
|
1331
|
-
if (pPr) assignParagraphPropertySource(paragraph, captureParagraphPropertySource(pPr));
|
|
1333
|
+
if (pPr) assignParagraphPropertySource(paragraph, captureParagraphPropertySource(pPr, paragraph));
|
|
1332
1334
|
return paragraph;
|
|
1333
1335
|
}
|
|
1334
1336
|
/**
|
package/dist/docx/parser.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { toArrayBuffer } from "../utils/docxInput.js";
|
|
2
2
|
import { loadFontsWithMapping } from "../utils/fontLoader.js";
|
|
3
|
+
import { decodeText, encodeUtf8 } from "../utils/textEncoding.js";
|
|
3
4
|
import { MAX_PACKAGE_TIFF_PIXELS, convertTiffToPngDataUrl, isTiffMimeType } from "../utils/tiffConverter.js";
|
|
4
5
|
import { ALT_CHUNK_PLAIN_TEXT_LIMIT_BYTES, getAltChunkRelationshipId, isAltChunkMarkup } from "./altChunk.js";
|
|
5
6
|
import { normalizeBookmarkIdsInXmlParts } from "./bookmarkIdNormalization.js";
|
|
@@ -122,7 +123,7 @@ const extractAltChunkPlainText = async (xml, context) => {
|
|
|
122
123
|
if (bytes.byteLength > context.budget.remainingBytes) return;
|
|
123
124
|
context.budget.remainingBytes -= bytes.byteLength;
|
|
124
125
|
try {
|
|
125
|
-
return
|
|
126
|
+
return decodeText(bytes, { fatal: true });
|
|
126
127
|
} catch {
|
|
127
128
|
return;
|
|
128
129
|
}
|
|
@@ -560,7 +561,7 @@ async function buildMediaMap(raw, rels) {
|
|
|
560
561
|
}
|
|
561
562
|
const emfSvg = isReferenced && mimeType === EMF_MIME_TYPE && remainingEmfPreviewBytes > 0 ? renderEmfSvg(data) : null;
|
|
562
563
|
if (emfSvg) {
|
|
563
|
-
const svgBytes =
|
|
564
|
+
const svgBytes = encodeUtf8(emfSvg);
|
|
564
565
|
if (svgBytes.byteLength <= remainingEmfPreviewBytes) {
|
|
565
566
|
remainingEmfPreviewBytes -= svgBytes.byteLength;
|
|
566
567
|
const mediaFile = {
|
|
@@ -589,8 +590,8 @@ async function buildMediaMap(raw, rels) {
|
|
|
589
590
|
if (normalizedPath !== path) media.set(normalizedPath, mediaFile);
|
|
590
591
|
}
|
|
591
592
|
for (const [path, xml] of raw.allXml.entries()) {
|
|
592
|
-
if (!/^word\/diagrams\/[^/]+\.xml$/iu.test(path) ||
|
|
593
|
-
const data =
|
|
593
|
+
if (!/^word\/diagrams\/[^/]+\.xml$/iu.test(path) || encodeUtf8(xml).byteLength > FOLIO_XML_RESOURCE_LIMITS.maxBytes) continue;
|
|
594
|
+
const data = encodeUtf8(xml).buffer;
|
|
594
595
|
const file = {
|
|
595
596
|
path,
|
|
596
597
|
filename: path.split("/").pop() ?? path,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sliceText } from "../utils/sliceText.js";
|
|
1
2
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
2
3
|
import { findChildrenByLocalName, getChildElements, getLocalName, getTextContent } from "./xmlParser.js";
|
|
3
4
|
//#region src/docx/preservedRunContent.ts
|
|
@@ -31,7 +32,11 @@ const preservedRunChildText = (element) => {
|
|
|
31
32
|
if (sources === void 0) return "";
|
|
32
33
|
const parts = [];
|
|
33
34
|
for (const localName of sources) for (const source of findChildrenByLocalName(element, localName)) collectTextContent(source, parts);
|
|
34
|
-
return
|
|
35
|
+
return sliceText({
|
|
36
|
+
text: parts.join(""),
|
|
37
|
+
units: "codeUnits",
|
|
38
|
+
end: MAX_PRESERVED_TEXT_LENGTH
|
|
39
|
+
});
|
|
35
40
|
};
|
|
36
41
|
/**
|
|
37
42
|
* Capture one unmodelled run child.
|
|
@@ -90,7 +95,11 @@ const preserveInlineChild = (element) => {
|
|
|
90
95
|
return {
|
|
91
96
|
type: "preservedInline",
|
|
92
97
|
xml: captureVerbatimXml(element),
|
|
93
|
-
text:
|
|
98
|
+
text: sliceText({
|
|
99
|
+
text: parts.join(""),
|
|
100
|
+
units: "codeUnits",
|
|
101
|
+
end: MAX_PRESERVED_TEXT_LENGTH
|
|
102
|
+
})
|
|
94
103
|
};
|
|
95
104
|
};
|
|
96
105
|
//#endregion
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeText, encodeUtf8 } from "../utils/textEncoding.js";
|
|
1
2
|
import { isUnsafePackagePath } from "./packageParts.js";
|
|
2
3
|
import { parseRelationships, resolveRelativePath } from "./relsParser.js";
|
|
3
4
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
@@ -18,7 +19,7 @@ const removeResolvedHeaderFooterParts = async ({ document, zip, removedReference
|
|
|
18
19
|
if (removedReferences.length === 0) return;
|
|
19
20
|
const relsFile = zip.file(DOCUMENT_RELS_PATH);
|
|
20
21
|
if (!relsFile) return panic("A resolved header/footer part has no document relationships");
|
|
21
|
-
const relsXml = await relsFile.async("
|
|
22
|
+
const relsXml = await relsFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
22
23
|
const relationships = parseRelationships(relsXml);
|
|
23
24
|
const removedIds = /* @__PURE__ */ new Set();
|
|
24
25
|
const candidates = /* @__PURE__ */ new Set();
|
|
@@ -39,7 +40,7 @@ const removeResolvedHeaderFooterParts = async ({ document, zip, removedReference
|
|
|
39
40
|
}
|
|
40
41
|
for (const file of Object.values(zip.files)) {
|
|
41
42
|
if (file.dir || !file.name.endsWith(".rels") || file.name === DOCUMENT_RELS_PATH) continue;
|
|
42
|
-
const otherRelationships = parseRelationships(await file.async("
|
|
43
|
+
const otherRelationships = parseRelationships(await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })));
|
|
43
44
|
for (const relationship of otherRelationships.values()) if (relationship.targetMode !== "External") candidates.delete(resolveRelativePath(file.name, relationship.target));
|
|
44
45
|
}
|
|
45
46
|
const retiredMedia = /* @__PURE__ */ new Set();
|
|
@@ -48,7 +49,7 @@ const removeResolvedHeaderFooterParts = async ({ document, zip, removedReference
|
|
|
48
49
|
const relsPath = `${path.slice(0, slash + 1)}_rels/${path.slice(slash + 1)}.rels`;
|
|
49
50
|
const partRels = zip.file(relsPath);
|
|
50
51
|
if (!partRels) continue;
|
|
51
|
-
for (const relationship of parseRelationships(await partRels.async("
|
|
52
|
+
for (const relationship of parseRelationships(await partRels.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }))).values()) {
|
|
52
53
|
if (!officeRelationshipHasType(relationship.type, "image") || relationship.targetMode === "External") continue;
|
|
53
54
|
const mediaPath = resolveRelativePath(relsPath, relationship.target);
|
|
54
55
|
if (!isUnsafePackagePath(mediaPath) && mediaPath.startsWith("word/media/")) retiredMedia.add(mediaPath);
|
|
@@ -58,7 +59,7 @@ const removeResolvedHeaderFooterParts = async ({ document, zip, removedReference
|
|
|
58
59
|
writeZipPart({
|
|
59
60
|
zip,
|
|
60
61
|
path: DOCUMENT_RELS_PATH,
|
|
61
|
-
data: withoutChildren(relsXml, (child) => getNamespaceUri(child) === OOXML_NS.pr && getLocalName(child.name) === "Relationship" && removedIds.has(getAttribute(child, null, "Id") ?? "")),
|
|
62
|
+
data: encodeUtf8(withoutChildren(relsXml, (child) => getNamespaceUri(child) === OOXML_NS.pr && getLocalName(child.name) === "Relationship" && removedIds.has(getAttribute(child, null, "Id") ?? ""))),
|
|
62
63
|
options: {
|
|
63
64
|
compression: "DEFLATE",
|
|
64
65
|
compressionOptions
|
|
@@ -75,7 +76,7 @@ const removeResolvedHeaderFooterParts = async ({ document, zip, removedReference
|
|
|
75
76
|
}
|
|
76
77
|
for (const file of Object.values(zip.files)) {
|
|
77
78
|
if (file.dir || !file.name.endsWith(".rels")) continue;
|
|
78
|
-
for (const relationship of parseRelationships(await file.async("
|
|
79
|
+
for (const relationship of parseRelationships(await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }))).values()) if (relationship.targetMode !== "External") retiredMedia.delete(resolveRelativePath(file.name, relationship.target));
|
|
79
80
|
}
|
|
80
81
|
for (const path of retiredMedia) {
|
|
81
82
|
zip.remove(path);
|
|
@@ -83,11 +84,11 @@ const removeResolvedHeaderFooterParts = async ({ document, zip, removedReference
|
|
|
83
84
|
}
|
|
84
85
|
const contentTypes = zip.file("[Content_Types].xml");
|
|
85
86
|
if (!contentTypes) return panic("The package has no content types");
|
|
86
|
-
const contentTypesXml = await contentTypes.async("
|
|
87
|
+
const contentTypesXml = await contentTypes.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
87
88
|
writeZipPart({
|
|
88
89
|
zip,
|
|
89
90
|
path: "[Content_Types].xml",
|
|
90
|
-
data: withoutChildren(contentTypesXml, (child) => getNamespaceUri(child) === OOXML_NS.ct && getLocalName(child.name) === "Override" && candidates.has((getAttribute(child, null, "PartName") ?? "").replace(/^\//u, ""))),
|
|
91
|
+
data: encodeUtf8(withoutChildren(contentTypesXml, (child) => getNamespaceUri(child) === OOXML_NS.ct && getLocalName(child.name) === "Override" && candidates.has((getAttribute(child, null, "PartName") ?? "").replace(/^\//u, "")))),
|
|
91
92
|
options: {
|
|
92
93
|
compression: "DEFLATE",
|
|
93
94
|
compressionOptions
|