@stll/folio-core 0.3.0 → 0.4.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.
Files changed (57) hide show
  1. package/README.md +2 -2
  2. package/dist/ai-edits/headless.d.ts +2 -1
  3. package/dist/ai-edits/headless.js +9 -7
  4. package/dist/compat/eigenpal.d.ts +29 -0
  5. package/dist/compat/eigenpal.js +24 -0
  6. package/dist/controller/layoutPipeline.d.ts +1 -1
  7. package/dist/controller/layoutPipeline.js +22 -0
  8. package/dist/controller/layoutSession.d.ts +1 -1
  9. package/dist/docx/encryption/agileDecryption.d.ts +6 -0
  10. package/dist/docx/encryption/agileDecryption.js +208 -0
  11. package/dist/docx/encryption/compoundFile.d.ts +20 -0
  12. package/dist/docx/encryption/compoundFile.js +249 -0
  13. package/dist/docx/encryption/containerFormat.d.ts +15 -0
  14. package/dist/docx/encryption/containerFormat.js +40 -0
  15. package/dist/docx/encryption/cryptoBytes.d.ts +11 -0
  16. package/dist/docx/encryption/cryptoBytes.js +55 -0
  17. package/dist/docx/encryption/encryptionInfo.d.ts +32 -0
  18. package/dist/docx/encryption/encryptionInfo.js +128 -0
  19. package/dist/docx/encryption/errors.d.ts +18 -0
  20. package/dist/docx/encryption/errors.js +13 -0
  21. package/dist/docx/encryption/index.d.ts +4 -0
  22. package/dist/docx/encryption/index.js +4 -0
  23. package/dist/docx/encryption/openEncryptedDocx.d.ts +28 -0
  24. package/dist/docx/encryption/openEncryptedDocx.js +65 -0
  25. package/dist/docx/index.d.ts +3 -1
  26. package/dist/docx/index.js +3 -1
  27. package/dist/docx/parser.d.ts +4 -5
  28. package/dist/docx/parser.js +8 -2
  29. package/dist/docx/unzip.d.ts +7 -5
  30. package/dist/docx/unzip.js +8 -2
  31. package/dist/index.d.ts +6 -5
  32. package/dist/index.js +2 -1
  33. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -1
  34. package/dist/layout-bridge/convert/headerFooterLayout.js +16 -6
  35. package/dist/layout-bridge/convert/templatePreviewFlow.d.ts +1 -1
  36. package/dist/layout-bridge/convert/toFlowBlocks.js +2 -0
  37. package/dist/layout-engine/index.js +27 -1
  38. package/dist/layout-engine/measure/measureBlocks.js +15 -1
  39. package/dist/layout-engine/measure/measureParagraph.js +37 -6
  40. package/dist/layout-engine/types.d.ts +3 -0
  41. package/dist/layout-painter/renderPage.js +2 -1
  42. package/dist/layout-painter/renderParagraph.js +30 -5
  43. package/dist/layout-painter/renderTable.js +17 -0
  44. package/dist/managers/DocumentLoaderManager.d.ts +3 -1
  45. package/dist/managers/DocumentLoaderManager.js +3 -2
  46. package/dist/paged-layout/headerFooterMargins.d.ts +3 -1
  47. package/dist/paged-layout/headerFooterMargins.js +5 -3
  48. package/dist/prosemirror/attrs/index.js +1 -0
  49. package/dist/prosemirror/conversion/fromProseDoc.js +7 -2
  50. package/dist/prosemirror/conversion/toProseDoc.js +14 -7
  51. package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
  52. package/dist/prosemirror/extensions/nodes/TableExtension.js +2 -0
  53. package/dist/prosemirror/schema/nodes.d.ts +4 -2
  54. package/dist/server.d.ts +2 -2
  55. package/dist/utils/fontResolver.d.ts +3 -1
  56. package/dist/utils/fontResolver.js +28 -4
  57. package/package.json +9 -1
