@office-open/core 0.10.12 → 0.10.13

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.
Files changed (62) hide show
  1. package/README.md +10 -9
  2. package/dist/chart/index.d.mts +1 -1
  3. package/dist/chart/index.mjs +1 -1
  4. package/dist/{chart-DwE8FCFk.mjs → chart-DOO1st0K.mjs} +3 -1
  5. package/dist/chart-DOO1st0K.mjs.map +1 -0
  6. package/dist/converters-C_Y2QX1w.mjs +1789 -0
  7. package/dist/converters-C_Y2QX1w.mjs.map +1 -0
  8. package/dist/data-type-CZMKVaaT.d.mts +10 -0
  9. package/dist/data-type-CZMKVaaT.d.mts.map +1 -0
  10. package/dist/descriptor/index.d.mts +2 -2
  11. package/dist/descriptor/index.mjs +2 -2
  12. package/dist/{descriptor-DAER86Rt.mjs → descriptor-BjNkn5jo.mjs} +3 -33
  13. package/dist/descriptor-BjNkn5jo.mjs.map +1 -0
  14. package/dist/drawingml/index.d.mts +2 -2
  15. package/dist/drawingml/index.mjs +2 -2
  16. package/dist/{src-bVqU5Bh3.mjs → drawingml-sj1J3lb0.mjs} +22 -3728
  17. package/dist/drawingml-sj1J3lb0.mjs.map +1 -0
  18. package/dist/index-BVK2dv6C.d.mts +63 -0
  19. package/dist/index-BVK2dv6C.d.mts.map +1 -0
  20. package/dist/index-BfHfKuKy.d.mts +275 -0
  21. package/dist/index-BfHfKuKy.d.mts.map +1 -0
  22. package/dist/index-CE53SwpZ.d.mts +1463 -0
  23. package/dist/index-CE53SwpZ.d.mts.map +1 -0
  24. package/dist/{index-3STznXzZ.d.mts → index-D6VE-MnJ.d.mts} +3 -15
  25. package/dist/index-D6VE-MnJ.d.mts.map +1 -0
  26. package/dist/{index-CN0YjNSx.d.mts → index-DDyd2ErZ.d.mts} +3 -15
  27. package/dist/index-DDyd2ErZ.d.mts.map +1 -0
  28. package/dist/{index-CsQP7Cl4.d.mts → index-DlPA26cw.d.mts} +2 -47
  29. package/dist/index-DlPA26cw.d.mts.map +1 -0
  30. package/dist/index-k8WKLXnA.d.mts +150 -0
  31. package/dist/index-k8WKLXnA.d.mts.map +1 -0
  32. package/dist/index.d.mts +650 -8
  33. package/dist/index.d.mts.map +1 -0
  34. package/dist/index.mjs +1271 -7
  35. package/dist/index.mjs.map +1 -0
  36. package/dist/output-D_G_JR_8.d.mts +24 -0
  37. package/dist/output-D_G_JR_8.d.mts.map +1 -0
  38. package/dist/patch/index.d.mts +2 -2
  39. package/dist/patch/index.mjs +1 -1
  40. package/dist/{patch-OSbQIQAi.mjs → patch-BmRfghOo.mjs} +65 -84
  41. package/dist/patch-BmRfghOo.mjs.map +1 -0
  42. package/dist/smartart/index.d.mts +1 -1
  43. package/dist/smartart/index.mjs +1 -1
  44. package/dist/{smartart-DCY-Vdv7.mjs → smartart-B7ukZ-Tw.mjs} +5 -3
  45. package/dist/smartart-B7ukZ-Tw.mjs.map +1 -0
  46. package/dist/theme/index.d.mts +1 -1
  47. package/dist/theme-CiNzdl-9.mjs +2 -0
  48. package/dist/theme-CiNzdl-9.mjs.map +1 -0
  49. package/dist/util/index.d.mts +4 -0
  50. package/dist/util/index.mjs +3 -0
  51. package/dist/util-Tq9PSjK0.mjs +1113 -0
  52. package/dist/util-Tq9PSjK0.mjs.map +1 -0
  53. package/dist/values-8di32lIe.d.mts +56 -0
  54. package/dist/values-8di32lIe.d.mts.map +1 -0
  55. package/package.json +7 -6
  56. package/dist/index-CHGFwCCQ.d.mts +0 -266
  57. package/dist/index-CpNwAcem.d.mts +0 -169
  58. package/dist/index-DdXU1_sq.d.mts +0 -5029
  59. package/dist/util/values.d.mts +0 -2
  60. package/dist/util/values.mjs +0 -2
  61. package/dist/values-CVIZcTRw.mjs +0 -371
  62. package/dist/values-DQfI1FSg.d.mts +0 -426
