@stll/folio-core 0.64.1 → 0.65.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/headless.js +3 -3
- package/dist/compare/reproducible-package.d.ts +5 -0
- package/dist/compare/reproducible-package.js +10 -23
- package/dist/docx/archiveInflation.d.ts +8 -103
- package/dist/docx/archiveInflation.js +8 -145
- package/dist/docx/canonicalStoryRepack.js +2 -2
- package/dist/docx/ensureParaIds.js +7 -22
- package/dist/docx/metadataPrivacy.js +38 -48
- package/dist/docx/packageParts.d.ts +4 -4
- package/dist/docx/packageParts.js +17 -24
- package/dist/docx/parser.js +4 -5
- package/dist/docx/removeHeaderFooterParts.d.ts +2 -2
- package/dist/docx/removeHeaderFooterParts.js +19 -36
- package/dist/docx/rezip.d.ts +7 -7
- package/dist/docx/rezip.js +143 -531
- package/dist/docx/selectiveSave.js +37 -41
- package/dist/docx/selectiveSaveFlags.d.ts +1 -1
- package/dist/docx/selectiveSaveTripwire.js +5 -11
- package/dist/docx/server/applyDocxXmlPatchProposal.js +20 -41
- package/dist/docx/server/boundedArchive.d.ts +1 -2
- package/dist/docx/server/boundedArchive.js +77 -98
- package/dist/docx/server/inspectDocxPackage.d.ts +1 -1
- package/dist/docx/server/inspectDocxPackage.js +1 -1
- package/dist/docx/unzip.d.ts +2 -3
- package/dist/docx/unzip.js +126 -154
- package/package.json +3 -4
package/dist/docx/rezip.js
CHANGED
|
@@ -45,8 +45,7 @@ import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, WORDPROCESSI
|
|
|
45
45
|
import { Result, TaggedError, panic } from "better-result";
|
|
46
46
|
import { assertValidOoxmlNumericIds, escapeXmlAttribute, escapeXmlText, validateDocxPackage } from "@stll/docx-core";
|
|
47
47
|
import { mintRelationshipId, numberingInstanceResolves } from "@stll/docx-core/model";
|
|
48
|
-
import
|
|
49
|
-
import { writeZipPart } from "@stll/docx-core/zip";
|
|
48
|
+
import { ZipArchive } from "@stll/docx-core/zip";
|
|
50
49
|
// src/docx/rezip.ts
|
|
51
50
|
const DocxPackageFidelityErrorBase = TaggedError("DocxPackageFidelityError");
|
|
52
51
|
var DocxPackageFidelityError = class extends DocxPackageFidelityErrorBase {
|
|
@@ -172,7 +171,7 @@ function assertDocumentPackageFidelity({ originalDocumentFacts, serializedDocume
|
|
|
172
171
|
async function serializeCommentsToZip(doc, zip, compressionLevel) {
|
|
173
172
|
const comments = doc.package.document.comments ?? [];
|
|
174
173
|
const sourceCommentsFile = findZipEntryCaseInsensitive(zip, "word/comments.xml");
|
|
175
|
-
const sourceCommentsXml = sourceCommentsFile ? await sourceCommentsFile.
|
|
174
|
+
const sourceCommentsXml = sourceCommentsFile ? await sourceCommentsFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })) : void 0;
|
|
176
175
|
if (sourceCommentsXml !== void 0 && canReplayCommentsPart(doc, sourceCommentsXml)) return;
|
|
177
176
|
if (comments.length === 0) {
|
|
178
177
|
const ownsCommentsPart = hasOwnedCommentsPart(doc);
|
|
@@ -180,15 +179,7 @@ async function serializeCommentsToZip(doc, zip, compressionLevel) {
|
|
|
180
179
|
}
|
|
181
180
|
const plan = planCommentParts(comments);
|
|
182
181
|
const commentsXml = serializeComments(plan, sourceCommentsXml === void 0 ? void 0 : readRootNamespaceBindings(sourceCommentsXml));
|
|
183
|
-
|
|
184
|
-
zip,
|
|
185
|
-
path: sourceCommentsFile?.name ?? "word/comments.xml",
|
|
186
|
-
data: encodeUtf8(commentsXml),
|
|
187
|
-
options: {
|
|
188
|
-
compression: "DEFLATE",
|
|
189
|
-
compressionOptions: { level: compressionLevel }
|
|
190
|
-
}
|
|
191
|
-
});
|
|
182
|
+
zip.set(sourceCommentsFile?.path ?? "word/comments.xml", commentsXml, { level: compressionLevel });
|
|
192
183
|
await ensureCommentsContentType(zip, compressionLevel);
|
|
193
184
|
await ensureCommentsRelationship(zip, compressionLevel);
|
|
194
185
|
await syncCommentsExtendedPart(plan, zip, compressionLevel);
|
|
@@ -212,43 +203,30 @@ async function syncCommentsExtendedPart(plan, zip, compressionLevel) {
|
|
|
212
203
|
const existing = findZipEntryCaseInsensitive(zip, COMMENTS_EXTENDED_PART_LOWER);
|
|
213
204
|
if (!xml) {
|
|
214
205
|
if (!existing) return;
|
|
215
|
-
zip.
|
|
206
|
+
zip.delete(existing.path);
|
|
216
207
|
await transformPackagingFile(zip, "[Content_Types].xml", removeCommentsExtendedOverride, compressionLevel);
|
|
217
208
|
await transformPackagingFile(zip, "word/_rels/document.xml.rels", removeCommentsExtendedRelationship, compressionLevel);
|
|
218
209
|
return;
|
|
219
210
|
}
|
|
220
|
-
|
|
221
|
-
zip,
|
|
222
|
-
path: existing?.name ?? "word/commentsExtended.xml",
|
|
223
|
-
data: encodeUtf8(xml),
|
|
224
|
-
options: {
|
|
225
|
-
compression: "DEFLATE",
|
|
226
|
-
compressionOptions: { level: compressionLevel }
|
|
227
|
-
}
|
|
228
|
-
});
|
|
211
|
+
zip.set(existing?.path ?? "word/commentsExtended.xml", xml, { level: compressionLevel });
|
|
229
212
|
await transformPackagingFile(zip, "[Content_Types].xml", addCommentsExtendedOverride, compressionLevel);
|
|
230
213
|
await transformPackagingFile(zip, "word/_rels/document.xml.rels", addCommentsExtendedRelationship, compressionLevel);
|
|
231
214
|
}
|
|
232
215
|
/** Read a packaging file, apply a pure transform, and write it back if it changed. */
|
|
233
216
|
async function transformPackagingFile(zip, path, transform, compressionLevel) {
|
|
234
|
-
const file = zip.
|
|
217
|
+
const file = zip.entry(path);
|
|
235
218
|
if (!file) return;
|
|
236
|
-
const xml = await file.
|
|
219
|
+
const xml = await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
237
220
|
const next = transform(xml);
|
|
238
|
-
if (next !== xml)
|
|
239
|
-
zip,
|
|
240
|
-
path,
|
|
241
|
-
data: encodeUtf8(next),
|
|
242
|
-
options: {
|
|
243
|
-
compression: "DEFLATE",
|
|
244
|
-
compressionOptions: { level: compressionLevel }
|
|
245
|
-
}
|
|
246
|
-
});
|
|
221
|
+
if (next !== xml) zip.set(path, next, { level: compressionLevel });
|
|
247
222
|
}
|
|
248
223
|
function findZipEntryCaseInsensitive(zip, lowerPath) {
|
|
249
|
-
const direct = zip.
|
|
224
|
+
const direct = zip.entry(lowerPath);
|
|
250
225
|
if (direct) return direct;
|
|
251
|
-
for (const
|
|
226
|
+
for (const file of zip.entries()) {
|
|
227
|
+
const { path } = file;
|
|
228
|
+
if (path.toLowerCase() === lowerPath) return file;
|
|
229
|
+
}
|
|
252
230
|
return null;
|
|
253
231
|
}
|
|
254
232
|
const PACKAGE_RELATIONSHIPS_NAMESPACE_URI = "http://schemas.openxmlformats.org/package/2006/relationships";
|
|
@@ -312,7 +290,7 @@ function collectDocxParts(doc, zip) {
|
|
|
312
290
|
addHeaderFooterParts(doc.package.footers, RELATIONSHIP_TYPES.footer);
|
|
313
291
|
const addNoteParts = (notes, conventionalLowerPath) => {
|
|
314
292
|
if (!notes || notes.length === 0) return;
|
|
315
|
-
const partPath = findNotePartEntry(zip, conventionalLowerPath)?.
|
|
293
|
+
const partPath = findNotePartEntry(zip, conventionalLowerPath)?.path ?? conventionalLowerPath;
|
|
316
294
|
parts.push({
|
|
317
295
|
relsPath: notePartRelsPath(partPath),
|
|
318
296
|
blocks: notes.flatMap((note) => note.content)
|
|
@@ -323,25 +301,25 @@ function collectDocxParts(doc, zip) {
|
|
|
323
301
|
return parts;
|
|
324
302
|
}
|
|
325
303
|
async function readRelsOrStub(zip, relsPath) {
|
|
326
|
-
const file = zip.
|
|
327
|
-
return (file ? await file.
|
|
304
|
+
const file = zip.entry(relsPath);
|
|
305
|
+
return (file ? await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })) : EMPTY_RELS_XML).replace(/<Relationships(?<attrs>[^>]*)\/>/u, "<Relationships$<attrs>></Relationships>");
|
|
328
306
|
}
|
|
329
307
|
function findMaxImageNum(zip) {
|
|
330
308
|
let maxImageNum = 0;
|
|
331
|
-
zip.
|
|
309
|
+
for (const relativePath of zip.paths()) {
|
|
332
310
|
const m = /^word\/media\/image(?<num>\d+)\./u.exec(relativePath);
|
|
333
311
|
if (m) {
|
|
334
312
|
const num = Number.parseInt(m.groups["num"], 10);
|
|
335
313
|
if (num > maxImageNum) maxImageNum = num;
|
|
336
314
|
}
|
|
337
|
-
}
|
|
315
|
+
}
|
|
338
316
|
return maxImageNum;
|
|
339
317
|
}
|
|
340
318
|
async function registerImageExtensions(zip, extensions, compressionLevel) {
|
|
341
319
|
if (extensions.size === 0) return;
|
|
342
|
-
const ctFile = zip.
|
|
320
|
+
const ctFile = zip.entry("[Content_Types].xml");
|
|
343
321
|
if (!ctFile) return;
|
|
344
|
-
let ctXml = await ctFile.
|
|
322
|
+
let ctXml = await ctFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
345
323
|
let changed = false;
|
|
346
324
|
for (const ext of extensions) {
|
|
347
325
|
if (ctXml.includes(`Extension="${ext}"`)) continue;
|
|
@@ -350,15 +328,7 @@ async function registerImageExtensions(zip, extensions, compressionLevel) {
|
|
|
350
328
|
changed = true;
|
|
351
329
|
}
|
|
352
330
|
if (!changed) return;
|
|
353
|
-
|
|
354
|
-
zip,
|
|
355
|
-
path: "[Content_Types].xml",
|
|
356
|
-
data: encodeUtf8(ctXml),
|
|
357
|
-
options: {
|
|
358
|
-
compression: "DEFLATE",
|
|
359
|
-
compressionOptions: { level: compressionLevel }
|
|
360
|
-
}
|
|
361
|
-
});
|
|
331
|
+
zip.set("[Content_Types].xml", ctXml, { level: compressionLevel });
|
|
362
332
|
}
|
|
363
333
|
function collectNewImages(blocks) {
|
|
364
334
|
const images = [];
|
|
@@ -425,15 +395,7 @@ async function processNewImages(parts, zip, compressionLevel) {
|
|
|
425
395
|
maxId++;
|
|
426
396
|
const mediaPath = `word/media/${`image${maxImageNum}.${extension}`}`;
|
|
427
397
|
const newRId = mintRelationshipId(maxId);
|
|
428
|
-
|
|
429
|
-
zip,
|
|
430
|
-
path: mediaPath,
|
|
431
|
-
data: new Uint8Array(data),
|
|
432
|
-
options: {
|
|
433
|
-
compression: "DEFLATE",
|
|
434
|
-
compressionOptions: { level: compressionLevel }
|
|
435
|
-
}
|
|
436
|
-
});
|
|
398
|
+
zip.set(mediaPath, new Uint8Array(data), { level: compressionLevel });
|
|
437
399
|
relEntries.push(`<Relationship Id="${newRId}" Type="${RELATIONSHIP_TYPES.image}" Target="${escapeXmlAttribute(relativeTargetForPart(partPath, mediaPath))}"/>`);
|
|
438
400
|
extensionsAdded.add(extension);
|
|
439
401
|
if (drawing.rawXml) drawing.rawXml = rebindDrawingImageRelationship({
|
|
@@ -445,15 +407,7 @@ async function processNewImages(parts, zip, compressionLevel) {
|
|
|
445
407
|
}
|
|
446
408
|
if (relEntries.length > 0) {
|
|
447
409
|
const updatedRelsXml = relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`);
|
|
448
|
-
|
|
449
|
-
zip,
|
|
450
|
-
path: relsPath,
|
|
451
|
-
data: encodeUtf8(updatedRelsXml),
|
|
452
|
-
options: {
|
|
453
|
-
compression: "DEFLATE",
|
|
454
|
-
compressionOptions: { level: compressionLevel }
|
|
455
|
-
}
|
|
456
|
-
});
|
|
410
|
+
zip.set(relsPath, updatedRelsXml, { level: compressionLevel });
|
|
457
411
|
}
|
|
458
412
|
}
|
|
459
413
|
await registerImageExtensions(zip, extensionsAdded, compressionLevel);
|
|
@@ -466,15 +420,7 @@ const materializeEmbeddedMedia = async ({ document, zip, compressionLevel }) =>
|
|
|
466
420
|
if (isUnsafePackagePath(media.path) || !media.path.startsWith("word/media/")) panic("Detached media has an invalid package path");
|
|
467
421
|
const extension = media.path.split(".").at(-1)?.toLowerCase();
|
|
468
422
|
if (!extension || !/^[a-z0-9]+$/u.test(extension)) panic("Embedded media has an invalid extension");
|
|
469
|
-
|
|
470
|
-
zip,
|
|
471
|
-
path: media.path,
|
|
472
|
-
data: new Uint8Array(media.data),
|
|
473
|
-
options: {
|
|
474
|
-
compression: "DEFLATE",
|
|
475
|
-
compressionOptions: { level: compressionLevel }
|
|
476
|
-
}
|
|
477
|
-
});
|
|
423
|
+
zip.set(media.path, new Uint8Array(media.data), { level: compressionLevel });
|
|
478
424
|
extensions.add(extension);
|
|
479
425
|
}
|
|
480
426
|
await registerImageExtensions(zip, extensions, compressionLevel);
|
|
@@ -529,15 +475,7 @@ const publishCanonicalImageResources = async ({ document, zip, compressionLevel
|
|
|
529
475
|
const media = document.package.media?.get(absolute);
|
|
530
476
|
if (!media) return refuse("missingMedia", "A canonical inline picture is missing its media bytes.");
|
|
531
477
|
if (isUnsafePackagePath(media.path) || media.path !== absolute) return refuse("unsafeTarget", "A canonical inline picture media path does not match its relationship.");
|
|
532
|
-
|
|
533
|
-
zip,
|
|
534
|
-
path: absolute,
|
|
535
|
-
data: new Uint8Array(media.data),
|
|
536
|
-
options: {
|
|
537
|
-
compression: "DEFLATE",
|
|
538
|
-
compressionOptions: { level: compressionLevel }
|
|
539
|
-
}
|
|
540
|
-
});
|
|
478
|
+
zip.set(absolute, new Uint8Array(media.data), { level: compressionLevel });
|
|
541
479
|
const extension = absolute.split(".").at(-1)?.toLowerCase();
|
|
542
480
|
if (!extension || !/^[a-z0-9]+$/u.test(extension)) return refuse("unsafeTarget", "A canonical inline picture media extension is invalid.");
|
|
543
481
|
await registerImageExtensions(zip, /* @__PURE__ */ new Set([extension]), compressionLevel);
|
|
@@ -559,15 +497,7 @@ const publishCanonicalImageResources = async ({ document, zip, compressionLevel
|
|
|
559
497
|
});
|
|
560
498
|
changed = true;
|
|
561
499
|
}
|
|
562
|
-
if (changed)
|
|
563
|
-
zip,
|
|
564
|
-
path: relsPath,
|
|
565
|
-
data: encodeUtf8(xml),
|
|
566
|
-
options: {
|
|
567
|
-
compression: "DEFLATE",
|
|
568
|
-
compressionOptions: { level: compressionLevel }
|
|
569
|
-
}
|
|
570
|
-
});
|
|
500
|
+
if (changed) zip.set(relsPath, xml, { level: compressionLevel });
|
|
571
501
|
}
|
|
572
502
|
};
|
|
573
503
|
const collectExternalHyperlinkResources = (blocks) => {
|
|
@@ -710,15 +640,7 @@ async function processNewHyperlinks(parts, zip, compressionLevel) {
|
|
|
710
640
|
}
|
|
711
641
|
if (relEntries.length > 0) {
|
|
712
642
|
(root.elements ??= []).push(...relEntries);
|
|
713
|
-
|
|
714
|
-
zip,
|
|
715
|
-
path: relsPath,
|
|
716
|
-
data: encodeUtf8(captureVerbatimXml(root)),
|
|
717
|
-
options: {
|
|
718
|
-
compression: "DEFLATE",
|
|
719
|
-
compressionOptions: { level: compressionLevel }
|
|
720
|
-
}
|
|
721
|
-
});
|
|
643
|
+
zip.set(relsPath, captureVerbatimXml(root), { level: compressionLevel });
|
|
722
644
|
}
|
|
723
645
|
}
|
|
724
646
|
}
|
|
@@ -736,23 +658,18 @@ async function processNewHyperlinks(parts, zip, compressionLevel) {
|
|
|
736
658
|
*/
|
|
737
659
|
const normalizePackageIdsInZip = async (zip, compressionLevel) => {
|
|
738
660
|
const xmlParts = /* @__PURE__ */ new Map();
|
|
739
|
-
for (const
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
|
|
661
|
+
for (const file of zip.entries()) {
|
|
662
|
+
const { path } = file;
|
|
663
|
+
if (path.startsWith("word/") && path.endsWith(".xml")) {
|
|
664
|
+
const xml = await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
665
|
+
assertValidOoxmlNumericIds(xml, path);
|
|
666
|
+
xmlParts.set(path, xml);
|
|
667
|
+
}
|
|
743
668
|
}
|
|
744
669
|
const normalizedParts = normalizeParaIdRangeInXmlParts(normalizeRevisionIdsInXmlParts(xmlParts));
|
|
745
670
|
for (const [path, xml] of normalizedParts) if (xml !== xmlParts.get(path)) {
|
|
746
671
|
assertValidOoxmlNumericIds(xml, path);
|
|
747
|
-
|
|
748
|
-
zip,
|
|
749
|
-
path,
|
|
750
|
-
data: encodeUtf8(xml),
|
|
751
|
-
options: {
|
|
752
|
-
compression: "DEFLATE",
|
|
753
|
-
compressionOptions: { level: compressionLevel }
|
|
754
|
-
}
|
|
755
|
-
});
|
|
672
|
+
zip.set(path, xml, { level: compressionLevel });
|
|
756
673
|
}
|
|
757
674
|
};
|
|
758
675
|
const EXTENDED_PROPERTIES_PATH = "docProps/app.xml";
|
|
@@ -762,19 +679,11 @@ const EXTENDED_PROPERTIES_PATH = "docProps/app.xml";
|
|
|
762
679
|
* holding none.
|
|
763
680
|
*/
|
|
764
681
|
const normalizeAppVersionInZip = async (zip, compressionLevel) => {
|
|
765
|
-
const extendedProperties = zip.
|
|
682
|
+
const extendedProperties = zip.entry(EXTENDED_PROPERTIES_PATH);
|
|
766
683
|
if (!extendedProperties) return;
|
|
767
|
-
const xml = await extendedProperties.
|
|
684
|
+
const xml = await extendedProperties.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
768
685
|
const normalized = normalizeAppVersionInExtendedProperties(xml);
|
|
769
|
-
if (normalized !== xml)
|
|
770
|
-
zip,
|
|
771
|
-
path: EXTENDED_PROPERTIES_PATH,
|
|
772
|
-
data: encodeUtf8(normalized),
|
|
773
|
-
options: {
|
|
774
|
-
compression: "DEFLATE",
|
|
775
|
-
compressionOptions: { level: compressionLevel }
|
|
776
|
-
}
|
|
777
|
-
});
|
|
686
|
+
if (normalized !== xml) zip.set(EXTENDED_PROPERTIES_PATH, normalized, { level: compressionLevel });
|
|
778
687
|
};
|
|
779
688
|
/**
|
|
780
689
|
* Every pass a save owes the package as a whole, in one function so that the
|
|
@@ -792,11 +701,7 @@ const normalizePackageOnSave = async (zip, compressionLevel) => {
|
|
|
792
701
|
const generateDocxZip = async (zip, compressionLevel) => {
|
|
793
702
|
await reconcilePackageReferences(zip, compressionLevel);
|
|
794
703
|
await normalizePackageOnSave(zip, compressionLevel);
|
|
795
|
-
return zip.
|
|
796
|
-
type: "arraybuffer",
|
|
797
|
-
compression: "DEFLATE",
|
|
798
|
-
compressionOptions: { level: compressionLevel }
|
|
799
|
-
});
|
|
704
|
+
return zip.generate({ level: compressionLevel }).then((bytes) => bytes.buffer);
|
|
800
705
|
};
|
|
801
706
|
/**
|
|
802
707
|
* Section facts of each source package's `word/document.xml`, keyed by the
|
|
@@ -826,9 +731,9 @@ const parsedZipSources = /* @__PURE__ */ new WeakMap();
|
|
|
826
731
|
const loadParsedZipSource = async (document, buffer) => {
|
|
827
732
|
const cached = parsedZipSources.get(document);
|
|
828
733
|
if (cached?.buffer === buffer) return cached;
|
|
829
|
-
const zip = await
|
|
734
|
+
const zip = await ZipArchive.read(buffer);
|
|
830
735
|
removeUnsafeEntries(zip);
|
|
831
|
-
const [documentXml, corePropertiesXml] = await Promise.all([zip.
|
|
736
|
+
const [documentXml, corePropertiesXml] = await Promise.all([zip.entry("word/document.xml")?.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })), zip.entry("docProps/core.xml")?.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }))]);
|
|
832
737
|
const source = {
|
|
833
738
|
buffer,
|
|
834
739
|
zip,
|
|
@@ -838,11 +743,7 @@ const loadParsedZipSource = async (document, buffer) => {
|
|
|
838
743
|
parsedZipSources.set(document, source);
|
|
839
744
|
return source;
|
|
840
745
|
};
|
|
841
|
-
const cloneDocxZip = (source) =>
|
|
842
|
-
const clone = new JSZip();
|
|
843
|
-
clone.files = { ...source.files };
|
|
844
|
-
return clone;
|
|
845
|
-
};
|
|
746
|
+
const cloneDocxZip = (source) => source.clone();
|
|
846
747
|
const normalizeExportDrawingIds = ({ package: docxPackage }) => {
|
|
847
748
|
normalizeDrawingIds({
|
|
848
749
|
documentBody: docxPackage.document,
|
|
@@ -896,15 +797,7 @@ const finishRepack = async ({ document: sourceDocument, originalZip, outputZip,
|
|
|
896
797
|
removedReferences: [...sectionReferenceRemovals ?? [], ...sectionEndpointRemoval?.removedReferences ?? []],
|
|
897
798
|
compressionLevel
|
|
898
799
|
});
|
|
899
|
-
|
|
900
|
-
zip: outputZip,
|
|
901
|
-
path: "word/document.xml",
|
|
902
|
-
data: encodeUtf8(documentXml),
|
|
903
|
-
options: {
|
|
904
|
-
compression: "DEFLATE",
|
|
905
|
-
compressionOptions: { level: compressionLevel }
|
|
906
|
-
}
|
|
907
|
-
});
|
|
800
|
+
outputZip.set("word/document.xml", documentXml, { level: compressionLevel });
|
|
908
801
|
await rebindWatermarkRelIds({
|
|
909
802
|
document,
|
|
910
803
|
zip: outputZip,
|
|
@@ -934,15 +827,7 @@ const finishRepack = async ({ document: sourceDocument, originalZip, outputZip,
|
|
|
934
827
|
updateModifiedDate,
|
|
935
828
|
...modifiedBy !== void 0 ? { modifiedBy } : {}
|
|
936
829
|
});
|
|
937
|
-
|
|
938
|
-
zip: outputZip,
|
|
939
|
-
path: "docProps/core.xml",
|
|
940
|
-
data: encodeUtf8(updatedCoreProperties),
|
|
941
|
-
options: {
|
|
942
|
-
compression: "DEFLATE",
|
|
943
|
-
compressionOptions: { level: compressionLevel }
|
|
944
|
-
}
|
|
945
|
-
});
|
|
830
|
+
outputZip.set("docProps/core.xml", updatedCoreProperties, { level: compressionLevel });
|
|
946
831
|
}
|
|
947
832
|
return generateDocxZip(outputZip, compressionLevel);
|
|
948
833
|
};
|
|
@@ -950,8 +835,8 @@ async function repackDocxWithSectionEndpointRemoval({ document: doc, options, se
|
|
|
950
835
|
if (!doc.originalBuffer) panic("Cannot repack document: no original buffer for round-trip. Use createDocx() for new documents.");
|
|
951
836
|
const { compressionLevel = 6, updateModifiedDate = true, modifiedBy, changedNoteParaIds } = options;
|
|
952
837
|
const exportDocument = withoutExhaustedComments(withoutOrphanCommentRanges(doc));
|
|
953
|
-
const originalZip = await
|
|
954
|
-
const [originalDocumentXml, originalCorePropertiesXml] = await Promise.all([originalZip.
|
|
838
|
+
const originalZip = await ZipArchive.read(doc.originalBuffer);
|
|
839
|
+
const [originalDocumentXml, originalCorePropertiesXml] = await Promise.all([originalZip.entry("word/document.xml")?.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })), originalZip.entry("docProps/core.xml")?.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }))]);
|
|
955
840
|
removeUnsafeEntries(originalZip);
|
|
956
841
|
const newZip = cloneDocxZip(originalZip);
|
|
957
842
|
return finishRepack({
|
|
@@ -991,32 +876,8 @@ function repackDocx(doc, options = {}) {
|
|
|
991
876
|
async function repackDocxFromRaw(doc, rawContent, options = {}) {
|
|
992
877
|
const { compressionLevel = 6, updateModifiedDate = true, modifiedBy, changedNoteParaIds } = options;
|
|
993
878
|
let exportDocument = withoutExhaustedComments(withoutOrphanCommentRanges(cloneExportDocument(doc)));
|
|
994
|
-
const newZip =
|
|
995
|
-
for (const
|
|
996
|
-
if (file.dir) {
|
|
997
|
-
writeZipPart({
|
|
998
|
-
zip: newZip,
|
|
999
|
-
path,
|
|
1000
|
-
data: /* @__PURE__ */ new Uint8Array(),
|
|
1001
|
-
options: {
|
|
1002
|
-
dir: true,
|
|
1003
|
-
date: file.date
|
|
1004
|
-
}
|
|
1005
|
-
});
|
|
1006
|
-
continue;
|
|
1007
|
-
}
|
|
1008
|
-
if (isUnsafePackagePath(path)) continue;
|
|
1009
|
-
const content = await file.async("arraybuffer");
|
|
1010
|
-
writeZipPart({
|
|
1011
|
-
zip: newZip,
|
|
1012
|
-
path,
|
|
1013
|
-
data: new Uint8Array(content),
|
|
1014
|
-
options: {
|
|
1015
|
-
compression: "DEFLATE",
|
|
1016
|
-
compressionOptions: { level: compressionLevel }
|
|
1017
|
-
}
|
|
1018
|
-
});
|
|
1019
|
-
}
|
|
879
|
+
const newZip = rawContent.originalZip.clone();
|
|
880
|
+
for (const path of newZip.paths()) if (isUnsafePackagePath(path)) newZip.delete(path);
|
|
1020
881
|
exportDocument = await materializeNewHeaderFooterParts(exportDocument, newZip, compressionLevel);
|
|
1021
882
|
await materializeEmbeddedMedia({
|
|
1022
883
|
document: exportDocument,
|
|
@@ -1044,15 +905,7 @@ async function repackDocxFromRaw(doc, rawContent, options = {}) {
|
|
|
1044
905
|
serializedDocumentXml: documentXml,
|
|
1045
906
|
doc: exportDocument
|
|
1046
907
|
});
|
|
1047
|
-
|
|
1048
|
-
zip: newZip,
|
|
1049
|
-
path: "word/document.xml",
|
|
1050
|
-
data: encodeUtf8(documentXml),
|
|
1051
|
-
options: {
|
|
1052
|
-
compression: "DEFLATE",
|
|
1053
|
-
compressionOptions: { level: compressionLevel }
|
|
1054
|
-
}
|
|
1055
|
-
});
|
|
908
|
+
newZip.set("word/document.xml", documentXml, { level: compressionLevel });
|
|
1056
909
|
await rebindWatermarkRelIds({
|
|
1057
910
|
document: exportDocument,
|
|
1058
911
|
zip: newZip,
|
|
@@ -1082,15 +935,7 @@ async function repackDocxFromRaw(doc, rawContent, options = {}) {
|
|
|
1082
935
|
updateModifiedDate,
|
|
1083
936
|
...modifiedBy !== void 0 ? { modifiedBy } : {}
|
|
1084
937
|
});
|
|
1085
|
-
|
|
1086
|
-
zip: newZip,
|
|
1087
|
-
path: "docProps/core.xml",
|
|
1088
|
-
data: encodeUtf8(updatedCoreProps),
|
|
1089
|
-
options: {
|
|
1090
|
-
compression: "DEFLATE",
|
|
1091
|
-
compressionOptions: { level: compressionLevel }
|
|
1092
|
-
}
|
|
1093
|
-
});
|
|
938
|
+
newZip.set("docProps/core.xml", updatedCoreProps, { level: compressionLevel });
|
|
1094
939
|
}
|
|
1095
940
|
return generateDocxZip(newZip, compressionLevel);
|
|
1096
941
|
}
|
|
@@ -1176,49 +1021,25 @@ function withoutRelationships(relsXml, ids) {
|
|
|
1176
1021
|
* can neither read nor rewrite. Both sides are filtered on save.
|
|
1177
1022
|
*/
|
|
1178
1023
|
async function dropAttachedTemplateReference(zip, compressionLevel) {
|
|
1179
|
-
const settingsFile = zip.
|
|
1024
|
+
const settingsFile = zip.entry(SETTINGS_PART);
|
|
1180
1025
|
if (!settingsFile) return;
|
|
1181
|
-
const relsFile = zip.
|
|
1182
|
-
const filtered = withoutAttachedTemplate(await settingsFile.
|
|
1183
|
-
if (filtered.settingsXml !== void 0)
|
|
1184
|
-
|
|
1185
|
-
path: SETTINGS_PART,
|
|
1186
|
-
data: encodeUtf8(filtered.settingsXml),
|
|
1187
|
-
options: {
|
|
1188
|
-
compression: "DEFLATE",
|
|
1189
|
-
compressionOptions: { level: compressionLevel }
|
|
1190
|
-
}
|
|
1191
|
-
});
|
|
1192
|
-
if (filtered.relsXml !== void 0) writeZipPart({
|
|
1193
|
-
zip,
|
|
1194
|
-
path: SETTINGS_RELS_PART,
|
|
1195
|
-
data: encodeUtf8(filtered.relsXml),
|
|
1196
|
-
options: {
|
|
1197
|
-
compression: "DEFLATE",
|
|
1198
|
-
compressionOptions: { level: compressionLevel }
|
|
1199
|
-
}
|
|
1200
|
-
});
|
|
1026
|
+
const relsFile = zip.entry(SETTINGS_RELS_PART);
|
|
1027
|
+
const filtered = withoutAttachedTemplate(await settingsFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })), await relsFile?.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })));
|
|
1028
|
+
if (filtered.settingsXml !== void 0) zip.set(SETTINGS_PART, filtered.settingsXml, { level: compressionLevel });
|
|
1029
|
+
if (filtered.relsXml !== void 0) zip.set(SETTINGS_RELS_PART, filtered.relsXml, { level: compressionLevel });
|
|
1201
1030
|
}
|
|
1202
1031
|
/**
|
|
1203
1032
|
* Ensure [Content_Types].xml contains an Override for word/comments.xml.
|
|
1204
1033
|
* If the document already had comments, this is a no-op.
|
|
1205
1034
|
*/
|
|
1206
1035
|
async function ensureCommentsContentType(zip, compressionLevel) {
|
|
1207
|
-
const ctFile = zip.
|
|
1036
|
+
const ctFile = zip.entry("[Content_Types].xml");
|
|
1208
1037
|
if (!ctFile) return;
|
|
1209
|
-
let ctXml = await ctFile.
|
|
1038
|
+
let ctXml = await ctFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
1210
1039
|
if (ctXml.toLowerCase().includes("/word/comments.xml")) return;
|
|
1211
|
-
const commentsPartPath = findZipEntryCaseInsensitive(zip, "word/comments.xml")?.
|
|
1040
|
+
const commentsPartPath = findZipEntryCaseInsensitive(zip, "word/comments.xml")?.path ?? "word/comments.xml";
|
|
1212
1041
|
ctXml = ctXml.replace("</Types>", `<Override PartName="/${commentsPartPath}" ContentType="${COMMENTS_CONTENT_TYPE}"/></Types>`);
|
|
1213
|
-
|
|
1214
|
-
zip,
|
|
1215
|
-
path: "[Content_Types].xml",
|
|
1216
|
-
data: encodeUtf8(ctXml),
|
|
1217
|
-
options: {
|
|
1218
|
-
compression: "DEFLATE",
|
|
1219
|
-
compressionOptions: { level: compressionLevel }
|
|
1220
|
-
}
|
|
1221
|
-
});
|
|
1042
|
+
zip.set("[Content_Types].xml", ctXml, { level: compressionLevel });
|
|
1222
1043
|
}
|
|
1223
1044
|
/**
|
|
1224
1045
|
* Ensure word/_rels/document.xml.rels contains a Relationship for comments.xml.
|
|
@@ -1226,22 +1047,14 @@ async function ensureCommentsContentType(zip, compressionLevel) {
|
|
|
1226
1047
|
*/
|
|
1227
1048
|
async function ensureCommentsRelationship(zip, compressionLevel) {
|
|
1228
1049
|
const relsPath = "word/_rels/document.xml.rels";
|
|
1229
|
-
const relsFile = zip.
|
|
1050
|
+
const relsFile = zip.entry(relsPath);
|
|
1230
1051
|
if (!relsFile) return;
|
|
1231
|
-
let relsXml = await relsFile.
|
|
1052
|
+
let relsXml = await relsFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
1232
1053
|
if (relsXml.toLowerCase().includes("comments.xml")) return;
|
|
1233
|
-
const commentsPartPath = findZipEntryCaseInsensitive(zip, "word/comments.xml")?.
|
|
1054
|
+
const commentsPartPath = findZipEntryCaseInsensitive(zip, "word/comments.xml")?.path ?? "word/comments.xml";
|
|
1234
1055
|
const newRId = `rId${findMaxRId(relsXml) + 1}`;
|
|
1235
1056
|
relsXml = relsXml.replace("</Relationships>", `<Relationship Id="${newRId}" Type="${RELATIONSHIP_TYPES.comments}" Target="${commentsPartPath.slice(5)}"/></Relationships>`);
|
|
1236
|
-
|
|
1237
|
-
zip,
|
|
1238
|
-
path: relsPath,
|
|
1239
|
-
data: encodeUtf8(relsXml),
|
|
1240
|
-
options: {
|
|
1241
|
-
compression: "DEFLATE",
|
|
1242
|
-
compressionOptions: { level: compressionLevel }
|
|
1243
|
-
}
|
|
1244
|
-
});
|
|
1057
|
+
zip.set(relsPath, relsXml, { level: compressionLevel });
|
|
1245
1058
|
}
|
|
1246
1059
|
/**
|
|
1247
1060
|
* Update only document.xml in a DOCX buffer (minimal changes)
|
|
@@ -1253,21 +1066,9 @@ async function ensureCommentsRelationship(zip, compressionLevel) {
|
|
|
1253
1066
|
*/
|
|
1254
1067
|
async function updateDocumentXml(originalBuffer, newDocumentXml, options = {}) {
|
|
1255
1068
|
const { compressionLevel = 6 } = options;
|
|
1256
|
-
const zip = await
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
path: "word/document.xml",
|
|
1260
|
-
data: encodeUtf8(newDocumentXml),
|
|
1261
|
-
options: {
|
|
1262
|
-
compression: "DEFLATE",
|
|
1263
|
-
compressionOptions: { level: compressionLevel }
|
|
1264
|
-
}
|
|
1265
|
-
});
|
|
1266
|
-
return zip.generateAsync({
|
|
1267
|
-
type: "arraybuffer",
|
|
1268
|
-
compression: "DEFLATE",
|
|
1269
|
-
compressionOptions: { level: compressionLevel }
|
|
1270
|
-
});
|
|
1069
|
+
const zip = await ZipArchive.read(originalBuffer);
|
|
1070
|
+
zip.set("word/document.xml", newDocumentXml, { level: compressionLevel });
|
|
1071
|
+
return zip.generate({ level: compressionLevel }).then((bytes) => bytes.buffer);
|
|
1271
1072
|
}
|
|
1272
1073
|
/**
|
|
1273
1074
|
* Update a specific XML file in a DOCX buffer
|
|
@@ -1280,21 +1081,9 @@ async function updateDocumentXml(originalBuffer, newDocumentXml, options = {}) {
|
|
|
1280
1081
|
*/
|
|
1281
1082
|
async function updateXmlFile(originalBuffer, path, content, options = {}) {
|
|
1282
1083
|
const { compressionLevel = 6 } = options;
|
|
1283
|
-
const zip = await
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
path,
|
|
1287
|
-
data: encodeUtf8(content),
|
|
1288
|
-
options: {
|
|
1289
|
-
compression: "DEFLATE",
|
|
1290
|
-
compressionOptions: { level: compressionLevel }
|
|
1291
|
-
}
|
|
1292
|
-
});
|
|
1293
|
-
return zip.generateAsync({
|
|
1294
|
-
type: "arraybuffer",
|
|
1295
|
-
compression: "DEFLATE",
|
|
1296
|
-
compressionOptions: { level: compressionLevel }
|
|
1297
|
-
});
|
|
1084
|
+
const zip = await ZipArchive.read(originalBuffer);
|
|
1085
|
+
zip.set(path, content, { level: compressionLevel });
|
|
1086
|
+
return zip.generate({ level: compressionLevel }).then((bytes) => bytes.buffer);
|
|
1298
1087
|
}
|
|
1299
1088
|
/**
|
|
1300
1089
|
* Update multiple files in a DOCX buffer
|
|
@@ -1305,10 +1094,10 @@ async function updateXmlFile(originalBuffer, path, content, options = {}) {
|
|
|
1305
1094
|
* @returns Promise resolving to DOCX as ArrayBuffer
|
|
1306
1095
|
*/
|
|
1307
1096
|
async function updateMultipleFiles(originalBuffer, updates, options = {}) {
|
|
1308
|
-
return applyUpdatesToZip(await
|
|
1097
|
+
return applyUpdatesToZip(await ZipArchive.read(originalBuffer), updates, options);
|
|
1309
1098
|
}
|
|
1310
1099
|
/**
|
|
1311
|
-
* Apply file updates to an already-loaded
|
|
1100
|
+
* Apply file updates to an already-loaded ZipArchive instance and generate the output.
|
|
1312
1101
|
* Use this when the zip is already loaded to avoid a redundant decompression pass.
|
|
1313
1102
|
*
|
|
1314
1103
|
* This is the selective save's exit, so it owes the package the same passes
|
|
@@ -1319,21 +1108,9 @@ async function updateMultipleFiles(originalBuffer, updates, options = {}) {
|
|
|
1319
1108
|
*/
|
|
1320
1109
|
async function applyUpdatesToZip(zip, updates, options = {}) {
|
|
1321
1110
|
const { compressionLevel = 6 } = options;
|
|
1322
|
-
for (const [path, content] of updates)
|
|
1323
|
-
zip,
|
|
1324
|
-
path,
|
|
1325
|
-
data: typeof content === "string" ? encodeUtf8(content) : new Uint8Array(content),
|
|
1326
|
-
options: {
|
|
1327
|
-
compression: "DEFLATE",
|
|
1328
|
-
compressionOptions: { level: compressionLevel }
|
|
1329
|
-
}
|
|
1330
|
-
});
|
|
1111
|
+
for (const [path, content] of updates) zip.set(path, typeof content === "string" ? encodeUtf8(content) : new Uint8Array(content), { level: compressionLevel });
|
|
1331
1112
|
await normalizePackageOnSave(zip, compressionLevel);
|
|
1332
|
-
return await zip.
|
|
1333
|
-
type: "arraybuffer",
|
|
1334
|
-
compression: "DEFLATE",
|
|
1335
|
-
compressionOptions: { level: compressionLevel }
|
|
1336
|
-
});
|
|
1113
|
+
return (await zip.generate({ level: compressionLevel })).buffer;
|
|
1337
1114
|
}
|
|
1338
1115
|
/**
|
|
1339
1116
|
* Add a new relationship to document.xml.rels
|
|
@@ -1343,26 +1120,18 @@ async function applyUpdatesToZip(zip, updates, options = {}) {
|
|
|
1343
1120
|
* @returns Promise resolving to { buffer: ArrayBuffer, rId: string }
|
|
1344
1121
|
*/
|
|
1345
1122
|
async function addRelationship(originalBuffer, relationship) {
|
|
1346
|
-
const zip = await
|
|
1123
|
+
const zip = await ZipArchive.read(originalBuffer);
|
|
1347
1124
|
const relsPath = "word/_rels/document.xml.rels";
|
|
1348
|
-
const relsFile = zip.
|
|
1125
|
+
const relsFile = zip.entry(relsPath);
|
|
1349
1126
|
if (!relsFile) panic("document.xml.rels not found in DOCX");
|
|
1350
|
-
const relsXml = await relsFile.
|
|
1127
|
+
const relsXml = await relsFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
1351
1128
|
const newRId = `rId${findMaxRId(relsXml) + 1}`;
|
|
1352
1129
|
const targetModeAttr = relationship.targetMode === "External" ? " TargetMode=\"External\"" : "";
|
|
1353
1130
|
const newRelElement = `<Relationship Id="${newRId}" Type="${relationship.type}" Target="${escapeXmlAttribute(relationship.target)}"${targetModeAttr}/>`;
|
|
1354
1131
|
const updatedRelsXml = relsXml.replace("</Relationships>", `${newRelElement}</Relationships>`);
|
|
1355
|
-
|
|
1356
|
-
zip,
|
|
1357
|
-
path: relsPath,
|
|
1358
|
-
data: encodeUtf8(updatedRelsXml)
|
|
1359
|
-
});
|
|
1132
|
+
zip.set(relsPath, updatedRelsXml);
|
|
1360
1133
|
return {
|
|
1361
|
-
buffer: await zip.
|
|
1362
|
-
type: "arraybuffer",
|
|
1363
|
-
compression: "DEFLATE",
|
|
1364
|
-
compressionOptions: { level: 6 }
|
|
1365
|
-
}),
|
|
1134
|
+
buffer: (await zip.generate({ level: 6 })).buffer,
|
|
1366
1135
|
rId: newRId
|
|
1367
1136
|
};
|
|
1368
1137
|
}
|
|
@@ -1376,36 +1145,24 @@ async function addRelationship(originalBuffer, relationship) {
|
|
|
1376
1145
|
* @returns Promise resolving to { buffer: ArrayBuffer, rId: string, path: string }
|
|
1377
1146
|
*/
|
|
1378
1147
|
async function addMedia(originalBuffer, filename, data, mimeType) {
|
|
1379
|
-
const zip = await
|
|
1148
|
+
const zip = await ZipArchive.read(originalBuffer);
|
|
1380
1149
|
const mediaPath = `word/media/${filename}`;
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
path: mediaPath,
|
|
1384
|
-
data: new Uint8Array(data)
|
|
1385
|
-
});
|
|
1386
|
-
const relResult = await addRelationship(await zip.generateAsync({ type: "arraybuffer" }), {
|
|
1150
|
+
zip.set(mediaPath, new Uint8Array(data));
|
|
1151
|
+
const relResult = await addRelationship((await zip.generate({ compression: "store" })).buffer, {
|
|
1387
1152
|
type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
|
1388
1153
|
target: `media/${filename}`
|
|
1389
1154
|
});
|
|
1390
|
-
const contentTypesFile = zip.
|
|
1155
|
+
const contentTypesFile = zip.entry("[Content_Types].xml");
|
|
1391
1156
|
if (contentTypesFile) {
|
|
1392
|
-
const contentTypesXml = await contentTypesFile.
|
|
1157
|
+
const contentTypesXml = await contentTypesFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
1393
1158
|
const extension = filename.split(".").pop()?.toLowerCase() || "";
|
|
1394
1159
|
if (!contentTypesXml.includes(`Extension="${extension}"`) && extension) {
|
|
1395
1160
|
const extensionElement = `<Default Extension="${extension}" ContentType="${getContentTypeForExtension(extension, mimeType)}"/>`;
|
|
1396
1161
|
const updatedContentTypes = contentTypesXml.replace("</Types>", `${extensionElement}</Types>`);
|
|
1397
|
-
const finalZip = await
|
|
1398
|
-
|
|
1399
|
-
zip: finalZip,
|
|
1400
|
-
path: "[Content_Types].xml",
|
|
1401
|
-
data: encodeUtf8(updatedContentTypes)
|
|
1402
|
-
});
|
|
1162
|
+
const finalZip = await ZipArchive.read(relResult.buffer);
|
|
1163
|
+
finalZip.set("[Content_Types].xml", updatedContentTypes);
|
|
1403
1164
|
return {
|
|
1404
|
-
buffer: await finalZip.
|
|
1405
|
-
type: "arraybuffer",
|
|
1406
|
-
compression: "DEFLATE",
|
|
1407
|
-
compressionOptions: { level: 6 }
|
|
1408
|
-
}),
|
|
1165
|
+
buffer: (await finalZip.generate({ level: 6 })).buffer,
|
|
1409
1166
|
rId: relResult.rId,
|
|
1410
1167
|
path: mediaPath
|
|
1411
1168
|
};
|
|
@@ -1445,13 +1202,13 @@ function hasUnmaterializedHeaderFooter(doc) {
|
|
|
1445
1202
|
function findMaxHeaderFooterNum(zip, prefix) {
|
|
1446
1203
|
let max = 0;
|
|
1447
1204
|
const pattern = new RegExp(`^word/${prefix}(\\d+)\\.xml$`, "u");
|
|
1448
|
-
zip.
|
|
1205
|
+
for (const relativePath of zip.paths()) {
|
|
1449
1206
|
const m = pattern.exec(relativePath);
|
|
1450
1207
|
if (m) {
|
|
1451
1208
|
const num = Number.parseInt(m[1], 10);
|
|
1452
1209
|
if (num > max) max = num;
|
|
1453
1210
|
}
|
|
1454
|
-
}
|
|
1211
|
+
}
|
|
1455
1212
|
return max;
|
|
1456
1213
|
}
|
|
1457
1214
|
/**
|
|
@@ -1545,28 +1302,12 @@ async function materializeNewHeaderFooterParts(doc, zip, compressionLevel) {
|
|
|
1545
1302
|
materialize(doc.package.headers, RELATIONSHIP_TYPES.header, "header", HEADER_CONTENT_TYPE, true);
|
|
1546
1303
|
materialize(doc.package.footers, RELATIONSHIP_TYPES.footer, "footer", FOOTER_CONTENT_TYPE, false);
|
|
1547
1304
|
const compressionOptions = { level: compressionLevel };
|
|
1548
|
-
if (relEntries.length > 0)
|
|
1549
|
-
|
|
1550
|
-
path: relsPath,
|
|
1551
|
-
data: encodeUtf8(relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`)),
|
|
1552
|
-
options: {
|
|
1553
|
-
compression: "DEFLATE",
|
|
1554
|
-
compressionOptions
|
|
1555
|
-
}
|
|
1556
|
-
});
|
|
1557
|
-
const ctFile = zip.file("[Content_Types].xml");
|
|
1305
|
+
if (relEntries.length > 0) zip.set(relsPath, relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`), compressionOptions);
|
|
1306
|
+
const ctFile = zip.entry("[Content_Types].xml");
|
|
1558
1307
|
if (ctFile) {
|
|
1559
|
-
const original = await ctFile.
|
|
1308
|
+
const original = await ctFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
1560
1309
|
const updated = registerContentTypeParts(original, contentTypeParts);
|
|
1561
|
-
if (updated !== original)
|
|
1562
|
-
zip,
|
|
1563
|
-
path: "[Content_Types].xml",
|
|
1564
|
-
data: encodeUtf8(updated),
|
|
1565
|
-
options: {
|
|
1566
|
-
compression: "DEFLATE",
|
|
1567
|
-
compressionOptions
|
|
1568
|
-
}
|
|
1569
|
-
});
|
|
1310
|
+
if (updated !== original) zip.set("[Content_Types].xml", updated, compressionOptions);
|
|
1570
1311
|
}
|
|
1571
1312
|
return doc;
|
|
1572
1313
|
}
|
|
@@ -1672,7 +1413,7 @@ async function rebindWatermarkRelIds({ document: doc, zip, compressionLevel, bod
|
|
|
1672
1413
|
if (bodyAuthority === "canonical") throw new DocxPackageFidelityError("A canonical picture watermark image cannot be resolved");
|
|
1673
1414
|
continue;
|
|
1674
1415
|
}
|
|
1675
|
-
if (bodyAuthority === "canonical" && canonical.mode === "internal" && !zip.
|
|
1416
|
+
if (bodyAuthority === "canonical" && canonical.mode === "internal" && !zip.entry(canonical.absolute)) throw new DocxPackageFidelityError("A canonical picture watermark image part is missing");
|
|
1676
1417
|
const relsXml = relsXmlByPath.get(relsPath) ?? EMPTY_RELS_XML;
|
|
1677
1418
|
const localRels = parseRelationships(relsXml);
|
|
1678
1419
|
const local = relImage(localRels.get(watermark.imageRId), relsPath);
|
|
@@ -1712,15 +1453,7 @@ async function rebindWatermarkRelIds({ document: doc, zip, compressionLevel, bod
|
|
|
1712
1453
|
const compressionOptions = { level: compressionLevel };
|
|
1713
1454
|
for (const path of changedPaths) {
|
|
1714
1455
|
const xml = relsXmlByPath.get(path);
|
|
1715
|
-
if (xml)
|
|
1716
|
-
zip,
|
|
1717
|
-
path,
|
|
1718
|
-
data: encodeUtf8(xml),
|
|
1719
|
-
options: {
|
|
1720
|
-
compression: "DEFLATE",
|
|
1721
|
-
compressionOptions
|
|
1722
|
-
}
|
|
1723
|
-
});
|
|
1456
|
+
if (xml) zip.set(path, xml, compressionOptions);
|
|
1724
1457
|
}
|
|
1725
1458
|
}
|
|
1726
1459
|
async function collectHeaderFooterUpdates(doc, { sourceZip, onDiagnostic }) {
|
|
@@ -1742,7 +1475,7 @@ async function collectHeaderFooterUpdates(doc, { sourceZip, onDiagnostic }) {
|
|
|
1742
1475
|
if (rel && rel.type === type && rel.target) {
|
|
1743
1476
|
const path = resolveRelativePath(documentRelsPath, rel.target);
|
|
1744
1477
|
const sourceFile = findZipEntryCaseInsensitive(sourceZip, path.toLowerCase());
|
|
1745
|
-
const bindings = sourceFile ? readRootNamespaceBindings(await sourceFile.
|
|
1478
|
+
const bindings = sourceFile ? readRootNamespaceBindings(await sourceFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }))) : /* @__PURE__ */ new Map();
|
|
1746
1479
|
updates.set(path, serializeHeaderFooter(headerFooter, {
|
|
1747
1480
|
path,
|
|
1748
1481
|
bindings,
|
|
@@ -1759,15 +1492,7 @@ async function serializeHeadersFootersToZip({ doc, zip, compressionLevel, onDiag
|
|
|
1759
1492
|
for (const [filename, xml] of await collectHeaderFooterUpdates(doc, {
|
|
1760
1493
|
sourceZip: zip,
|
|
1761
1494
|
onDiagnostic
|
|
1762
|
-
}))
|
|
1763
|
-
zip,
|
|
1764
|
-
path: filename,
|
|
1765
|
-
data: encodeUtf8(xml),
|
|
1766
|
-
options: {
|
|
1767
|
-
compression: "DEFLATE",
|
|
1768
|
-
compressionOptions
|
|
1769
|
-
}
|
|
1770
|
-
});
|
|
1495
|
+
})) zip.set(filename, xml, compressionOptions);
|
|
1771
1496
|
}
|
|
1772
1497
|
async function serializeNotesToZip({ doc, originalZip, newZip, compressionLevel, changedNoteParaIds }) {
|
|
1773
1498
|
const footnotes = doc.package.footnotes ?? [];
|
|
@@ -1822,46 +1547,22 @@ async function materializeNewNotePart({ contentType, newZip, partPath, relations
|
|
|
1822
1547
|
const relationshipsFile = findNotePartEntry(newZip, "word/_rels/document.xml.rels");
|
|
1823
1548
|
if (!contentTypesFile || !relationshipsFile) throw new DocxPackageFidelityError(`Cannot create ${partPath}: the DOCX package is missing content types or document relationships`);
|
|
1824
1549
|
const compressionOptions = { level: compressionLevel };
|
|
1825
|
-
const contentTypesXml = await contentTypesFile.
|
|
1550
|
+
const contentTypesXml = await contentTypesFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
1826
1551
|
const partName = `/${partPath}`;
|
|
1827
|
-
if (!contentTypesXml.toLowerCase().includes(partName.toLowerCase()))
|
|
1828
|
-
|
|
1829
|
-
path: contentTypesFile.name,
|
|
1830
|
-
data: encodeUtf8(contentTypesXml.replace("</Types>", () => `<Override PartName="${partName}" ContentType="${contentType}"/></Types>`)),
|
|
1831
|
-
options: {
|
|
1832
|
-
compression: "DEFLATE",
|
|
1833
|
-
compressionOptions
|
|
1834
|
-
}
|
|
1835
|
-
});
|
|
1836
|
-
const relationshipsXml = await relationshipsFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
1552
|
+
if (!contentTypesXml.toLowerCase().includes(partName.toLowerCase())) newZip.set(contentTypesFile.path, contentTypesXml.replace("</Types>", () => `<Override PartName="${partName}" ContentType="${contentType}"/></Types>`), compressionOptions);
|
|
1553
|
+
const relationshipsXml = await relationshipsFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
1837
1554
|
if (!relationshipsXml.includes(relationshipType)) {
|
|
1838
1555
|
const relationshipId = `rId${findMaxRId(relationshipsXml) + 1}`;
|
|
1839
1556
|
const target = partPath.slice(5);
|
|
1840
|
-
|
|
1841
|
-
zip: newZip,
|
|
1842
|
-
path: relationshipsFile.name,
|
|
1843
|
-
data: encodeUtf8(relationshipsXml.replace("</Relationships>", () => `<Relationship Id="${relationshipId}" Type="${relationshipType}" Target="${target}"/></Relationships>`)),
|
|
1844
|
-
options: {
|
|
1845
|
-
compression: "DEFLATE",
|
|
1846
|
-
compressionOptions
|
|
1847
|
-
}
|
|
1848
|
-
});
|
|
1557
|
+
newZip.set(relationshipsFile.path, relationshipsXml.replace("</Relationships>", () => `<Relationship Id="${relationshipId}" Type="${relationshipType}" Target="${target}"/></Relationships>`), compressionOptions);
|
|
1849
1558
|
}
|
|
1850
|
-
|
|
1851
|
-
zip: newZip,
|
|
1852
|
-
path: partPath,
|
|
1853
|
-
data: encodeUtf8(serializedPart),
|
|
1854
|
-
options: {
|
|
1855
|
-
compression: "DEFLATE",
|
|
1856
|
-
compressionOptions
|
|
1857
|
-
}
|
|
1858
|
-
});
|
|
1559
|
+
newZip.set(partPath, serializedPart, compressionOptions);
|
|
1859
1560
|
}
|
|
1860
1561
|
/** Only this modeled switch changes on story lifecycle edits; preserve all other settings. */
|
|
1861
1562
|
async function serializeHeaderFooterSettingsIntoZip(doc, zip, compressionLevel) {
|
|
1862
1563
|
if (!doc.package.settings) return;
|
|
1863
1564
|
const enabled = doc.package.settings.evenAndOddHeaders;
|
|
1864
|
-
const file = zip.
|
|
1565
|
+
const file = zip.entry(SETTINGS_PART);
|
|
1865
1566
|
if (!file) {
|
|
1866
1567
|
if (enabled === void 0) return;
|
|
1867
1568
|
await materializeNewNotePart({
|
|
@@ -1874,19 +1575,11 @@ async function serializeHeaderFooterSettingsIntoZip(doc, zip, compressionLevel)
|
|
|
1874
1575
|
});
|
|
1875
1576
|
return;
|
|
1876
1577
|
}
|
|
1877
|
-
const xml = await file.
|
|
1578
|
+
const xml = await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
1878
1579
|
if (parseSettings(xml).evenAndOddHeaders === enabled) return;
|
|
1879
1580
|
const patched = updateEvenAndOddHeaders(xml, enabled);
|
|
1880
1581
|
if (patched === null) throw new DocxPackageFidelityError("Cannot update malformed header/footer settings");
|
|
1881
|
-
|
|
1882
|
-
zip,
|
|
1883
|
-
path: file.name,
|
|
1884
|
-
data: encodeUtf8(patched),
|
|
1885
|
-
options: {
|
|
1886
|
-
compression: "DEFLATE",
|
|
1887
|
-
compressionOptions: { level: compressionLevel }
|
|
1888
|
-
}
|
|
1889
|
-
});
|
|
1582
|
+
zip.set(file.path, patched, { level: compressionLevel });
|
|
1890
1583
|
}
|
|
1891
1584
|
const numberingBaselines = /* @__PURE__ */ new WeakMap();
|
|
1892
1585
|
async function serializeNumberingIntoZip(doc, originalZip, newZip, compressionLevel) {
|
|
@@ -1906,7 +1599,7 @@ async function serializeNumberingIntoZip(doc, originalZip, newZip, compressionLe
|
|
|
1906
1599
|
}
|
|
1907
1600
|
let baseline = doc.originalBuffer ? numberingBaselines.get(doc) : void 0;
|
|
1908
1601
|
if (!baseline || baseline.originalBuffer !== doc.originalBuffer) {
|
|
1909
|
-
const originalXml = await file.
|
|
1602
|
+
const originalXml = await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
1910
1603
|
baseline = {
|
|
1911
1604
|
originalBuffer: doc.originalBuffer,
|
|
1912
1605
|
originalXml,
|
|
@@ -1921,15 +1614,7 @@ async function serializeNumberingIntoZip(doc, originalZip, newZip, compressionLe
|
|
|
1921
1614
|
currentXml
|
|
1922
1615
|
});
|
|
1923
1616
|
if (patched === null || patched === baseline.originalXml) return;
|
|
1924
|
-
|
|
1925
|
-
zip: newZip,
|
|
1926
|
-
path: file.name,
|
|
1927
|
-
data: encodeUtf8(patched),
|
|
1928
|
-
options: {
|
|
1929
|
-
compression: "DEFLATE",
|
|
1930
|
-
compressionOptions: { level: compressionLevel }
|
|
1931
|
-
}
|
|
1932
|
-
});
|
|
1617
|
+
newZip.set(file.path, patched, { level: compressionLevel });
|
|
1933
1618
|
}
|
|
1934
1619
|
const STYLES_PART_PATH = "word/styles.xml";
|
|
1935
1620
|
const STYLES_CLOSE_ROOT = "</w:styles>";
|
|
@@ -2041,7 +1726,7 @@ async function planAddedStyles(doc, originalZip) {
|
|
|
2041
1726
|
const styles = doc.package.styles;
|
|
2042
1727
|
if (!styles || styles.styles.length === 0) return { type: "unchanged" };
|
|
2043
1728
|
const file = findNotePartEntry(originalZip, STYLES_PART_PATH);
|
|
2044
|
-
const originalXml = file ? await file.
|
|
1729
|
+
const originalXml = file ? await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })) : null;
|
|
2045
1730
|
const rootName = originalXml === null ? void 0 : /<(?![?!])(?<name>[^\s/>]+)/u.exec(originalXml)?.groups?.["name"];
|
|
2046
1731
|
const rootClose = originalXml !== null && rootName !== void 0 && getLocalName(rootName) === "styles" ? originalXml.lastIndexOf(`</${rootName}>`) : -1;
|
|
2047
1732
|
if (file === null || originalXml === null || rootClose < 0) return {
|
|
@@ -2057,10 +1742,10 @@ async function planAddedStyles(doc, originalZip) {
|
|
|
2057
1742
|
}
|
|
2058
1743
|
if (added.length === 0) return { type: "unchanged" };
|
|
2059
1744
|
const canonical = hasCanonicalWordprocessingPrefixes(originalXml);
|
|
2060
|
-
const patched = originalXml.slice(0, rootClose) + added.map((style) => canonical ? serializeStyle(style) : selfBoundFragment(serializeStyle(style), file.
|
|
1745
|
+
const patched = originalXml.slice(0, rootClose) + added.map((style) => canonical ? serializeStyle(style) : selfBoundFragment(serializeStyle(style), file.path)).join("") + originalXml.slice(rootClose);
|
|
2061
1746
|
return {
|
|
2062
1747
|
type: "patch",
|
|
2063
|
-
path: file.
|
|
1748
|
+
path: file.path,
|
|
2064
1749
|
xml: patched
|
|
2065
1750
|
};
|
|
2066
1751
|
}
|
|
@@ -2079,15 +1764,7 @@ async function serializeAddedStylesIntoZip(doc, originalZip, newZip, compression
|
|
|
2079
1764
|
});
|
|
2080
1765
|
return;
|
|
2081
1766
|
case "patch":
|
|
2082
|
-
|
|
2083
|
-
zip: newZip,
|
|
2084
|
-
path: plan.path,
|
|
2085
|
-
data: encodeUtf8(plan.xml),
|
|
2086
|
-
options: {
|
|
2087
|
-
compression: "DEFLATE",
|
|
2088
|
-
compressionOptions: { level: compressionLevel }
|
|
2089
|
-
}
|
|
2090
|
-
});
|
|
1767
|
+
newZip.set(plan.path, plan.xml, { level: compressionLevel });
|
|
2091
1768
|
return;
|
|
2092
1769
|
default: return plan;
|
|
2093
1770
|
}
|
|
@@ -2125,7 +1802,7 @@ const notePartXmlFor = ({ patch, originalXml, replacementXml, hasDirtyParagraph,
|
|
|
2125
1802
|
async function patchNotePartIntoZip({ conventionalLowerPath, currentXml, replacementXml, baselineFrom, elementName, changedNoteParaIds, originalZip, newZip, compressionLevel }) {
|
|
2126
1803
|
const file = findNotePartEntry(originalZip, conventionalLowerPath);
|
|
2127
1804
|
if (!file) return;
|
|
2128
|
-
const originalXml = await file.
|
|
1805
|
+
const originalXml = await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
2129
1806
|
const baselineXml = baselineFrom(originalXml);
|
|
2130
1807
|
const effectiveChangedNoteParaIds = changedNoteParaIds ?? collectChangedNoteParaIds(baselineXml, currentXml);
|
|
2131
1808
|
const currentParaIds = collectParaIds(currentXml);
|
|
@@ -2142,18 +1819,10 @@ async function patchNotePartIntoZip({ conventionalLowerPath, currentXml, replace
|
|
|
2142
1819
|
replacementXml,
|
|
2143
1820
|
hasDirtyParagraph: [...effectiveChangedNoteParaIds].some((paraId) => currentParaIds.has(paraId)),
|
|
2144
1821
|
elementName,
|
|
2145
|
-
partName: file.
|
|
1822
|
+
partName: file.path
|
|
2146
1823
|
});
|
|
2147
1824
|
if (patchedXml === null || patchedXml === originalXml) return;
|
|
2148
|
-
|
|
2149
|
-
zip: newZip,
|
|
2150
|
-
path: file.name,
|
|
2151
|
-
data: encodeUtf8(patchedXml),
|
|
2152
|
-
options: {
|
|
2153
|
-
compression: "DEFLATE",
|
|
2154
|
-
compressionOptions: { level: compressionLevel }
|
|
2155
|
-
}
|
|
2156
|
-
});
|
|
1825
|
+
newZip.set(file.path, patchedXml, { level: compressionLevel });
|
|
2157
1826
|
}
|
|
2158
1827
|
/** `word/Footnotes.xml` -> `word/_rels/Footnotes.xml.rels` (casing preserved). */
|
|
2159
1828
|
function notePartRelsPath(partPath) {
|
|
@@ -2165,9 +1834,12 @@ function notePartRelsPath(partPath) {
|
|
|
2165
1834
|
* cased the entry differently still resolves to the existing part.
|
|
2166
1835
|
*/
|
|
2167
1836
|
function findNotePartEntry(zip, conventionalLowerPath) {
|
|
2168
|
-
const direct = zip.
|
|
1837
|
+
const direct = zip.entry(conventionalLowerPath);
|
|
2169
1838
|
if (direct) return direct;
|
|
2170
|
-
for (const
|
|
1839
|
+
for (const file of zip.entries()) {
|
|
1840
|
+
const { path } = file;
|
|
1841
|
+
if (path.toLowerCase() === conventionalLowerPath) return file;
|
|
1842
|
+
}
|
|
2171
1843
|
return null;
|
|
2172
1844
|
}
|
|
2173
1845
|
/** Update existing core-property values without synthesizing absent metadata. */
|
|
@@ -2249,11 +1921,8 @@ function createEmptyDocx(properties = {}) {
|
|
|
2249
1921
|
return generateDocxZip(createEmptyDocxZip(properties), 6);
|
|
2250
1922
|
}
|
|
2251
1923
|
const createEmptyDocxZip = ({ creator, application }) => {
|
|
2252
|
-
const zip = new
|
|
2253
|
-
|
|
2254
|
-
zip,
|
|
2255
|
-
path: "[Content_Types].xml",
|
|
2256
|
-
data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1924
|
+
const zip = new ZipArchive();
|
|
1925
|
+
zip.set("[Content_Types].xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2257
1926
|
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
2258
1927
|
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
2259
1928
|
<Default Extension="xml" ContentType="application/xml"/>
|
|
@@ -2261,30 +1930,18 @@ const createEmptyDocxZip = ({ creator, application }) => {
|
|
|
2261
1930
|
<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
|
|
2262
1931
|
<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
|
|
2263
1932
|
<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
|
|
2264
|
-
</Types>`)
|
|
2265
|
-
|
|
2266
|
-
writeZipPart({
|
|
2267
|
-
zip,
|
|
2268
|
-
path: "_rels/.rels",
|
|
2269
|
-
data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1933
|
+
</Types>`);
|
|
1934
|
+
zip.set("_rels/.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2270
1935
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
2271
1936
|
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
|
|
2272
1937
|
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
|
|
2273
1938
|
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
|
|
2274
|
-
</Relationships>`)
|
|
2275
|
-
|
|
2276
|
-
writeZipPart({
|
|
2277
|
-
zip,
|
|
2278
|
-
path: "word/_rels/document.xml.rels",
|
|
2279
|
-
data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1939
|
+
</Relationships>`);
|
|
1940
|
+
zip.set("word/_rels/document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2280
1941
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
2281
1942
|
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
|
|
2282
|
-
</Relationships>`)
|
|
2283
|
-
|
|
2284
|
-
writeZipPart({
|
|
2285
|
-
zip,
|
|
2286
|
-
path: "word/document.xml",
|
|
2287
|
-
data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1943
|
+
</Relationships>`);
|
|
1944
|
+
zip.set("word/document.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2288
1945
|
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
2289
1946
|
<w:body>
|
|
2290
1947
|
<w:p>
|
|
@@ -2297,32 +1954,19 @@ const createEmptyDocxZip = ({ creator, application }) => {
|
|
|
2297
1954
|
<w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/>
|
|
2298
1955
|
</w:sectPr>
|
|
2299
1956
|
</w:body>
|
|
2300
|
-
</w:document>`)
|
|
2301
|
-
|
|
2302
|
-
writeZipPart({
|
|
2303
|
-
zip,
|
|
2304
|
-
path: STYLES_PART_PATH,
|
|
2305
|
-
data: encodeUtf8(SEED_STYLES_XML)
|
|
2306
|
-
});
|
|
1957
|
+
</w:document>`);
|
|
1958
|
+
zip.set(STYLES_PART_PATH, SEED_STYLES_XML);
|
|
2307
1959
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2308
1960
|
const creatorElement = creator === void 0 ? "" : `\n <dc:creator>${escapeXmlText(creator)}</dc:creator>`;
|
|
2309
|
-
|
|
2310
|
-
zip,
|
|
2311
|
-
path: "docProps/core.xml",
|
|
2312
|
-
data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1961
|
+
zip.set("docProps/core.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2313
1962
|
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">${creatorElement}
|
|
2314
1963
|
<dcterms:created xsi:type="dcterms:W3CDTF">${now}</dcterms:created>
|
|
2315
1964
|
<dcterms:modified xsi:type="dcterms:W3CDTF">${now}</dcterms:modified>
|
|
2316
|
-
</cp:coreProperties>`)
|
|
2317
|
-
});
|
|
1965
|
+
</cp:coreProperties>`);
|
|
2318
1966
|
const applicationElements = application === void 0 ? "" : `\n <Application>${escapeXmlText(application)}</Application>\n <AppVersion>${CREATED_APP_VERSION}</AppVersion>`;
|
|
2319
|
-
|
|
2320
|
-
zip,
|
|
2321
|
-
path: "docProps/app.xml",
|
|
2322
|
-
data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1967
|
+
zip.set("docProps/app.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2323
1968
|
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">${applicationElements}
|
|
2324
|
-
</Properties>`)
|
|
2325
|
-
});
|
|
1969
|
+
</Properties>`);
|
|
2326
1970
|
return zip;
|
|
2327
1971
|
};
|
|
2328
1972
|
/**
|
|
@@ -2354,7 +1998,7 @@ async function createDocx(doc, properties = {}) {
|
|
|
2354
1998
|
originalZip: zip,
|
|
2355
1999
|
outputZip: zip,
|
|
2356
2000
|
originalDocument: void 0,
|
|
2357
|
-
originalCorePropertiesXml: await zip.
|
|
2001
|
+
originalCorePropertiesXml: await zip.entry("docProps/core.xml")?.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })),
|
|
2358
2002
|
compressionLevel: 6,
|
|
2359
2003
|
updateModifiedDate: true,
|
|
2360
2004
|
onDiagnostic: properties.onDiagnostic,
|
|
@@ -2369,70 +2013,38 @@ const createDocumentSeedZip = async (doc, properties) => {
|
|
|
2369
2013
|
const styleDefinitions = styleDefinitionsToSerialize(doc);
|
|
2370
2014
|
if (styleDefinitions) {
|
|
2371
2015
|
assertStyleNumberingReferences(doc);
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
path: STYLES_PART_PATH,
|
|
2375
|
-
data: encodeUtf8(serializeStylesXml(styleDefinitions))
|
|
2376
|
-
});
|
|
2377
|
-
} else writeZipPart({
|
|
2378
|
-
zip,
|
|
2379
|
-
path: STYLES_PART_PATH,
|
|
2380
|
-
data: encodeUtf8(seedStylesXmlWith(missingNoteReferenceStyles(void 0, noteReferenceNeeds(doc.package))))
|
|
2381
|
-
});
|
|
2016
|
+
zip.set(STYLES_PART_PATH, serializeStylesXml(styleDefinitions));
|
|
2017
|
+
} else zip.set(STYLES_PART_PATH, seedStylesXmlWith(missingNoteReferenceStyles(void 0, noteReferenceNeeds(doc.package))));
|
|
2382
2018
|
const numbering = doc.package.numbering;
|
|
2383
2019
|
if (numbering && (numbering.abstractNums.length > 0 || numbering.nums.length > 0)) {
|
|
2384
|
-
|
|
2385
|
-
zip,
|
|
2386
|
-
path: "word/numbering.xml",
|
|
2387
|
-
data: encodeUtf8(serializeNumberingXml(numbering))
|
|
2388
|
-
});
|
|
2020
|
+
zip.set("word/numbering.xml", serializeNumberingXml(numbering));
|
|
2389
2021
|
relationships.push(relationshipXml(nextRelationshipId, "numbering", "numbering.xml"));
|
|
2390
2022
|
overrides.push(overrideXml("/word/numbering.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"));
|
|
2391
2023
|
nextRelationshipId += 1;
|
|
2392
2024
|
}
|
|
2393
2025
|
if (doc.package.theme) {
|
|
2394
|
-
|
|
2395
|
-
zip,
|
|
2396
|
-
path: "word/theme/theme1.xml",
|
|
2397
|
-
data: encodeUtf8(serializeThemeXml(doc.package.theme))
|
|
2398
|
-
});
|
|
2026
|
+
zip.set("word/theme/theme1.xml", serializeThemeXml(doc.package.theme));
|
|
2399
2027
|
relationships.push(relationshipXml(nextRelationshipId, "theme", "theme/theme1.xml"));
|
|
2400
2028
|
overrides.push(overrideXml("/word/theme/theme1.xml", "application/vnd.openxmlformats-officedocument.theme+xml"));
|
|
2401
2029
|
nextRelationshipId += 1;
|
|
2402
2030
|
}
|
|
2403
2031
|
if (doc.package.fontTable && doc.package.fontTable.fonts.length > 0) {
|
|
2404
|
-
|
|
2405
|
-
zip,
|
|
2406
|
-
path: "word/fontTable.xml",
|
|
2407
|
-
data: encodeUtf8(serializeFontTableXml(doc.package.fontTable))
|
|
2408
|
-
});
|
|
2032
|
+
zip.set("word/fontTable.xml", serializeFontTableXml(doc.package.fontTable));
|
|
2409
2033
|
relationships.push(relationshipXml(nextRelationshipId, "fontTable", "fontTable.xml"));
|
|
2410
2034
|
overrides.push(overrideXml("/word/fontTable.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"));
|
|
2411
2035
|
nextRelationshipId += 1;
|
|
2412
2036
|
}
|
|
2413
2037
|
if (doc.package.settings) {
|
|
2414
|
-
|
|
2415
|
-
zip,
|
|
2416
|
-
path: "word/settings.xml",
|
|
2417
|
-
data: encodeUtf8(serializeSettingsXml(doc.package.settings))
|
|
2418
|
-
});
|
|
2038
|
+
zip.set("word/settings.xml", serializeSettingsXml(doc.package.settings));
|
|
2419
2039
|
relationships.push(relationshipXml(nextRelationshipId, "settings", "settings.xml"));
|
|
2420
2040
|
overrides.push(overrideXml("/word/settings.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"));
|
|
2421
2041
|
}
|
|
2422
|
-
|
|
2423
|
-
zip,
|
|
2424
|
-
path: "word/_rels/document.xml.rels",
|
|
2425
|
-
data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${relationships.join("")}</Relationships>`)
|
|
2426
|
-
});
|
|
2042
|
+
zip.set("word/_rels/document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${relationships.join("")}</Relationships>`);
|
|
2427
2043
|
if (overrides.length > 0) {
|
|
2428
|
-
const contentTypesFile = zip.
|
|
2044
|
+
const contentTypesFile = zip.entry("[Content_Types].xml");
|
|
2429
2045
|
if (!contentTypesFile) panic("Fresh DOCX seed is missing [Content_Types].xml");
|
|
2430
|
-
const contentTypes = await contentTypesFile.
|
|
2431
|
-
|
|
2432
|
-
zip,
|
|
2433
|
-
path: "[Content_Types].xml",
|
|
2434
|
-
data: encodeUtf8(contentTypes.replace("</Types>", `${overrides.join("")}\n</Types>`))
|
|
2435
|
-
});
|
|
2046
|
+
const contentTypes = await contentTypesFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
2047
|
+
zip.set("[Content_Types].xml", contentTypes.replace("</Types>", `${overrides.join("")}\n</Types>`));
|
|
2436
2048
|
}
|
|
2437
2049
|
return zip;
|
|
2438
2050
|
};
|