@@ -0,0 +1,15 @@
1
+ //#region src/docx/encryption/containerFormat.d.ts
2
+ /**
3
+ * OOXML container format detection (ZIP vs encrypted OLE compound file).
4
+ *
5
+ * @see https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/
6
+ */
7
+ declare const DOCX_CONTAINER_TYPES: {
8
+ readonly ZIP: "zip";
9
+ readonly CFB: "cfb";
10
+ readonly UNKNOWN: "unknown";
11
+ };
12
+ type DocxContainerType = (typeof DOCX_CONTAINER_TYPES)[keyof typeof DOCX_CONTAINER_TYPES];
13
+ declare const detectDocxContainerType: (data: ArrayBuffer | Uint8Array) => DocxContainerType;
14
+ //#endregion
15
+ export { DOCX_CONTAINER_TYPES, DocxContainerType, detectDocxContainerType };
@@ -0,0 +1,40 @@
1
+ //#region src/docx/encryption/containerFormat.ts
2
+ /**
3
+ * OOXML container format detection (ZIP vs encrypted OLE compound file).
4
+ *
5
+ * @see https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/
6
+ */
7
+ const DOCX_CONTAINER_TYPES = {
8
+ ZIP: "zip",
9
+ CFB: "cfb",
10
+ UNKNOWN: "unknown"
11
+ };
12
+ const ZIP_SIGNATURE = [
13
+ 80,
14
+ 75,
15
+ 3,
16
+ 4
17
+ ];
18
+ const OLE_SIGNATURE = [
19
+ 208,
20
+ 207,
21
+ 17,
22
+ 224,
23
+ 161,
24
+ 177,
25
+ 26,
26
+ 225
27
+ ];
28
+ const startsWith = (bytes, signature) => {
29
+ if (bytes.length < signature.length) return false;
30
+ for (let i = 0; i < signature.length; i++) if (bytes[i] !== signature[i]) return false;
31
+ return true;
32
+ };
33
+ const detectDocxContainerType = (data) => {
34
+ const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
35
+ if (startsWith(bytes, ZIP_SIGNATURE)) return DOCX_CONTAINER_TYPES.ZIP;
36
+ if (startsWith(bytes, OLE_SIGNATURE)) return DOCX_CONTAINER_TYPES.CFB;
37
+ return DOCX_CONTAINER_TYPES.UNKNOWN;
38
+ };
39
+ //#endregion
40
+ export { DOCX_CONTAINER_TYPES, detectDocxContainerType };
@@ -0,0 +1,11 @@
1
+ //#region src/docx/encryption/cryptoBytes.d.ts
2
+ /** Byte helpers shared by MS-CFB and MS-OFFCRYPTO agile decryption. */
3
+ declare const joinBytes: (parts: readonly Uint8Array[]) => Uint8Array;
4
+ declare const writeUint32Le: (value: number) => Uint8Array;
5
+ declare const passwordToUtf16Le: (password: string) => Uint8Array;
6
+ declare const bytesEqual: (left: Uint8Array, right: Uint8Array) => boolean;
7
+ declare const toArrayBuffer: (bytes: Uint8Array) => ArrayBuffer;
8
+ declare const decodeBase64: (encoded: string) => Uint8Array;
9
+ declare const padToBlock: (bytes: Uint8Array, blockSize: number, padByte?: number) => Uint8Array;
10
+ //#endregion
11
+ export { bytesEqual, decodeBase64, joinBytes, padToBlock, passwordToUtf16Le, toArrayBuffer, writeUint32Le };
@@ -0,0 +1,55 @@
1
+ //#region src/docx/encryption/cryptoBytes.ts
2
+ /** Byte helpers shared by MS-CFB and MS-OFFCRYPTO agile decryption. */
3
+ const joinBytes = (parts) => {
4
+ let total = 0;
5
+ for (const part of parts) total += part.length;
6
+ const out = new Uint8Array(total);
7
+ let offset = 0;
8
+ for (const part of parts) {
9
+ out.set(part, offset);
10
+ offset += part.length;
11
+ }
12
+ return out;
13
+ };
14
+ const writeUint32Le = (value) => {
15
+ const out = /* @__PURE__ */ new Uint8Array(4);
16
+ out[0] = value & 255;
17
+ out[1] = value >>> 8 & 255;
18
+ out[2] = value >>> 16 & 255;
19
+ out[3] = value >>> 24 & 255;
20
+ return out;
21
+ };
22
+ const passwordToUtf16Le = (password) => {
23
+ const out = new Uint8Array(password.length * 2);
24
+ for (let i = 0; i < password.length; i++) {
25
+ const code = password.charCodeAt(i);
26
+ out[i * 2] = code & 255;
27
+ out[i * 2 + 1] = code >> 8;
28
+ }
29
+ return out;
30
+ };
31
+ const bytesEqual = (left, right) => {
32
+ if (left.length !== right.length) return false;
33
+ let mismatch = 0;
34
+ for (let i = 0; i < left.length; i++) mismatch |= left[i] ^ right[i];
35
+ return mismatch === 0;
36
+ };
37
+ const toArrayBuffer = (bytes) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
38
+ const decodeBase64 = (encoded) => {
39
+ if (typeof atob === "function") {
40
+ const binary = atob(encoded);
41
+ const out = new Uint8Array(binary.length);
42
+ for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
43
+ return out;
44
+ }
45
+ return new Uint8Array(Buffer.from(encoded, "base64"));
46
+ };
47
+ const padToBlock = (bytes, blockSize, padByte = 54) => {
48
+ if (bytes.length >= blockSize) return bytes.subarray(0, blockSize);
49
+ const out = new Uint8Array(blockSize);
50
+ out.set(bytes);
51
+ out.fill(padByte, bytes.length);
52
+ return out;
53
+ };
54
+ //#endregion
55
+ export { bytesEqual, decodeBase64, joinBytes, padToBlock, passwordToUtf16Le, toArrayBuffer, writeUint32Le };
@@ -0,0 +1,32 @@
1
+ //#region src/docx/encryption/encryptionInfo.d.ts
2
+ /**
3
+ * Parse the EncryptionInfo stream for Agile Encryption (Office 2010+).
4
+ *
5
+ * @see https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/87020a34-e73f-4139-99bc-bbdf6cf6fa55
6
+ */
7
+ type AgileKeyMaterial = {
8
+ packageSalt: Uint8Array;
9
+ packageKeyBits: number;
10
+ packageHash: string;
11
+ packageHashBytes: number;
12
+ cipherBlockBytes: number;
13
+ cipherName: string;
14
+ cipherMode: string;
15
+ passwordIterations: number;
16
+ passwordSalt: Uint8Array;
17
+ passwordHash: string;
18
+ passwordKeyBits: number;
19
+ passwordBlockBytes: number;
20
+ encryptedVerifierInput: Uint8Array;
21
+ encryptedVerifierDigest: Uint8Array;
22
+ encryptedIntermediateKey: Uint8Array;
23
+ encryptedIntegrityKey: Uint8Array;
24
+ encryptedIntegrityDigest: Uint8Array;
25
+ };
26
+ type AgileEncryptionDescriptor = {
27
+ scheme: "agile";
28
+ material: AgileKeyMaterial;
29
+ };
30
+ declare const parseAgileEncryptionInfo: (stream: Uint8Array) => AgileEncryptionDescriptor;
31
+ //#endregion
32
+ export { AgileEncryptionDescriptor, AgileKeyMaterial, parseAgileEncryptionInfo };
@@ -0,0 +1,128 @@
1
+ import { decodeBase64 } from "./cryptoBytes.js";
2
+ import { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError } from "./errors.js";
3
+ //#region src/docx/encryption/encryptionInfo.ts
4
+ /**
5
+ * Parse the EncryptionInfo stream for Agile Encryption (Office 2010+).
6
+ *
7
+ * @see https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/87020a34-e73f-4139-99bc-bbdf6cf6fa55
8
+ */
9
+ const AGILE_STREAM_VERSION = 4;
10
+ const AGILE_STREAM_KIND = 4;
11
+ const STANDARD_STREAM_KIND = 3;
12
+ const STREAM_PREFIX_BYTES = 8;
13
+ const readUint16Le = (bytes, offset) => bytes[offset] | bytes[offset + 1] << 8;
14
+ const findElement = (xml, localName) => {
15
+ let index = 0;
16
+ while (index < xml.length) {
17
+ const hit = xml.indexOf(localName, index);
18
+ if (hit === -1) return null;
19
+ const tagStart = xml.lastIndexOf("<", hit);
20
+ if (tagStart === -1 || hit - tagStart > 24) {
21
+ index = hit + localName.length;
22
+ continue;
23
+ }
24
+ const tagEnd = xml.indexOf(">", hit);
25
+ if (tagEnd === -1) return null;
26
+ const tag = xml.slice(tagStart, tagEnd + 1);
27
+ if (tag.includes(localName)) return tag;
28
+ index = hit + localName.length;
29
+ }
30
+ return null;
31
+ };
32
+ const parseAttributes = (tag) => {
33
+ const attrs = {};
34
+ let i = tag.indexOf(" ");
35
+ if (i === -1) return attrs;
36
+ while (i < tag.length) {
37
+ while (i < tag.length && (tag[i] === " " || tag[i] === "\n" || tag[i] === "\r" || tag[i] === " ")) i++;
38
+ const nameStart = i;
39
+ while (i < tag.length && tag[i] !== "=" && tag[i] !== ">" && tag[i] !== "/") i++;
40
+ if (i >= tag.length || tag[i] !== "=") break;
41
+ const name = tag.slice(nameStart, i).trim();
42
+ i++;
43
+ const quote = tag[i];
44
+ if (quote !== "\"" && quote !== "'") break;
45
+ i++;
46
+ const valueStart = i;
47
+ while (i < tag.length && tag[i] !== quote) i++;
48
+ if (i >= tag.length) break;
49
+ attrs[name] = tag.slice(valueStart, i);
50
+ i++;
51
+ }
52
+ return attrs;
53
+ };
54
+ const requireAttr = (attrs, name, element) => {
55
+ const value = attrs[name];
56
+ if (value === void 0) throw new DocxEncryptionError({
57
+ code: DOCX_ENCRYPTION_ERROR_CODES.DECRYPTION_FAILED,
58
+ message: `EncryptionInfo <${element}> missing attribute "${name}"`
59
+ });
60
+ return value;
61
+ };
62
+ const requireInt = (attrs, name, element) => {
63
+ const parsed = Number.parseInt(requireAttr(attrs, name, element), 10);
64
+ if (Number.isNaN(parsed)) throw new DocxEncryptionError({
65
+ code: DOCX_ENCRYPTION_ERROR_CODES.DECRYPTION_FAILED,
66
+ message: `EncryptionInfo <${element}> attribute "${name}" is not an integer`
67
+ });
68
+ return parsed;
69
+ };
70
+ const requireBase64 = (attrs, name, element) => decodeBase64(requireAttr(attrs, name, element));
71
+ const parseAgileXml = (xmlBytes) => {
72
+ const xml = new TextDecoder("utf-8").decode(xmlBytes);
73
+ const keyDataTag = findElement(xml, "keyData");
74
+ const integrityTag = findElement(xml, "dataIntegrity");
75
+ const encryptedKeyTag = findElement(xml, "encryptedKey");
76
+ if (!keyDataTag || !integrityTag || !encryptedKeyTag) throw new DocxEncryptionError({
77
+ code: DOCX_ENCRYPTION_ERROR_CODES.DECRYPTION_FAILED,
78
+ message: "EncryptionInfo XML is missing required agile encryption elements"
79
+ });
80
+ const keyData = parseAttributes(keyDataTag);
81
+ const integrity = parseAttributes(integrityTag);
82
+ const encryptedKey = parseAttributes(encryptedKeyTag);
83
+ return {
84
+ packageSalt: requireBase64(keyData, "saltValue", "keyData"),
85
+ packageKeyBits: requireInt(keyData, "keyBits", "keyData"),
86
+ packageHash: requireAttr(keyData, "hashAlgorithm", "keyData"),
87
+ packageHashBytes: requireInt(keyData, "hashSize", "keyData"),
88
+ cipherBlockBytes: requireInt(keyData, "blockSize", "keyData"),
89
+ cipherName: requireAttr(keyData, "cipherAlgorithm", "keyData"),
90
+ cipherMode: requireAttr(keyData, "cipherChaining", "keyData"),
91
+ passwordIterations: requireInt(encryptedKey, "spinCount", "encryptedKey"),
92
+ passwordSalt: requireBase64(encryptedKey, "saltValue", "encryptedKey"),
93
+ passwordHash: requireAttr(encryptedKey, "hashAlgorithm", "encryptedKey"),
94
+ passwordKeyBits: requireInt(encryptedKey, "keyBits", "encryptedKey"),
95
+ passwordBlockBytes: requireInt(encryptedKey, "blockSize", "encryptedKey"),
96
+ encryptedVerifierInput: requireBase64(encryptedKey, "encryptedVerifierHashInput", "encryptedKey"),
97
+ encryptedVerifierDigest: requireBase64(encryptedKey, "encryptedVerifierHashValue", "encryptedKey"),
98
+ encryptedIntermediateKey: requireBase64(encryptedKey, "encryptedKeyValue", "encryptedKey"),
99
+ encryptedIntegrityKey: requireBase64(integrity, "encryptedHmacKey", "dataIntegrity"),
100
+ encryptedIntegrityDigest: requireBase64(integrity, "encryptedHmacValue", "dataIntegrity")
101
+ };
102
+ };
103
+ const parseAgileEncryptionInfo = (stream) => {
104
+ if (stream.length < STREAM_PREFIX_BYTES) throw new DocxEncryptionError({
105
+ code: DOCX_ENCRYPTION_ERROR_CODES.DECRYPTION_FAILED,
106
+ message: `EncryptionInfo stream too short (${stream.length} bytes)`
107
+ });
108
+ const version = readUint16Le(stream, 0);
109
+ const kind = readUint16Le(stream, 2);
110
+ if (version === AGILE_STREAM_VERSION && kind === AGILE_STREAM_KIND) return {
111
+ scheme: "agile",
112
+ material: parseAgileXml(stream.subarray(STREAM_PREFIX_BYTES))
113
+ };
114
+ if ((version === 3 || version === AGILE_STREAM_VERSION) && kind === STANDARD_STREAM_KIND) throw new DocxEncryptionError({
115
+ code: DOCX_ENCRYPTION_ERROR_CODES.ENCRYPTION_UNSUPPORTED,
116
+ message: "Standard Encryption (Office 2007) is not supported. Only Agile Encryption (Office 2010+) is supported."
117
+ });
118
+ if (version <= 2) throw new DocxEncryptionError({
119
+ code: DOCX_ENCRYPTION_ERROR_CODES.ENCRYPTION_UNSUPPORTED,
120
+ message: "Legacy RC4 encryption is not supported. Only Agile Encryption (Office 2010+) is supported."
121
+ });
122
+ throw new DocxEncryptionError({
123
+ code: DOCX_ENCRYPTION_ERROR_CODES.ENCRYPTION_UNSUPPORTED,
124
+ message: `Unrecognized EncryptionInfo header version=${version} kind=${kind}`
125
+ });
126
+ };
127
+ //#endregion
128
+ export { parseAgileEncryptionInfo };
@@ -0,0 +1,18 @@
1
+ //#region src/docx/encryption/errors.d.ts
2
+ /** Machine-readable codes for password-protected OOXML failures. */
3
+ declare const DOCX_ENCRYPTION_ERROR_CODES: {
4
+ readonly PASSWORD_REQUIRED: "DOCX_PASSWORD_REQUIRED";
5
+ readonly PASSWORD_INVALID: "DOCX_PASSWORD_INVALID";
6
+ readonly ENCRYPTION_UNSUPPORTED: "DOCX_ENCRYPTION_UNSUPPORTED";
7
+ readonly DECRYPTION_FAILED: "DOCX_DECRYPTION_FAILED";
8
+ };
9
+ type DocxEncryptionErrorCode = (typeof DOCX_ENCRYPTION_ERROR_CODES)[keyof typeof DOCX_ENCRYPTION_ERROR_CODES];
10
+ declare const DocxEncryptionError_base: import("better-result").TaggedErrorClass<"DocxEncryptionError", {
11
+ message: string;
12
+ code: DocxEncryptionErrorCode;
13
+ cause?: unknown;
14
+ }>;
15
+ declare class DocxEncryptionError extends DocxEncryptionError_base {}
16
+ declare const isDocxEncryptionError: (error: unknown) => error is DocxEncryptionError;
17
+ //#endregion
18
+ export { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError, DocxEncryptionErrorCode, isDocxEncryptionError };
@@ -0,0 +1,13 @@
1
+ import { TaggedError } from "better-result";
2
+ //#region src/docx/encryption/errors.ts
3
+ /** Machine-readable codes for password-protected OOXML failures. */
4
+ const DOCX_ENCRYPTION_ERROR_CODES = {
5
+ PASSWORD_REQUIRED: "DOCX_PASSWORD_REQUIRED",
6
+ PASSWORD_INVALID: "DOCX_PASSWORD_INVALID",
7
+ ENCRYPTION_UNSUPPORTED: "DOCX_ENCRYPTION_UNSUPPORTED",
8
+ DECRYPTION_FAILED: "DOCX_DECRYPTION_FAILED"
9
+ };
10
+ var DocxEncryptionError = class extends TaggedError("DocxEncryptionError")() {};
11
+ const isDocxEncryptionError = (error) => error instanceof DocxEncryptionError;
12
+ //#endregion
13
+ export { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError, isDocxEncryptionError };
@@ -0,0 +1,4 @@
1
+ import { DOCX_CONTAINER_TYPES, DocxContainerType, detectDocxContainerType } from "./containerFormat.js";
2
+ import { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError, DocxEncryptionErrorCode, isDocxEncryptionError } from "./errors.js";
3
+ import { DecryptDocxOptions, DecryptDocxResult, decryptDocxIfNeeded, openDocxBuffer } from "./openEncryptedDocx.js";
4
+ export { DOCX_CONTAINER_TYPES, DOCX_ENCRYPTION_ERROR_CODES, type DecryptDocxOptions, type DecryptDocxResult, type DocxContainerType, DocxEncryptionError, type DocxEncryptionErrorCode, decryptDocxIfNeeded, detectDocxContainerType, isDocxEncryptionError, openDocxBuffer };
@@ -0,0 +1,4 @@
1
+ import { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError, isDocxEncryptionError } from "./errors.js";
2
+ import { DOCX_CONTAINER_TYPES, detectDocxContainerType } from "./containerFormat.js";
3
+ import { decryptDocxIfNeeded, openDocxBuffer } from "./openEncryptedDocx.js";
4
+ export { DOCX_CONTAINER_TYPES, DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError, decryptDocxIfNeeded, detectDocxContainerType, isDocxEncryptionError, openDocxBuffer };
@@ -0,0 +1,28 @@
1
+ //#region src/docx/encryption/openEncryptedDocx.d.ts
2
+ /**
3
+ * Decrypt password-protected OOXML before ZIP extraction.
4
+ *
5
+ * Implementation is derived from [MS-OFFCRYPTO] and [MS-CFB] only.
6
+ *
7
+ * @see https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/
8
+ */
9
+ type DecryptDocxOptions = {
10
+ /** Password for Agile-encrypted .docx files (Office 2010+). */password?: string | undefined;
11
+ };
12
+ type DecryptDocxResult = {
13
+ /** Plaintext OOXML ZIP bytes, or the original buffer when unencrypted. */data: ArrayBuffer; /** True when the input was an encrypted CFB container and decryption ran. */
14
+ wasEncrypted: boolean;
15
+ };
16
+ /**
17
+ * Decrypt a password-protected `.docx` when needed, or return the input unchanged.
18
+ *
19
+ * Supports Agile Encryption (Office 2010+). Standard Encryption and legacy RC4
20
+ * are rejected with `DOCX_ENCRYPTION_UNSUPPORTED`.
21
+ *
22
+ * Save produces a standard unencrypted OOXML ZIP; folio does not re-encrypt on export.
23
+ */
24
+ declare const decryptDocxIfNeeded: (data: ArrayBuffer | Uint8Array, options?: DecryptDocxOptions) => Promise<DecryptDocxResult>;
25
+ /** Open a `.docx` buffer for parsing: decrypt when encrypted, then return the ZIP bytes. */
26
+ declare const openDocxBuffer: (data: ArrayBuffer | Uint8Array, options?: DecryptDocxOptions) => Promise<ArrayBuffer>;
27
+ //#endregion
28
+ export { DecryptDocxOptions, DecryptDocxResult, decryptDocxIfNeeded, openDocxBuffer };
@@ -0,0 +1,65 @@
1
+ import { toArrayBuffer } from "./cryptoBytes.js";
2
+ import { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError } from "./errors.js";
3
+ import { decryptAgileEncryptedPackage } from "./agileDecryption.js";
4
+ import { readEncryptedPackageStreams } from "./compoundFile.js";
5
+ import { DOCX_CONTAINER_TYPES, detectDocxContainerType } from "./containerFormat.js";
6
+ import { parseAgileEncryptionInfo } from "./encryptionInfo.js";
7
+ //#region src/docx/encryption/openEncryptedDocx.ts
8
+ /**
9
+ * Decrypt password-protected OOXML before ZIP extraction.
10
+ *
11
+ * Implementation is derived from [MS-OFFCRYPTO] and [MS-CFB] only.
12
+ *
13
+ * @see https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/
14
+ */
15
+ const assertZipPayload = (bytes) => {
16
+ if (detectDocxContainerType(bytes) !== DOCX_CONTAINER_TYPES.ZIP) throw new DocxEncryptionError({
17
+ code: DOCX_ENCRYPTION_ERROR_CODES.DECRYPTION_FAILED,
18
+ message: "Decrypted output is not a valid ZIP archive — the file may be corrupt"
19
+ });
20
+ };
21
+ /**
22
+ * Decrypt a password-protected `.docx` when needed, or return the input unchanged.
23
+ *
24
+ * Supports Agile Encryption (Office 2010+). Standard Encryption and legacy RC4
25
+ * are rejected with `DOCX_ENCRYPTION_UNSUPPORTED`.
26
+ *
27
+ * Save produces a standard unencrypted OOXML ZIP; folio does not re-encrypt on export.
28
+ */
29
+ const decryptDocxIfNeeded = async (data, options = {}) => {
30
+ const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
31
+ const container = detectDocxContainerType(bytes);
32
+ if (container === DOCX_CONTAINER_TYPES.ZIP) return {
33
+ data: toArrayBuffer(bytes),
34
+ wasEncrypted: false
35
+ };
36
+ if (container !== DOCX_CONTAINER_TYPES.CFB) throw new DocxEncryptionError({
37
+ code: DOCX_ENCRYPTION_ERROR_CODES.DECRYPTION_FAILED,
38
+ message: "Unrecognized file format — expected a .docx (ZIP) or encrypted .docx (OLE/CFB)"
39
+ });
40
+ if (options.password == null) throw new DocxEncryptionError({
41
+ code: DOCX_ENCRYPTION_ERROR_CODES.PASSWORD_REQUIRED,
42
+ message: "This document is password-protected. A password is required to open it."
43
+ });
44
+ let streams;
45
+ try {
46
+ streams = readEncryptedPackageStreams(bytes);
47
+ } catch (cause) {
48
+ throw new DocxEncryptionError({
49
+ code: DOCX_ENCRYPTION_ERROR_CODES.DECRYPTION_FAILED,
50
+ message: "Failed to read encrypted OOXML streams",
51
+ cause
52
+ });
53
+ }
54
+ const descriptor = parseAgileEncryptionInfo(streams.encryptionInfo);
55
+ const decryptedZip = await decryptAgileEncryptedPackage(options.password, descriptor.material, streams.encryptedPackage);
56
+ assertZipPayload(decryptedZip);
57
+ return {
58
+ data: toArrayBuffer(decryptedZip),
59
+ wasEncrypted: true
60
+ };
61
+ };
62
+ /** Open a `.docx` buffer for parsing: decrypt when encrypted, then return the ZIP bytes. */
63
+ const openDocxBuffer = async (data, options = {}) => (await decryptDocxIfNeeded(data, options)).data;
64
+ //#endregion
65
+ export { decryptDocxIfNeeded, openDocxBuffer };
@@ -1,2 +1,4 @@
1
1
  import { getCachedNumberingMap } from "./numberingParser.js";