@@ -0,0 +1,1113 @@
1
+ import { U as encodeBase64, W as Buffer } from "./converters-C_Y2QX1w.mjs";
2
+ import { AsyncZipDeflate, Zip, ZipPassThrough, strFromU8, strFromU8 as strFromU8$1, unzipSync as unzipSync$1, zip, zipSync } from "fflate";
3
+ import { sha512 } from "@noble/hashes/sha2.js";
4
+ //#region src/opc/output.ts
5
+ /**
6
+ * Output type definitions for OOXML document export.
7
+ *
8
+ * @module
9
+ */
10
+ const OoxmlMimeType = {
11
+ DOCX: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
12
+ PPTX: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
13
+ XLSX: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
14
+ };
15
+ const convertOutput = (data, type, mimeType = OoxmlMimeType.DOCX) => {
16
+ switch (type) {
17
+ case "nodebuffer": return Buffer.from(data.buffer, data.byteOffset, data.byteLength);
18
+ case "blob": return new Blob([data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)], { type: mimeType });
19
+ case "arraybuffer": return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
20
+ case "uint8array": return data;
21
+ case "base64": return encodeBase64(data);
22
+ case "string":
23
+ case "text":
24
+ case "binarystring": return strFromU8(data, true);
25
+ case "array": return [...data];
26
+ default: return data;
27
+ }
28
+ };
29
+ //#endregion
30
+ //#region src/opc/zip-native.ts
31
+ const CRC_TABLE = new Int32Array(256);
32
+ for (let i = 0; i < 256; i++) {
33
+ let c = i;
34
+ for (let k = 0; k < 8; k++) c = c & 1 ? c >>> 1 ^ -306674912 : c >>> 1;
35
+ CRC_TABLE[i] = c;
36
+ }
37
+ function computeCrc32(data) {
38
+ let crc = -1;
39
+ for (const byte of data) crc = CRC_TABLE[(crc ^ byte) & 255] ^ crc >>> 8;
40
+ return ~crc;
41
+ }
42
+ let _nativeDeflate;
43
+ let _nativeDeflateAsync;
44
+ let _nativeInflate;
45
+ let _nativeCrc32;
46
+ try {
47
+ const zlib = await import("node:zlib");
48
+ if (typeof zlib.deflateRawSync !== "function") throw new Error("no native deflate");
49
+ _nativeDeflate = (data, level) => zlib.deflateRawSync(data, { level });
50
+ _nativeDeflateAsync = (data, level) => new Promise((resolve, reject) => zlib.deflateRaw(data, { level }, (err, result) => {
51
+ if (err) reject(err);
52
+ else resolve(result);
53
+ }));
54
+ _nativeCrc32 = typeof zlib.crc32 === "function" ? (data) => zlib.crc32(data) : computeCrc32;
55
+ if (typeof zlib.inflateRawSync === "function") _nativeInflate = (data) => zlib.inflateRawSync(data);
56
+ } catch {}
57
+ const hasNativeDeflate = () => _nativeDeflate !== void 0;
58
+ const hasNativeInflate = () => _nativeInflate !== void 0;
59
+ function wU16(b, o, v) {
60
+ b[o] = v & 255;
61
+ b[o + 1] = v >> 8 & 255;
62
+ }
63
+ function wU32(b, o, v) {
64
+ b[o] = v & 255;
65
+ b[o + 1] = v >> 8 & 255;
66
+ b[o + 2] = v >> 16 & 255;
67
+ b[o + 3] = v >> 24 & 255;
68
+ }
69
+ function resolveEntryData(raw, defaultLevel) {
70
+ if (Array.isArray(raw)) return {
71
+ data: raw[0],
72
+ level: raw[1].level ?? defaultLevel
73
+ };
74
+ return {
75
+ data: raw,
76
+ level: defaultLevel
77
+ };
78
+ }
79
+ function compressOne(data, level, deflate) {
80
+ if (level === 0) return {
81
+ compressed: data,
82
+ method: 0
83
+ };
84
+ const compressed = deflate(data, level);
85
+ if (compressed.length >= data.length) return {
86
+ compressed: data,
87
+ method: 0
88
+ };
89
+ return {
90
+ compressed,
91
+ method: 8
92
+ };
93
+ }
94
+ async function compressOneAsync(data, level, deflate) {
95
+ if (level === 0) return {
96
+ compressed: data,
97
+ method: 0
98
+ };
99
+ const compressed = await deflate(data, level);
100
+ if (compressed.length >= data.length) return {
101
+ compressed: data,
102
+ method: 0
103
+ };
104
+ return {
105
+ compressed,
106
+ method: 8
107
+ };
108
+ }
109
+ function writeZipBuffer(entries) {
110
+ let totalSize = 0;
111
+ for (const e of entries) totalSize += 30 + e.filename.length + e.data.length;
112
+ const cdOffset = totalSize;
113
+ let cdSize = 0;
114
+ for (const e of entries) cdSize += 46 + e.filename.length;
115
+ totalSize += cdSize + 22;
116
+ const buf = new Uint8Array(totalSize);
117
+ let offset = 0;
118
+ for (const e of entries) {
119
+ e.localOffset = offset;
120
+ wU32(buf, offset, 67324752);
121
+ wU16(buf, offset + 4, 20);
122
+ wU16(buf, offset + 6, 0);
123
+ wU16(buf, offset + 8, e.method);
124
+ wU16(buf, offset + 10, 0);
125
+ wU16(buf, offset + 12, 0);
126
+ wU32(buf, offset + 14, e.crc);
127
+ wU32(buf, offset + 18, e.data.length);
128
+ wU32(buf, offset + 22, e.uncompressedSize);
129
+ wU16(buf, offset + 26, e.filename.length);
130
+ wU16(buf, offset + 28, 0);
131
+ offset += 30;
132
+ buf.set(e.filename, offset);
133
+ offset += e.filename.length;
134
+ buf.set(e.data, offset);
135
+ offset += e.data.length;
136
+ }
137
+ for (const e of entries) {
138
+ wU32(buf, offset, 33639248);
139
+ wU16(buf, offset + 4, 20);
140
+ wU16(buf, offset + 6, 20);
141
+ wU16(buf, offset + 8, 0);
142
+ wU16(buf, offset + 10, e.method);
143
+ wU16(buf, offset + 12, 0);
144
+ wU16(buf, offset + 14, 0);
145
+ wU32(buf, offset + 16, e.crc);
146
+ wU32(buf, offset + 20, e.data.length);
147
+ wU32(buf, offset + 24, e.uncompressedSize);
148
+ wU16(buf, offset + 28, e.filename.length);
149
+ wU16(buf, offset + 30, 0);
150
+ wU16(buf, offset + 32, 0);
151
+ wU16(buf, offset + 34, 0);
152
+ wU16(buf, offset + 36, 0);
153
+ wU32(buf, offset + 38, 0);
154
+ wU32(buf, offset + 42, e.localOffset);
155
+ offset += 46;
156
+ buf.set(e.filename, offset);
157
+ offset += e.filename.length;
158
+ }
159
+ wU32(buf, offset, 101010256);
160
+ wU16(buf, offset + 4, 0);
161
+ wU16(buf, offset + 6, 0);
162
+ wU16(buf, offset + 8, entries.length);
163
+ wU16(buf, offset + 10, entries.length);
164
+ wU32(buf, offset + 12, cdSize);
165
+ wU32(buf, offset + 16, cdOffset);
166
+ wU16(buf, offset + 20, 0);
167
+ return buf;
168
+ }
169
+ const textEncoder = new TextEncoder();
170
+ const textDecoder = new TextDecoder();
171
+ function nativeZip(files, level = 6) {
172
+ if (!_nativeDeflate) throw new Error("Native deflate not available");
173
+ const crc = _nativeCrc32 ?? computeCrc32;
174
+ const entries = [];
175
+ for (const key of Object.keys(files)) {
176
+ const raw = files[key];
177
+ if (raw === void 0) continue;
178
+ const { data, level: entryLevel } = resolveEntryData(raw, level);
179
+ const c = crc(data);
180
+ const { compressed, method } = compressOne(data, entryLevel, _nativeDeflate);
181
+ entries.push({
182
+ filename: textEncoder.encode(key),
183
+ data: compressed,
184
+ uncompressedSize: data.length,
185
+ crc: c,
186
+ method,
187
+ localOffset: 0
188
+ });
189
+ }
190
+ return writeZipBuffer(entries);
191
+ }
192
+ async function nativeZipAsync(files, level = 6) {
193
+ if (!_nativeDeflateAsync) throw new Error("Native async deflate not available");
194
+ const crc = _nativeCrc32 ?? computeCrc32;
195
+ const entries = [];
196
+ for (const key of Object.keys(files)) {
197
+ const raw = files[key];
198
+ if (raw === void 0) continue;
199
+ const { data, level: entryLevel } = resolveEntryData(raw, level);
200
+ const c = crc(data);
201
+ const { compressed, method } = await compressOneAsync(data, entryLevel, _nativeDeflateAsync);
202
+ entries.push({
203
+ filename: textEncoder.encode(key),
204
+ data: compressed,
205
+ uncompressedSize: data.length,
206
+ crc: c,
207
+ method,
208
+ localOffset: 0
209
+ });
210
+ }
211
+ return writeZipBuffer(entries);
212
+ }
213
+ function rU16(b, o) {
214
+ return b[o] | b[o + 1] << 8;
215
+ }
216
+ function rU32(b, o) {
217
+ return (b[o] | b[o + 1] << 8 | b[o + 2] << 16 | b[o + 3] << 24) >>> 0;
218
+ }
219
+ const EOCD_MAGIC = 101010256;
220
+ const CD_MAGIC = 33639248;
221
+ /** Locate the End-of-Central-Directory record (within a 64 KB comment window). */
222
+ function findEocd(b) {
223
+ const min = Math.max(0, b.length - 65557);
224
+ for (let i = b.length - 22; i >= min; i--) if (rU32(b, i) === EOCD_MAGIC) return i;
225
+ throw new Error("ZIP EOCD record not found");
226
+ }
227
+ /** Walk the central directory and resolve each entry's payload offset. */
228
+ function readCentralDirectory(buf) {
229
+ const eocd = findEocd(buf);
230
+ const count = rU16(buf, eocd + 10);
231
+ const cdOffset = rU32(buf, eocd + 16);
232
+ const entries = [];
233
+ let p = cdOffset;
234
+ for (let i = 0; i < count; i++) {
235
+ if (rU32(buf, p) !== CD_MAGIC) throw new Error(`ZIP central directory corrupt at offset ${p}`);
236
+ const method = rU16(buf, p + 10);
237
+ const crc = rU32(buf, p + 16);
238
+ const compSize = rU32(buf, p + 20);
239
+ const nameLen = rU16(buf, p + 28);
240
+ const extraLen = rU16(buf, p + 30);
241
+ const cmtLen = rU16(buf, p + 32);
242
+ const localOff = rU32(buf, p + 42);
243
+ const name = textDecoder.decode(buf.subarray(p + 46, p + 46 + nameLen));
244
+ const lNameLen = rU16(buf, localOff + 26);
245
+ const lExtraLen = rU16(buf, localOff + 28);
246
+ entries.push({
247
+ name,
248
+ method,
249
+ compSize,
250
+ crc,
251
+ dataStart: localOff + 30 + lNameLen + lExtraLen
252
+ });
253
+ p += 46 + nameLen + extraLen + cmtLen;
254
+ }
255
+ return entries;
256
+ }
257
+ /**
258
+ * Decompress a ZIP archive via `node:zlib` `inflateRawSync`, mirroring fflate's
259
+ * `unzipSync` signature and CRC-32 integrity check. Node/Bun only — callers
260
+ * gate on {@link hasNativeInflate} and fall back to fflate `unzipSync` elsewhere.
261
+ * Measured ~2x faster than fflate on large OOXML packages.
262
+ */
263
+ function nativeUnzip(buf) {
264
+ if (!_nativeInflate) throw new Error("Native inflate not available");
265
+ const inflate = _nativeInflate;
266
+ const crc = _nativeCrc32 ?? computeCrc32;
267
+ const out = {};
268
+ for (const e of readCentralDirectory(buf)) {
269
+ const raw = buf.subarray(e.dataStart, e.dataStart + e.compSize);
270
+ const dec = e.method === 0 ? raw.slice() : inflate(raw);
271
+ if (crc(dec) >>> 0 !== e.crc) throw new Error(`ZIP CRC-32 mismatch: ${e.name}`);
272
+ out[e.name] = dec;
273
+ }
274
+ return out;
275
+ }
276
+ //#endregion
277
+ //#region src/opc/packer.ts
278
+ /**
279
+ * Shared packer utilities for OOXML document generation.
280
+ *
281
+ * @module
282
+ */
283
+ /** Default DEFLATE level for XML entries (SuperFast, matching MS Office). */
284
+ const ZIP_DEFLATE_LEVEL = 1;
285
+ /** Level for already-compressed media — STORE (no compression). */
286
+ const ZIP_STORED_LEVEL = 0;
287
+ /**
288
+ * Media formats already compressed internally (DEFLATE for PNG, DCT for JPEG,
289
+ * LZW for GIF). Re-compressing via zip DEFLATE wastes CPU and often inflates
290
+ * the data, so MS Office STORE-s these (CompressionOption.NotCompressed → zip
291
+ * method 0). Everything else (EMF/WMF/BMP/TIFF/SVG/…) is compressible → DEFLATE.
292
+ */
293
+ const PRECOMPRESSED_MEDIA_EXT = new Set([
294
+ "jpg",
295
+ "jpeg",
296
+ "png",
297
+ "gif"
298
+ ]);
299
+ /**
300
+ * Resolve the ZIP level for a media entry by file-name extension, matching MS
301
+ * Office: already-compressed raster formats → STORE (0), everything else →
302
+ * DEFLATE (`mediaLevel`, default Normal). A `compression.media` override
303
+ * therefore applies only to compressible formats, never forcing DEFLATE onto
304
+ * pre-compressed assets.
305
+ */
306
+ const levelForMediaName = (fileName, mediaLevel) => {
307
+ const dot = fileName.lastIndexOf(".");
308
+ const ext = dot < 0 ? "" : fileName.slice(dot + 1).toLowerCase();
309
+ return PRECOMPRESSED_MEDIA_EXT.has(ext) ? 0 : mediaLevel;
310
+ };
311
+ /**
312
+ * Asynchronously compress files and convert to the requested output format.
313
+ *
314
+ * Uses fflate Web Workers for non-blocking DEFLATE compression.
315
+ * XML entries use DEFLATE level 1 (SuperFast) by default. Media entries are
316
+ * split by type, matching MS Office: already-compressed formats (PNG/JPEG/GIF)
317
+ * are STOREd, everything else uses the `media` level (default Normal).
318
+ * Set `{ media: ZIP_STORED_LEVEL }` to STORE all compressible media too.
319
+ */
320
+ const zipAndConvert = async (files, type, mimeType, level = 1) => {
321
+ return convertOutput(hasNativeDeflate() ? await nativeZipAsync(files, level) : await new Promise((resolve, reject) => {
322
+ zip(files, {
323
+ level,
324
+ consume: true
325
+ }, (err, data) => {
326
+ if (err) reject(err);
327
+ else resolve(data);
328
+ });
329
+ }), type, mimeType);
330
+ };
331
+ /**
332
+ * Synchronously compress files and convert to the requested output format.
333
+ *
334
+ * Uses synchronous DEFLATE compression for maximum throughput.
335
+ * Blocks the event loop — prefer {@link zipAndConvert} in server contexts.
336
+ */
337
+ const zipSyncAndConvert = (files, type, mimeType, level = 1) => {
338
+ return convertOutput(hasNativeDeflate() ? nativeZip(files, level) : zipSync(files, { level }), type, mimeType);
339
+ };
340
+ /**
341
+ * Create a `ReadableStream<Uint8Array>` from compressed file entries.
342
+ *
343
+ * Uses fflate's `AsyncZipDeflate` for non-blocking DEFLATE compression.
344
+ * `STORED` entries (media) pass through synchronously.
345
+ * Works in both Node.js and browsers (Web Streams API).
346
+ */
347
+ const createZipStream = (files, defaultLevel = 1) => {
348
+ return new ReadableStream({ start(controller) {
349
+ try {
350
+ const zip = new Zip((err, chunk, _final) => {
351
+ if (err) {
352
+ controller.error(err);
353
+ return;
354
+ }
355
+ controller.enqueue(chunk);
356
+ if (_final) controller.close();
357
+ });
358
+ for (const [name, data] of Object.entries(files)) {
359
+ const raw = Array.isArray(data) ? data[0] : data;
360
+ const level = Array.isArray(data) ? data[1].level ?? defaultLevel : defaultLevel;
361
+ const entry = level === 0 ? new ZipPassThrough(name) : new AsyncZipDeflate(name, { level });
362
+ zip.add(entry);
363
+ entry.push(raw, true);
364
+ }
365
+ zip.end();
366
+ } catch (err) {
367
+ controller.error(err instanceof Error ? err : new Error(String(err)));
368
+ }
369
+ } });
370
+ };
371
+ /**
372
+ * Create a Packer object with all output format methods.
373
+ *
374
+ * Centralises the ZIP → convert pipeline and the streaming implementation
375
+ * so that each OOXML package only needs to provide a `compile` function and
376
+ * a MIME type.
377
+ */
378
+ const createPacker = (options) => {
379
+ const { compile, mimeType } = options;
380
+ const pack = async (file, opts) => {
381
+ const type = opts?.type ?? "nodebuffer";
382
+ return zipAndConvert(compile(file, opts?.overrides ?? [], opts?.compression?.media ?? 6), type, mimeType, opts?.compression?.xml ?? 1);
383
+ };
384
+ const toBytes = (file, opts) => pack(file, {
385
+ ...opts,
386
+ type: "uint8array"
387
+ });
388
+ const toString = (file, opts) => pack(file, {
389
+ ...opts,
390
+ type: "string"
391
+ });
392
+ const toBuffer = (file, opts) => pack(file, {
393
+ ...opts,
394
+ type: "nodebuffer"
395
+ });
396
+ const toBase64 = (file, opts) => pack(file, {
397
+ ...opts,
398
+ type: "base64"
399
+ });
400
+ const toBlob = (file, opts) => pack(file, {
401
+ ...opts,
402
+ type: "blob"
403
+ });
404
+ const toArrayBuffer = (file, opts) => pack(file, {
405
+ ...opts,
406
+ type: "arraybuffer"
407
+ });
408
+ const packSync = (file, opts) => {
409
+ const type = opts?.type ?? "nodebuffer";
410
+ return zipSyncAndConvert(compile(file, opts?.overrides ?? [], opts?.compression?.media ?? 6), type, mimeType, opts?.compression?.xml ?? 1);
411
+ };
412
+ const toBytesSync = (file, opts) => packSync(file, {
413
+ ...opts,
414
+ type: "uint8array"
415
+ });
416
+ const toStringSync = (file, opts) => packSync(file, {
417
+ ...opts,
418
+ type: "string"
419
+ });
420
+ const toBufferSync = (file, opts) => packSync(file, {
421
+ ...opts,
422
+ type: "nodebuffer"
423
+ });
424
+ const toBase64Sync = (file, opts) => packSync(file, {
425
+ ...opts,
426
+ type: "base64"
427
+ });
428
+ const toBlobSync = (file, opts) => packSync(file, {
429
+ ...opts,
430
+ type: "blob"
431
+ });
432
+ const toArrayBufferSync = (file, opts) => packSync(file, {
433
+ ...opts,
434
+ type: "arraybuffer"
435
+ });
436
+ const toStream = (file, opts) => {
437
+ const mediaLevel = opts?.compression?.media ?? 6;
438
+ let files;
439
+ try {
440
+ files = compile(file, opts?.overrides ?? [], mediaLevel);
441
+ } catch (err) {
442
+ return new ReadableStream({ start(controller) {
443
+ controller.error(err instanceof Error ? err : new Error(String(err)));
444
+ } });
445
+ }
446
+ return createZipStream(files, opts?.compression?.xml ?? 1);
447
+ };
448
+ return {
449
+ compile,
450
+ pack,
451
+ packSync,
452
+ toBytes,
453
+ toBytesSync,
454
+ toString,
455
+ toStringSync,
456
+ toBuffer,
457
+ toBufferSync,
458
+ toBase64,
459
+ toBase64Sync,
460
+ toBlob,
461
+ toBlobSync,
462
+ toArrayBuffer,
463
+ toArrayBufferSync,
464
+ toStream
465
+ };
466
+ };
467
+ //#endregion
468
+ //#region src/util/compile.ts
469
+ /**
470
+ * Shared compiler utilities for OOXML document generation.
471
+ *
472
+ * @module
473
+ */
474
+ /** Reusable TextEncoder instance (stateless, safe to share). */
475
+ const encoder = new TextEncoder();
476
+ /**
477
+ * Convert a mapping of XML files + overrides + media into a Zippable structure.
478
+ *
479
+ * Centralises the mapping→Zippable conversion shared by all three compilers.
480
+ */
481
+ function compileMapping(mapping, overrides, media, mediaLevel = 0) {
482
+ const files = {};
483
+ for (const entry of Object.values(mapping)) files[entry.path] = encoder.encode(entry.data);
484
+ if (overrides) for (const o of overrides) files[o.path] = typeof o.data === "string" ? encoder.encode(o.data) : o.data;
485
+ if (media) for (const m of media) files[m.path] = [m.data, { level: levelForMediaName(m.path, mediaLevel) }];
486
+ return files;
487
+ }
488
+ //#endregion
489
+ //#region src/util/crypto.ts
490
+ /**
491
+ * Password hashing utilities for OOXML document protection.
492
+ *
493
+ * Implements ECMA-376 Agile Encryption password derivation.
494
+ * Passwords are encoded as UTF-16LE per the OOXML specification.
495
+ *
496
+ * Uses @noble/hashes for SHA-512 — audited, cross-platform,
497
+ * and comparable in speed to node:crypto for this workload.
498
+ *
499
+ * @module
500
+ */
501
+ function utf16leEncode(str) {
502
+ const bytes = new Uint8Array(str.length * 2);
503
+ for (let i = 0; i < str.length; i++) {
504
+ const code = str.charCodeAt(i);
505
+ bytes[i * 2] = code & 255;
506
+ bytes[i * 2 + 1] = code >> 8 & 255;
507
+ }
508
+ return bytes;
509
+ }
510
+ function randomBytes(length) {
511
+ const bytes = new Uint8Array(length);
512
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) crypto.getRandomValues(bytes);
513
+ else for (let i = 0; i < length; i++) bytes[i] = Math.floor(Math.random() * 256);
514
+ return bytes;
515
+ }
516
+ /**
517
+ * Computes the ECMA-376 Agile Encryption hash for a password.
518
+ *
519
+ * Algorithm: SHA-512(salt + UTF-16LE(password)), then iterate
520
+ * SHA-512(hash + i.toUint32LE()) spinCount times.
521
+ */
522
+ function hashPasswordAgile(password, salt, spinCount) {
523
+ const pwBytes = utf16leEncode(password);
524
+ const initial = new Uint8Array(salt.length + pwBytes.length);
525
+ initial.set(salt, 0);
526
+ initial.set(pwBytes, salt.length);
527
+ let h = sha512(initial);
528
+ for (let i = 0; i < spinCount; i++) {
529
+ const buf = new Uint8Array(h.length + 4);
530
+ buf.set(h, 0);
531
+ buf[h.length] = i & 255;
532
+ buf[h.length + 1] = i >> 8 & 255;
533
+ buf[h.length + 2] = i >> 16 & 255;
534
+ buf[h.length + 3] = i >> 24 & 255;
535
+ h = sha512(buf);
536
+ }
537
+ return encodeBase64(new Uint8Array(h));
538
+ }
539
+ /**
540
+ * Derives a password hash for OOXML document protection.
541
+ *
542
+ * Returns base64-encoded hashValue, saltValue, spinCount, and algorithmName
543
+ * suitable for direct use in document protection options.
544
+ */
545
+ function derivePasswordHash(password, spinCount = 1e5) {
546
+ const salt = randomBytes(16);
547
+ return {
548
+ hashValue: hashPasswordAgile(password, salt, spinCount),
549
+ saltValue: encodeBase64(salt),
550
+ spinCount,
551
+ algorithmName: "SHA-512"
552
+ };
553
+ }
554
+ //#endregion
555
+ //#region src/util/placeholder.ts
556
+ const formatId = (offset, index, style) => style === "rId" ? `rId${offset + index}` : `${offset + index}`;
557
+ /** Single-pass regex replacement for `{key}` placeholders using a Map lookup. */
558
+ const PLACEHOLDER_RE = /\{([^{}]+)\}/g;
559
+ function replacePlaceholders(xml, map) {
560
+ const parts = [];
561
+ let last = 0;
562
+ for (const m of xml.matchAll(PLACEHOLDER_RE)) {
563
+ const key = m[1];
564
+ if (key === void 0) continue;
565
+ const replacement = map.get(key);
566
+ if (replacement !== void 0) {
567
+ parts.push(xml.substring(last, m.index), replacement);
568
+ last = m.index + m[0].length;
569
+ }
570
+ }
571
+ if (last === 0) return xml;
572
+ parts.push(xml.substring(last));
573
+ return parts.join("");
574
+ }
575
+ function hasPlaceholders(xml) {
576
+ return xml.includes("{");
577
+ }
578
+ function collectPlaceholderKeys(xml, prefix) {
579
+ const keys = /* @__PURE__ */ new Set();
580
+ const search = `{${prefix}`;
581
+ let pos = 0;
582
+ while ((pos = xml.indexOf(search, pos)) !== -1) {
583
+ const keyStart = pos + search.length;
584
+ const keyEnd = xml.indexOf("}", keyStart);
585
+ if (keyEnd === -1) break;
586
+ const key = xml.substring(keyStart, keyEnd);
587
+ if (key.length > 0) keys.add(key);
588
+ pos = keyEnd + 1;
589
+ }
590
+ return [...keys];
591
+ }
592
+ function replaceImagePlaceholders(xml, mediaData, offset, idFormat = "rId") {
593
+ if (mediaData.length === 0) return xml;
594
+ const map = /* @__PURE__ */ new Map();
595
+ for (const [i, item] of mediaData.entries()) map.set(item.fileName, formatId(offset, i, idFormat));
596
+ return replacePlaceholders(xml, map);
597
+ }
598
+ function getReferencedMedia(xml, mediaArray) {
599
+ return mediaArray.filter((image) => xml.includes(`{${image.fileName}}`));
600
+ }
601
+ /**
602
+ * Combined find + replace for image placeholders in a single pass.
603
+ *
604
+ * Scans XML for `{fileName}` placeholders, replaces them with formatted IDs,
605
+ * and returns the list of referenced media items for relationship registration.
606
+ * More efficient than calling getReferencedMedia() + replaceImagePlaceholders() separately.
607
+ */
608
+ function findAndReplaceImagePlaceholders(xml, mediaArray, offset, idFormat = "rId") {
609
+ if (mediaArray.length === 0 || !hasPlaceholders(xml)) return {
610
+ xml,
611
+ referenced: []
612
+ };
613
+ const itemMap = /* @__PURE__ */ new Map();
614
+ for (const item of mediaArray) itemMap.set(item.fileName, item);
615
+ const referenced = [];
616
+ const replaceMap = /* @__PURE__ */ new Map();
617
+ const parts = [];
618
+ let last = 0;
619
+ for (const m of xml.matchAll(PLACEHOLDER_RE)) {
620
+ const key = m[1];
621
+ if (key === void 0) continue;
622
+ const item = itemMap.get(key);
623
+ if (item !== void 0) {
624
+ if (!replaceMap.has(key)) {
625
+ replaceMap.set(key, formatId(offset, referenced.length, idFormat));
626
+ referenced.push(item);
627
+ }
628
+ parts.push(xml.substring(last, m.index), replaceMap.get(key));
629
+ last = m.index + m[0].length;
630
+ }
631
+ }
632
+ if (last === 0) return {
633
+ xml,
634
+ referenced: []
635
+ };
636
+ parts.push(xml.substring(last));
637
+ return {
638
+ xml: parts.join(""),
639
+ referenced
640
+ };
641
+ }
642
+ /**
643
+ * Replace all placeholder types in a single pass.
644
+ *
645
+ * Combines image, chart, smartart, and arbitrary key-value replacements
646
+ * into one regex scan of the XML string. Used by compilers to avoid
647
+ * multiple full-string passes on large documents.
648
+ */
649
+ function replaceAllPlaceholders(xml, entries) {
650
+ if (entries.length === 0 || !hasPlaceholders(xml)) return xml;
651
+ const map = /* @__PURE__ */ new Map();
652
+ for (const { prefix, key, value } of entries) map.set(prefix ? `${prefix}${key}` : key, value);
653
+ return replacePlaceholders(xml, map);
654
+ }
655
+ function replaceChartPlaceholders(xml, chartKeys, offset, idFormat = "rId") {
656
+ if (chartKeys.length === 0) return xml;
657
+ const map = /* @__PURE__ */ new Map();
658
+ for (let i = 0; i < chartKeys.length; i++) map.set(`chart:${chartKeys[i]}`, formatId(offset, i, idFormat));
659
+ return replacePlaceholders(xml, map);
660
+ }
661
+ function replaceSmartArtPlaceholders(xml, keys, dataOffset, idFormat = "rId") {
662
+ if (keys.length === 0) return xml;
663
+ const count = keys.length;
664
+ const loOffset = dataOffset + count;
665
+ const qsOffset = loOffset + count;
666
+ const csOffset = qsOffset + count;
667
+ const map = /* @__PURE__ */ new Map();
668
+ for (let i = 0; i < count; i++) {
669
+ map.set(`smartart:${keys[i]}`, formatId(dataOffset, i, idFormat));
670
+ map.set(`smartart-lo:${keys[i]}`, formatId(loOffset, i, idFormat));
671
+ map.set(`smartart-qs:${keys[i]}`, formatId(qsOffset, i, idFormat));
672
+ map.set(`smartart-cs:${keys[i]}`, formatId(csOffset, i, idFormat));
673
+ }
674
+ return replacePlaceholders(xml, map);
675
+ }
676
+ function addSmartArtRelationships(keys, addRel, baseOffset, globalStartIndex, options) {
677
+ const { pathPrefix, styleRelType } = options;
678
+ const count = keys.length;
679
+ const loOffset = baseOffset + count;
680
+ const qsOffset = loOffset + count;
681
+ const csOffset = qsOffset + count;
682
+ for (let i = 0; i < keys.length; i++) {
683
+ const gi = globalStartIndex + i;
684
+ addRel(baseOffset + i, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramData", `${pathPrefix}diagrams/data${gi + 1}.xml`);
685
+ addRel(loOffset + i, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramLayout", `${pathPrefix}diagrams/layout${gi + 1}.xml`);
686
+ addRel(qsOffset + i, styleRelType, `${pathPrefix}diagrams/quickStyle${gi + 1}.xml`);
687
+ addRel(csOffset + i, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramColors", `${pathPrefix}diagrams/colors${gi + 1}.xml`);
688
+ addRel(csOffset + count + i, "http://schemas.microsoft.com/office/2007/relationships/diagramDrawing", `${pathPrefix}diagrams/drawing${gi + 1}.xml`);
689
+ }
690
+ }
691
+ /** Replace `{reference-instance}` placeholders with numId values. */
692
+ function replaceNumberingPlaceholders(xml, concreteNumberings) {
693
+ if (concreteNumberings.length === 0) return xml;
694
+ const map = /* @__PURE__ */ new Map();
695
+ for (const { reference, instance, numId } of concreteNumberings) map.set(`${reference}-${instance}`, numId.toString());
696
+ return replacePlaceholders(xml, map);
697
+ }
698
+ /** Replace `{media:fileName}` placeholders with relationship IDs. */
699
+ function replaceMediaPlaceholders(xml, mediaData, offset) {
700
+ if (mediaData.length === 0) return xml;
701
+ const map = /* @__PURE__ */ new Map();
702
+ for (const [i, item] of mediaData.entries()) map.set(`media:${item.fileName}`, formatId(offset, i, "rId"));
703
+ return replacePlaceholders(xml, map);
704
+ }
705
+ /** Replace `{video:fileName}` placeholders with relationship IDs. */
706
+ function replaceVideoPlaceholders(xml, mediaData, offset) {
707
+ if (mediaData.length === 0) return xml;
708
+ const map = /* @__PURE__ */ new Map();
709
+ for (const [i, item] of mediaData.entries()) map.set(`video:${item.fileName}`, formatId(offset, i, "rId"));
710
+ return replacePlaceholders(xml, map);
711
+ }
712
+ /** Collect media items referenced by `{media:...}` placeholders in XML. */
713
+ function getMediaRefs(xml, mediaArray) {
714
+ return collectPrefixedRefs(xml, "media:", mediaArray);
715
+ }
716
+ /** Collect media items referenced by `{video:...}` placeholders in XML. */
717
+ function getVideoRefs(xml, mediaArray) {
718
+ return collectPrefixedRefs(xml, "video:", mediaArray);
719
+ }
720
+ function collectPrefixedRefs(xml, prefix, mediaArray) {
721
+ const keys = /* @__PURE__ */ new Set();
722
+ const search = `{${prefix}`;
723
+ let pos = 0;
724
+ while ((pos = xml.indexOf(search, pos)) !== -1) {
725
+ const keyStart = pos + search.length;
726
+ const keyEnd = xml.indexOf("}", keyStart);
727
+ if (keyEnd === -1) break;
728
+ keys.add(xml.substring(keyStart, keyEnd));
729
+ pos = keyEnd + 1;
730
+ }
731
+ return mediaArray.filter((m) => keys.has(m.fileName));
732
+ }
733
+ /** Replace `{hlink:key}` placeholders with relationship IDs. */
734
+ function replaceHyperlinkPlaceholders(xml, hyperlinks, offset) {
735
+ if (hyperlinks.length === 0) return xml;
736
+ const map = /* @__PURE__ */ new Map();
737
+ for (const [i, h] of hyperlinks.entries()) map.set(`hlink:${h.key}`, formatId(offset, i, "rId"));
738
+ return replacePlaceholders(xml, map);
739
+ }
740
+ //#endregion
741
+ //#region src/util/values.ts
742
+ /**
743
+ * Validates and converts a number to an integer (decimal number).
744
+ *
745
+ * Reference: ST_DecimalNumber in OOXML specification
746
+ *
747
+ * @param val - The number to validate and convert
748
+ * @returns The floored integer value
749
+ * @throws Error if the value is NaN
750
+ *
751
+ * @example
752
+ * ```typescript
753
+ * const num = decimalNumber(10.7); // Returns 10
754
+ * const negative = decimalNumber(-5.3); // Returns -5
755
+ * ```
756
+ */
757
+ const decimalNumber = (val) => {
758
+ if (!Number.isFinite(val)) throw new Error(`Invalid value '${val}' specified. Must be a finite number.`);
759
+ return Math.floor(val);
760
+ };
761
+ /**
762
+ * Validates and converts a number to a positive integer (unsigned decimal number).
763
+ *
764
+ * Reference: ST_UnsignedDecimalNumber in OOXML specification
765
+ *
766
+ * @param val - The number to validate and convert
767
+ * @returns The floored positive integer value
768
+ * @throws Error if the value is NaN or negative
769
+ *
770
+ * @example
771
+ * ```typescript
772
+ * const num = unsignedDecimalNumber(10.7); // Returns 10
773
+ * const invalid = unsignedDecimalNumber(-5); // Throws Error
774
+ * ```
775
+ */
776
+ const unsignedDecimalNumber = (val) => {
777
+ const value = decimalNumber(val);
778
+ if (value < 0) throw new Error(`Invalid value '${val}' specified. Must be a positive integer.`);
779
+ return value;
780
+ };
781
+ /**
782
+ * Validates and normalizes a hexadecimal binary value.
783
+ *
784
+ * The xsd:hexBinary type represents binary data as a sequence of binary octets
785
+ * using hexadecimal encoding, where each binary octet is a two-character
786
+ * hexadecimal number. Both lowercase and uppercase letters A-F are permitted.
787
+ *
788
+ * @param val - The hexadecimal string to validate
789
+ * @param length - The expected length in bytes (not characters)
790
+ * @returns The validated hexadecimal string
791
+ * @throws Error if the value is not a valid hex string of the expected length
792
+ *
793
+ * @example
794
+ * ```typescript
795
+ * hexBinary("0FB8", 2); // Valid: 2 bytes = 4 characters
796
+ * hexBinary("ABC", 2); // Invalid: wrong length
797
+ * ```
798
+ */
799
+ const hexBinary = (val, length) => {
800
+ const expectedLength = length * 2;
801
+ if (val.length !== expectedLength || isNaN(Number(`0x${val}`))) throw new Error(`Invalid hex value '${val}'. Expected ${expectedLength} digit hex value`);
802
+ return val;
803
+ };
804
+ /**
805
+ * Validates a long hexadecimal number (4 bytes / 8 characters).
806
+ *
807
+ * Reference: ST_LongHexNumber in OOXML specification
808
+ *
809
+ * @param val - The hexadecimal string to validate
810
+ * @returns The validated hexadecimal string
811
+ * @throws Error if the value is not a valid 8-character hex string
812
+ *
813
+ * @example
814
+ * ```typescript
815
+ * const hex = longHexNumber("ABCD1234"); // Valid
816
+ * ```
817
+ */
818
+ const longHexNumber = (val) => hexBinary(val, 4);
819
+ /**
820
+ * Validates a short hexadecimal number (2 bytes / 4 characters).
821
+ *
822
+ * Reference: ST_ShortHexNumber in OOXML specification
823
+ *
824
+ * @param val - The hexadecimal string to validate
825
+ * @returns The validated hexadecimal string
826
+ * @throws Error if the value is not a valid 4-character hex string
827
+ *
828
+ * @example
829
+ * ```typescript
830
+ * const hex = shortHexNumber("AB12"); // Valid
831
+ * ```
832
+ */
833
+ const shortHexNumber = (val) => hexBinary(val, 2);
834
+ /**
835
+ * Validates a single-byte hexadecimal number (1 byte / 2 characters).
836
+ *
837
+ * Reference: ST_UcharHexNumber in OOXML specification
838
+ *
839
+ * @param val - The hexadecimal string to validate
840
+ * @returns The validated hexadecimal string
841
+ * @throws Error if the value is not a valid 2-character hex string
842
+ *
843
+ * @example
844
+ * ```typescript
845
+ * const hex = uCharHexNumber("FF"); // Valid
846
+ * ```
847
+ */
848
+ const uCharHexNumber = (val) => hexBinary(val, 1);
849
+ /**
850
+ * Normalizes a universal measure value by parsing and reformatting.
851
+ *
852
+ * Ensures the numeric portion is properly formatted while preserving the unit.
853
+ *
854
+ * Reference: ST_UniversalMeasure in OOXML specification
855
+ *
856
+ * @param val - The universal measure string to normalize
857
+ * @returns The normalized universal measure
858
+ *
859
+ * @example
860
+ * ```typescript
861
+ * const measure = universalMeasureValue("10.500mm"); // Returns "10.5mm"
862
+ * ```
863
+ */
864
+ const universalMeasureValue = (val) => {
865
+ const unit = val.slice(-2);
866
+ const amount = val.substring(0, val.length - 2);
867
+ return `${Number(amount)}${unit}`;
868
+ };
869
+ /**
870
+ * Validates and normalizes a positive universal measure value.
871
+ *
872
+ * Reference: ST_PositiveUniversalMeasure in OOXML specification
873
+ *
874
+ * @param val - The positive universal measure string to validate
875
+ * @returns The normalized positive universal measure
876
+ * @throws Error if the value is negative
877
+ *
878
+ * @example
879
+ * ```typescript
880
+ * const measure = positiveUniversalMeasureValue("10.5mm"); // Valid
881
+ * const invalid = positiveUniversalMeasureValue("-5mm"); // Throws Error
882
+ * ```
883
+ */
884
+ const positiveUniversalMeasureValue = (val) => {
885
+ const value = universalMeasureValue(val);
886
+ if (parseFloat(value) < 0) throw new Error(`Invalid value '${value}' specified. Expected a positive number.`);
887
+ return value;
888
+ };
889
+ /**
890
+ * Validates and normalizes a hexadecimal color value.
891
+ *
892
+ * Accepts either "auto" or a 6-character RGB hex value (with or without # prefix).
893
+ * The # prefix is commonly used but technically invalid in OOXML, so it is stripped
894
+ * for strict compliance.
895
+ *
896
+ * Reference: ST_HexColor in OOXML specification
897
+ *
898
+ * @param val - The color value to validate ("auto" or hex color)
899
+ * @returns The normalized color value
900
+ * @throws Error if the hex color is invalid
901
+ *
902
+ * @example
903
+ * ```typescript
904
+ * const color1 = hexColorValue("auto"); // Returns "auto"
905
+ * const color2 = hexColorValue("FF0000"); // Returns "FF0000"
906
+ * const color3 = hexColorValue("#00FF00"); // Returns "00FF00" (# stripped)
907
+ * ```
908
+ */
909
+ const hexColorValue = (val) => {
910
+ if (val === "auto") return val;
911
+ return hexBinary(val.charAt(0) === "#" ? val.substring(1) : val, 3);
912
+ };
913
+ /**
914
+ * Validates a signed TWIP measurement value.
915
+ *
916
+ * Accepts either a universal measure string or a numeric TWIP value.
917
+ *
918
+ * Reference: ST_SignedTwipsMeasure in OOXML specification
919
+ *
920
+ * @param val - The measurement value (universal measure or number)
921
+ * @returns The normalized measurement value
922
+ *
923
+ * @example
924
+ * ```typescript
925
+ * const measure1 = signedTwipsMeasureValue("10mm");
926
+ * const measure2 = signedTwipsMeasureValue(1440); // 1 inch in TWIP
927
+ * ```
928
+ */
929
+ const signedTwipsMeasureValue = (val) => typeof val === "string" ? universalMeasureValue(val) : decimalNumber(val);
930
+ /**
931
+ * Validates a half-point (HPS) measurement value.
932
+ *
933
+ * Accepts either a positive universal measure string or a positive number.
934
+ * HPS (half-points) are commonly used for font sizes.
935
+ *
936
+ * Reference: ST_HpsMeasure in OOXML specification
937
+ *
938
+ * @param val - The measurement value (positive universal measure or number)
939
+ * @returns The normalized measurement value
940
+ *
941
+ * @example
942
+ * ```typescript
943
+ * const fontSize1 = hpsMeasureValue("12pt");
944
+ * const fontSize2 = hpsMeasureValue(24); // 12pt in half-points
945
+ * ```
946
+ */
947
+ const hpsMeasureValue = (val) => typeof val === "string" ? positiveUniversalMeasureValue(val) : unsignedDecimalNumber(val);
948
+ /**
949
+ * Validates a signed half-point (HPS) measurement value.
950
+ *
951
+ * Accepts either a universal measure string or a numeric value.
952
+ *
953
+ * Reference: ST_SignedHpsMeasure in OOXML specification
954
+ *
955
+ * @param val - The measurement value (universal measure or number)
956
+ * @returns The normalized measurement value
957
+ *
958
+ * @example
959
+ * ```typescript
960
+ * const spacing1 = signedHpsMeasureValue("6pt");
961
+ * const spacing2 = signedHpsMeasureValue(-12); // Negative spacing
962
+ * ```
963
+ */
964
+ const signedHpsMeasureValue = (val) => typeof val === "string" ? universalMeasureValue(val) : decimalNumber(val);
965
+ /**
966
+ * Validates a positive TWIP measurement value.
967
+ *
968
+ * Accepts either a positive universal measure string or a positive number.
969
+ *
970
+ * Reference: ST_TwipsMeasure in OOXML specification
971
+ *
972
+ * @param val - The measurement value (positive universal measure or number)
973
+ * @returns The normalized measurement value
974
+ *
975
+ * @example
976
+ * ```typescript
977
+ * const width1 = twipsMeasureValue("25.4mm");
978
+ * const width2 = twipsMeasureValue(1440); // 1 inch in TWIP
979
+ * ```
980
+ */
981
+ const twipsMeasureValue = (val) => typeof val === "string" ? positiveUniversalMeasureValue(val) : unsignedDecimalNumber(val);
982
+ /**
983
+ * Normalizes a percentage value by parsing and reformatting.
984
+ *
985
+ * Reference: ST_Percentage in OOXML specification
986
+ *
987
+ * @param val - The percentage string to normalize
988
+ * @returns The normalized percentage
989
+ *
990
+ * @example
991
+ * ```typescript
992
+ * const percent = percentageValue("50.000%"); // Returns "50%"
993
+ * ```
994
+ */
995
+ const percentageValue = (val) => {
996
+ const percent = val.substring(0, val.length - 1);
997
+ return `${Number(percent)}%`;
998
+ };
999
+ /**
1000
+ * Validates a measurement value that can be expressed as a number, percentage, or universal measure.
1001
+ *
1002
+ * Reference: ST_MeasurementOrPercent in OOXML specification
1003
+ *
1004
+ * @param val - The measurement value (number, percentage, or universal measure)
1005
+ * @returns The normalized measurement value
1006
+ *
1007
+ * @example
1008
+ * ```typescript
1009
+ * const measure1 = measurementOrPercentValue(100); // Unqualified number
1010
+ * const measure2 = measurementOrPercentValue("50%"); // Percentage
1011
+ * const measure3 = measurementOrPercentValue("10mm"); // Universal measure
1012
+ * ```
1013
+ */
1014
+ const measurementOrPercentValue = (val) => {
1015
+ if (typeof val === "number") return decimalNumber(val);
1016
+ if (val.slice(-1) === "%") return percentageValue(val);
1017
+ return universalMeasureValue(val);
1018
+ };
1019
+ /**
1020
+ * Validates an eighth-point measurement value.
1021
+ *
1022
+ * Eighth-points are used for fine-grained measurements in text formatting.
1023
+ *
1024
+ * Reference: ST_EighthPointMeasure in OOXML specification
1025
+ *
1026
+ * @param val - The measurement value in eighth-points
1027
+ * @returns The validated positive integer value
1028
+ *
1029
+ * @example
1030
+ * ```typescript
1031
+ * const measure = eighthPointMeasureValue(16); // 2 points
1032
+ * ```
1033
+ */
1034
+ const eighthPointMeasureValue = unsignedDecimalNumber;
1035
+ /**
1036
+ * Validates a point measurement value.
1037
+ *
1038
+ * Reference: ST_PointMeasure in OOXML specification
1039
+ *
1040
+ * @param val - The measurement value in points
1041
+ * @returns The validated positive integer value
1042
+ *
1043
+ * @example
1044
+ * ```typescript
1045
+ * const fontSize = pointMeasureValue(12); // 12pt
1046
+ * ```
1047
+ */
1048
+ const pointMeasureValue = unsignedDecimalNumber;
1049
+ /**
1050
+ * Converts a JavaScript Date object to an ISO 8601 date-time string.
1051
+ *
1052
+ * The format is CCYY-MM-DDThh:mm:ss.sssZ where T is a literal and Z indicates UTC.
1053
+ * This matches the xsd:dateTime format required by OOXML.
1054
+ *
1055
+ * Reference: ST_DateTime in OOXML specification
1056
+ *
1057
+ * @param val - The Date object to convert
1058
+ * @returns An ISO 8601 formatted date-time string
1059
+ *
1060
+ * @example
1061
+ * ```typescript
1062
+ * const now = new Date();
1063
+ * const timestamp = dateTimeValue(now); // Returns "2024-01-15T10:30:00.000Z"
1064
+ * ```
1065
+ */
1066
+ const dateTimeValue = (val) => val.toISOString();
1067
+ /**
1068
+ * Theme color values used throughout OOXML for referencing document theme colors.
1069
+ *
1070
+ * Reference: ST_ThemeColor in OOXML specification
1071
+ *
1072
+ * @publicApi
1073
+ */
1074
+ const ThemeColor = {
1075
+ DARK1: "dark1",
1076
+ LIGHT1: "light1",
1077
+ DARK2: "dark2",
1078
+ LIGHT2: "light2",
1079
+ ACCENT1: "accent1",
1080
+ ACCENT2: "accent2",
1081
+ ACCENT3: "accent3",
1082
+ ACCENT4: "accent4",
1083
+ ACCENT5: "accent5",
1084
+ ACCENT6: "accent6",
1085
+ HYPERLINK: "hyperlink",
1086
+ FOLLOWED_HYPERLINK: "followedHyperlink",
1087
+ NONE: "none",
1088
+ BACKGROUND1: "background1",
1089
+ TEXT1: "text1",
1090
+ BACKGROUND2: "background2",
1091
+ TEXT2: "text2"
1092
+ };
1093
+ /**
1094
+ * Theme font values used for referencing document theme fonts.
1095
+ *
1096
+ * Reference: ST_Theme in OOXML specification
1097
+ *
1098
+ * @publicApi
1099
+ */
1100
+ const ThemeFont = {
1101
+ MAJOR_EAST_ASIA: "majorEastAsia",
1102
+ MAJOR_BIDI: "majorBidi",
1103
+ MAJOR_ASCII: "majorAscii",
1104
+ MAJOR_H_ANSI: "majorHAnsi",
1105
+ MINOR_EAST_ASIA: "minorEastAsia",
1106
+ MINOR_BIDI: "minorBidi",
1107
+ MINOR_ASCII: "minorAscii",
1108
+ MINOR_H_ANSI: "minorHAnsi"
1109
+ };
1110
+ //#endregion
1111
+ export { convertOutput as $, replaceChartPlaceholders as A, compileMapping as B, findAndReplaceImagePlaceholders as C, getVideoRefs as D, getReferencedMedia as E, replaceSmartArtPlaceholders as F, levelForMediaName as G, ZIP_STORED_LEVEL as H, replaceVideoPlaceholders as I, zipAndConvert as J, strFromU8$1 as K, derivePasswordHash as L, replaceImagePlaceholders as M, replaceMediaPlaceholders as N, hasPlaceholders as O, replaceNumberingPlaceholders as P, OoxmlMimeType as Q, hashPasswordAgile as R, collectPlaceholderKeys as S, getMediaRefs as T, createPacker as U, ZIP_DEFLATE_LEVEL as V, createZipStream as W, hasNativeInflate as X, zipSyncAndConvert as Y, nativeUnzip as Z, twipsMeasureValue as _, eighthPointMeasureValue as a, unsignedDecimalNumber as b, hpsMeasureValue as c, percentageValue as d, pointMeasureValue as f, signedTwipsMeasureValue as g, signedHpsMeasureValue as h, decimalNumber as i, replaceHyperlinkPlaceholders as j, replaceAllPlaceholders as k, longHexNumber as l, shortHexNumber as m, ThemeFont as n, hexBinary as o, positiveUniversalMeasureValue as p, unzipSync$1 as q, dateTimeValue as r, hexColorValue as s, ThemeColor as t, measurementOrPercentValue as u, uCharHexNumber as v, formatId as w, addSmartArtRelationships as x, universalMeasureValue as y, randomBytes as z };
1112
+
1113
+ //# sourceMappingURL=util-Tq9PSjK0.mjs.map