2
- export { getCachedNumberingMap };
2
+ import { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError, DocxEncryptionErrorCode, isDocxEncryptionError } from "./encryption/errors.js";
3
+ import { DecryptDocxOptions, DecryptDocxResult, decryptDocxIfNeeded, openDocxBuffer } from "./encryption/openEncryptedDocx.js";
4
+ export { DOCX_ENCRYPTION_ERROR_CODES, type DecryptDocxOptions, type DecryptDocxResult, DocxEncryptionError, type DocxEncryptionErrorCode, decryptDocxIfNeeded, getCachedNumberingMap, isDocxEncryptionError, openDocxBuffer };
@@ -1,2 +1,4 @@
1
1
  import { getCachedNumberingMap } from "./numberingParser.js";
2
- export { getCachedNumberingMap };
2
+ import { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError, isDocxEncryptionError } from "./encryption/errors.js";
3
+ import { decryptDocxIfNeeded, openDocxBuffer } from "./encryption/openEncryptedDocx.js";
4
+ export { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError, decryptDocxIfNeeded, getCachedNumberingMap, isDocxEncryptionError, openDocxBuffer };
@@ -1,6 +1,6 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
+ import { DocxUnzipOptions } from "./unzip.js";
2
3
  import { DocxInput } from "../utils/docxInput.js";
3
- import { DocxUnzipLimits } from "./unzip.js";
4
4
 
5
5
  //#region src/docx/parser.d.ts
6
6
  /**
@@ -24,10 +24,9 @@ type ParseOptions = {
24
24
  preloadFonts?: boolean; /** Whether to parse headers/footers (default: true) */
25
25
  parseHeadersFooters?: boolean; /** Whether to parse footnotes/endnotes (default: true) */
26
26
  parseNotes?: boolean; /** Whether to detect template variables (default: true) */
27
- detectVariables?: boolean; /** Security limits for DOCX ZIP extraction */
28
- unzipLimits?: Partial<Omit<DocxUnzipLimits, "allowedMediaMimeTypes">> & {
29
- allowedMediaMimeTypes?: Iterable<string>;
30
- }; /** Optional async hook to override display URLs for non-browser media. */
27
+ detectVariables?: boolean; /** Password for Agile-encrypted .docx files (Office 2010+). */
28
+ password?: string | undefined; /** Security limits for DOCX ZIP extraction */
29
+ unzipLimits?: DocxUnzipOptions; /** Optional async hook to override display URLs for non-browser media. */
31
30
  mediaResolver?: MediaResolver;
32
31
  };
33
32
  /**
@@ -3,6 +3,7 @@ import { applyThemeFontLang, parseTheme } from "./themeParser.js";
3
3
  import { parseEndnotes, parseFootnotes } from "./footnoteParser.js";
4
4
  import { DocxModelValidationError, formatDocumentModelIssues, validateFolioDocumentModel } from "./modelValidation.js";
5
5
  import { parseNumbering } from "./numberingParser.js";
6
+ import { DocxEncryptionError } from "./encryption/errors.js";
6
7
  import { getMediaMimeType, mediaToDataUrl, unzipDocx } from "./unzip.js";
7
8
  import { parseComments } from "./commentParser.js";
8
9
  import { parseFooter, parseHeader } from "./headerFooterParser.js";
@@ -48,13 +49,17 @@ import { TaggedError } from "better-result";
48
49
  */
49
50
  async function parseDocx(input, options = {}) {
50
51
  const buffer = input instanceof ArrayBuffer ? input : await toArrayBuffer(input);
51
- const { onProgress = () => {}, preloadFonts = true, parseHeadersFooters = true, parseNotes = true, detectVariables = true, unzipLimits, mediaResolver } = options;
52
+ const { onProgress = () => {}, preloadFonts = true, parseHeadersFooters = true, parseNotes = true, detectVariables = true, password, unzipLimits, mediaResolver } = options;
52
53
  const warnings = [];
53
54
  try {
54
55
  const timeStage = (_name, fn) => fn();
55
56
  const timeStageAsync = async (_name, fn) => await fn();
56
57
  onProgress("Extracting DOCX...", 0);
57
- const raw = await timeStageAsync("unzip", () => unzipDocx(buffer, unzipLimits));
58
+ const raw = await timeStageAsync("unzip", () => unzipDocx(buffer, {
59
+ ...unzipLimits,
60
+ password
61
+ }));
62
+ if (raw.wasEncrypted) warnings.push("Document was opened from password-protected storage; saving writes an unencrypted .docx file.");
58
63
  warnings.push(...raw.warnings);
59
64
  onProgress("Extracted DOCX", 10);
60
65
  onProgress("Parsing relationships...", 10);
@@ -186,6 +191,7 @@ async function parseDocx(input, options = {}) {
186
191
  onProgress("Complete", 100);
187
192
  return document;
188
193
  } catch (error) {
194
+ if (error instanceof DocxEncryptionError) throw error;
189
195
  throw new DocxParseError({
190
196
  message: `Failed to parse DOCX: ${error instanceof Error ? error.message : String(error)}`,
191
197
  cause: error
@@ -13,8 +13,9 @@ type DocxUnzipLimits = {
13
13
  maxTotalUncompressedBytes: number;
14
14
  allowedMediaMimeTypes: ReadonlySet<string>;
15
15
  };
16
- type PartialDocxUnzipLimits = Partial<Omit<DocxUnzipLimits, "allowedMediaMimeTypes">> & {
17
- allowedMediaMimeTypes?: Iterable<string>;
16
+ type DocxUnzipOptions = Partial<Omit<DocxUnzipLimits, "allowedMediaMimeTypes">> & {
17
+ allowedMediaMimeTypes?: Iterable<string>; /** Password for Agile-encrypted .docx files (Office 2010+). */
18
+ password?: string | undefined;
18
19
  };
19
20
  /**
20
21
  * Raw extracted content from a DOCX file
@@ -45,7 +46,8 @@ type RawDocxContent = {
45
46
  allXml: Map<string, string>;
46
47
  originalZip: JSZip;
47
48
  originalBuffer: ArrayBuffer;
48
- warnings: string[];
49
+ warnings: string[]; /** True when the input was a password-protected CFB container. */
50
+ wasEncrypted: boolean;
49
51
  };
50
52
  /**
51
53
  * Extract all content from a DOCX file
@@ -53,7 +55,7 @@ type RawDocxContent = {
53
55
  * @param buffer - DOCX file as ArrayBuffer
54
56
  * @returns Promise resolving to extracted content
55
57
  */
56
- declare function unzipDocx(buffer: ArrayBuffer, options?: PartialDocxUnzipLimits): Promise<RawDocxContent>;
58
+ declare function unzipDocx(buffer: ArrayBuffer, options?: DocxUnzipOptions): Promise<RawDocxContent>;
57
59
  declare function isPreservableDocxEntry(path: string): boolean;
58
60
  /**
59
61
  * Get a list of all files in the DOCX
@@ -115,4 +117,4 @@ declare function getContentSummary(content: RawDocxContent): {
115
117
  totalFiles: number;
116
118
  };
117
119
  //#endregion
118
- export { DocxSecurityError, DocxUnzipLimits, RawDocxContent, extractFile, getContentSummary, getFileList, getMediaMimeType, hasFile, isPreservableDocxEntry, mediaToDataUrl, unzipDocx };
120
+ export { DocxSecurityError, DocxUnzipLimits, DocxUnzipOptions, RawDocxContent, extractFile, getContentSummary, getFileList, getMediaMimeType, hasFile, isPreservableDocxEntry, mediaToDataUrl, unzipDocx };
@@ -1,3 +1,5 @@
1
+ import { DOCX_CONTAINER_TYPES, detectDocxContainerType } from "./encryption/containerFormat.js";
2
+ import { openDocxBuffer } from "./encryption/openEncryptedDocx.js";
1
3
  import JSZip from "jszip";
2
4
  //#region src/docx/unzip.ts
3
5
  /**
@@ -71,7 +73,10 @@ const ZIP_CENTRAL_DIRECTORY_FILE_HEADER_SIZE = 46;
71
73
  async function unzipDocx(buffer, options = {}) {
72
74
  const limits = createUnzipLimits(options);
73
75
  if (buffer.byteLength > limits.maxInputBytes) throw new DocxSecurityError("DOCX file exceeds the maximum allowed size");
74
- const loaded = await loadDocxZip(buffer, limits.maxFiles);
76
+ const zipBuffer = await openDocxBuffer(buffer, { password: options.password });
77
+ if (zipBuffer.byteLength > limits.maxInputBytes) throw new DocxSecurityError("DOCX file exceeds the maximum allowed size");
78
+ const wasEncrypted = detectDocxContainerType(buffer) === DOCX_CONTAINER_TYPES.CFB;
79
+ const loaded = await loadDocxZip(zipBuffer, limits.maxFiles);
75
80
  if (loaded.buffer.byteLength > limits.maxInputBytes) throw new DocxSecurityError("DOCX file exceeds the maximum allowed size");
76
81
  const { zip } = loaded;
77
82
  const entries = Object.entries(zip.files).filter(([, file]) => !file.dir);
@@ -102,7 +107,8 @@ async function unzipDocx(buffer, options = {}) {
102
107
  allXml: /* @__PURE__ */ new Map(),
103
108
  originalZip: zip,
104
109
  originalBuffer: loaded.buffer,
105
- warnings: []
110
+ warnings: [],
111
+ wasEncrypted
106
112
  };
107
113
  let totalUncompressedBytes = 0;
108
114
  for (const [path, file] of entries) {
package/dist/index.d.ts CHANGED
@@ -8,21 +8,22 @@ import { AIBarStatus, AIChatMode, AICitation, AICitationSource, AIGenerateInput,
8
8
  import { ApplyResult, applySuggestions } from "./ai-suggestions/apply.js";
9
9
  import { ResolvedAnchor, isSuggestionStale, resolveSuggestionAnchor } from "./ai-suggestions/conflict.js";
10
10
  import { PositionalText, buildPositionalText } from "./ai-suggestions/text-positions.js";
11
- import { TemplatePreviewSpan, TemplatePreviewValue, TemplatePreviewValues, setTemplatePreviewValues } from "./prosemirror/plugins/templatePreviewValues.js";
12
- import { DocxCompatibility } from "./docx/compatibility.js";
13
- import { createDocx } from "./docx/rezip.js";
14
- import { EmbeddedFont, EmbeddedFontParts, extractEmbeddedFonts, getEmbeddedFontFaces } from "./fonts/embeddedFonts.js";
15
11
  import { CreateEmptyDocumentOptions, createEmptyDocument } from "./utils/createDocument.js";
12
+ import { createDocx } from "./docx/rezip.js";
13
+ import { DocxCompatibility } from "./docx/compatibility.js";
16
14
  import { setAISuggestionsMeta, setFocusedSuggestionMeta } from "./prosemirror/plugins/aiSuggestionDecorations.js";
17
15
  import { scrollFolioPositionIntoView } from "./paged-layout/scrollToPmPosition.js";
18
16
  import { getFolioCaretViewportRect, getFolioSelectionViewportRect } from "./paged-layout/selectionViewportRect.js";
19
17
  import { AICitationRange, createAICitationDecorationsPlugin, setAICitationsMeta, setActiveCitationMeta } from "./prosemirror/plugins/aiCitationDecorations.js";
20
18
  import { AnonymizationMatch, AnonymizationTerm, anonymizationDecorationsKey, getAnonymizationMatches, setAnonymizationTermsMeta } from "./prosemirror/plugins/anonymizationDecorations.js";
21
19
  import { DirectiveKind, DirectiveRange, getTemplateDirectives, scanDirectives } from "./prosemirror/plugins/templateDirectives.js";
20
+ import { TemplatePreviewSpan, TemplatePreviewValue, TemplatePreviewValues, setTemplatePreviewValues } from "./prosemirror/plugins/templatePreviewValues.js";
22
21
  import { TemplateSlashMenuKeyAction, TemplateSlashMenuState, clearTemplateSlashMenu, consumeTemplateSlashQuery, getTemplateSlashMenu, resetTemplateSlashQuery, templateSlashMenuKey } from "./prosemirror/plugins/templateSlashMenu.js";
23
22
  import { AcceptAutocompleteResult, AutocompleteSuggestionPluginOptions, AutocompleteSuggestionState, AutocompleteSuggestionStatus, AutocompleteTriggerCheck, AutocompleteTriggerOptions, AutocompleteTriggerSkipReason, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, acceptAutocompleteSuggestion, acceptAutocompleteWord, appendAutocompleteToken, autocompleteSuggestionKey, autocompleteSuggestionPlugin, clearAutocompleteSuggestion, finishAutocompleteSuggestion, getAutocompleteSuggestion, shouldTriggerAutocomplete, startAutocompleteSuggestion } from "./prosemirror/plugins/autocompleteSuggestion.js";
24
23
  import { ImageMeta, ImageRef, MarkdownOptions, MarkdownResult } from "./markdown/types.js";
25
24
  import { fromMarkdown } from "./markdown/fromMarkdown.js";
26
25
  import { toMarkdown, toMarkdownResult } from "./markdown/index.js";
26
+ import { EmbeddedFont, EmbeddedFontParts, extractEmbeddedFonts, getEmbeddedFontFaces } from "./fonts/embeddedFonts.js";
27
+ import { getGoogleFontsEnabled, setGoogleFontsEnabled } from "./utils/fontResolver.js";
27
28
  type Document = document_d_exports.Document;
28
- export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocxCompatibility, type EmbeddedFont, type EmbeddedFontParts, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAISignatureParty, type FolioBlockId, type ImageMeta, type ImageRef, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applySuggestions, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, deriveBlockId, diffWordSegments, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, isFolioBlockId, isSequentialFolioBlockId, isSuggestionStale, normalizeFolioAIBlockText, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setFocusedSuggestionMeta, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
29
+ export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocxCompatibility, type EmbeddedFont, type EmbeddedFontParts, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAISignatureParty, type FolioBlockId, type ImageMeta, type ImageRef, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applySuggestions, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, deriveBlockId, diffWordSegments, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, isFolioBlockId, isSequentialFolioBlockId, isSuggestionStale, normalizeFolioAIBlockText, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import { createFolioAIEditSnapshot, hashFolioAIBlockText, normalizeFolioAIBlockT
9
9
  import { diffWordSegments } from "./ai-edits/word-diff.js";
10
10
  import { applyFolioAIEditOperations } from "./ai-edits/apply.js";
11
11
  import { setAISuggestionsMeta, setFocusedSuggestionMeta } from "./prosemirror/plugins/aiSuggestionDecorations.js";
12
+ import { getGoogleFontsEnabled, setGoogleFontsEnabled } from "./utils/fontResolver.js";
12
13
  import { scrollFolioPositionIntoView } from "./paged-layout/scrollToPmPosition.js";
13
14
  import { getFolioCaretViewportRect, getFolioSelectionViewportRect } from "./paged-layout/selectionViewportRect.js";
14
15
  import { createAICitationDecorationsPlugin, setAICitationsMeta, setActiveCitationMeta } from "./prosemirror/plugins/aiCitationDecorations.js";
@@ -20,4 +21,4 @@ import { DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, acceptAutocompleteSuggestion, acc
20
21
  import { fromMarkdown } from "./markdown/fromMarkdown.js";
21
22
  import { toMarkdown, toMarkdownResult } from "./markdown/index.js";
22
23
  import { extractEmbeddedFonts, getEmbeddedFontFaces } from "./fonts/embeddedFonts.js";
23
- export { DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applySuggestions, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, deriveBlockId, diffWordSegments, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, isFolioBlockId, isSequentialFolioBlockId, isSuggestionStale, normalizeFolioAIBlockText, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setFocusedSuggestionMeta, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
24
+ export { DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applySuggestions, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, deriveBlockId, diffWordSegments, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, isFolioBlockId, isSequentialFolioBlockId, isSuggestionStale, normalizeFolioAIBlockText, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
@@ -12,7 +12,7 @@ type HeaderFooterMetrics = {
12
12
  };
13
13
  margins: PageMargins;
14
14
  };
15
- declare function normalizeHeaderFooterMeasureBlocks(blocks: FlowBlock[]): FlowBlock[];
15
+ declare function normalizeHeaderFooterMeasureBlocks(blocks: FlowBlock[], section?: HeaderFooterMetrics["section"]): FlowBlock[];
16
16
  declare function resolveHeaderFooterPositionedVisualTop(position: ImageRunPosition | undefined, elementHeight: number, sourceY: number, flowHeight: number, metrics: HeaderFooterMetrics): number;
17
17
  declare function resolveHeaderFooterVisualTop(run: ImageRun, paragraphY: number, flowHeight: number, metrics: HeaderFooterMetrics): number;
18
18
  declare function calculateHeaderFooterVisualBounds(blocks: FlowBlock[], measures: Measure[], flowHeight: number, metrics: HeaderFooterMetrics): {