reamkit 1.2.0 → 1.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 (48) hide show
  1. package/README.md +10 -6
  2. package/dist/esm/core/converter/facade.d.ts +2 -2
  3. package/dist/esm/core/converter/facade.js +12 -0
  4. package/dist/esm/core/converter/ream.d.ts +1 -1
  5. package/dist/esm/core/converter/ream.js +14 -3
  6. package/dist/esm/core/document-model/types.d.ts +2 -0
  7. package/dist/esm/core/drawingml/chart-geometry.d.ts +1 -1
  8. package/dist/esm/core/drawingml/chart-geometry.js +79 -117
  9. package/dist/esm/core/drawingml/chart-parser.d.ts +3 -0
  10. package/dist/esm/core/drawingml/chart-parser.js +28 -1
  11. package/dist/esm/core/numbering/apply.js +2 -1
  12. package/dist/esm/core/opc/index.d.ts +3 -0
  13. package/dist/esm/core/opc/index.js +1 -0
  14. package/dist/esm/core/opc/opc-writer.d.ts +28 -0
  15. package/dist/esm/core/opc/opc-writer.js +60 -0
  16. package/dist/esm/core/opc/package.js +3 -2
  17. package/dist/esm/core/opc/relationship-types.d.ts +1 -0
  18. package/dist/esm/core/opc/relationship-types.js +5 -2
  19. package/dist/esm/excel/print-model.d.ts +3 -0
  20. package/dist/esm/excel/print-model.js +4 -8
  21. package/dist/esm/excel/sheet-drawing.d.ts +9 -0
  22. package/dist/esm/excel/sheet-drawing.js +113 -0
  23. package/dist/esm/excel/worksheet-parser.d.ts +1 -0
  24. package/dist/esm/excel/worksheet-parser.js +4 -1
  25. package/dist/esm/excel/xlsx-reader.js +41 -0
  26. package/dist/esm/excel/xlsx-to-pdf.js +25 -18
  27. package/dist/esm/index.d.ts +2 -0
  28. package/dist/esm/index.js +2 -1
  29. package/dist/esm/layout/page-doc.d.ts +1 -0
  30. package/dist/esm/layout/styled-layout.d.ts +2 -0
  31. package/dist/esm/layout/styled-layout.js +321 -171
  32. package/dist/esm/pdf/encryption.d.ts +34 -0
  33. package/dist/esm/pdf/encryption.js +143 -0
  34. package/dist/esm/pdf/index.d.ts +2 -1
  35. package/dist/esm/pdf/styled-page-emitter.d.ts +2 -0
  36. package/dist/esm/pdf/styled-page-emitter.js +25 -7
  37. package/dist/esm/pdf/styled-page-renderer.d.ts +1 -0
  38. package/dist/esm/pdf/styled-page-renderer.js +17 -2
  39. package/dist/esm/pdf/writer.d.ts +2 -0
  40. package/dist/esm/pdf/writer.js +6 -1
  41. package/dist/esm/word/document-parser.js +51 -13
  42. package/dist/esm/word/docx-reader.js +7 -11
  43. package/dist/esm/word/docx-to-pdf.js +23 -13
  44. package/dist/esm/word/docx-writer.d.ts +4 -0
  45. package/dist/esm/word/docx-writer.js +602 -0
  46. package/dist/esm/word/drawing-parser.d.ts +1 -0
  47. package/dist/esm/word/drawing-parser.js +37 -2
  48. package/package.json +4 -3
@@ -0,0 +1,34 @@
1
+ import { PdfValue } from './objects.js';
2
+ export interface PdfPermissions {
3
+ /** Bit 3 — print the document (default true). */
4
+ readonly printing?: boolean;
5
+ /** Bit 4 — modify contents (default true). */
6
+ readonly modifying?: boolean;
7
+ /** Bit 5 — copy text and graphics (default true). */
8
+ readonly copying?: boolean;
9
+ /** Bit 6 — add/modify annotations (default true). */
10
+ readonly annotating?: boolean;
11
+ /** Bit 9 — fill in form fields (default true). */
12
+ readonly fillingForms?: boolean;
13
+ /** Bit 10 — extraction for accessibility (default true; forced by PDF/UA). */
14
+ readonly contentAccessibility?: boolean;
15
+ /** Bit 11 — assemble (insert/rotate/delete pages) (default true). */
16
+ readonly documentAssembly?: boolean;
17
+ }
18
+ export interface PdfEncryptOptions {
19
+ /** Required to OPEN the document. Empty string = opens without a prompt. */
20
+ readonly userPassword?: string;
21
+ /** Overrides the permission restrictions. Defaults to the user password. */
22
+ readonly ownerPassword?: string;
23
+ readonly permissions?: PdfPermissions;
24
+ }
25
+ export interface PreparedEncryption {
26
+ readonly fileKey: Uint8Array;
27
+ readonly encryptDict: PdfValue;
28
+ }
29
+ export declare function permissionBits(p: PdfPermissions | undefined): number;
30
+ export declare function aesCbcNoPadDecrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Promise<Uint8Array>;
31
+ export declare function hardHash(password: Uint8Array, salt: Uint8Array, userData: Uint8Array): Promise<Uint8Array>;
32
+ export declare function preparePdfEncryption(options: PdfEncryptOptions): Promise<PreparedEncryption>;
33
+ export declare function decryptBytes(fileKey: Uint8Array, data: Uint8Array): Promise<Uint8Array>;
34
+ export declare function encryptObjectGraph(value: PdfValue, fileKey: Uint8Array): Promise<PdfValue>;
@@ -0,0 +1,143 @@
1
+ import { PdfHexString, PdfStream, dict, name } from "./objects.js";
2
+ //#region src/pdf/encryption.ts
3
+ var encoder = new TextEncoder();
4
+ var subtle = globalThis.crypto.subtle;
5
+ function permissionBits(p) {
6
+ let bits = -4;
7
+ const off = (bit) => bits &= ~(1 << bit - 1);
8
+ if (p?.printing === false) {
9
+ off(3);
10
+ off(12);
11
+ }
12
+ if (p?.modifying === false) off(4);
13
+ if (p?.copying === false) off(5);
14
+ if (p?.annotating === false) off(6);
15
+ if (p?.fillingForms === false) off(9);
16
+ if (p?.contentAccessibility === false) off(10);
17
+ if (p?.documentAssembly === false) off(11);
18
+ return bits | 0;
19
+ }
20
+ function passwordBytes(password) {
21
+ return encoder.encode(password).slice(0, 127);
22
+ }
23
+ function concat(...parts) {
24
+ const out = new Uint8Array(parts.reduce((s, p) => s + p.length, 0));
25
+ let off = 0;
26
+ for (const p of parts) {
27
+ out.set(p, off);
28
+ off += p.length;
29
+ }
30
+ return out;
31
+ }
32
+ async function sha(algo, data) {
33
+ return new Uint8Array(await subtle.digest(algo, data));
34
+ }
35
+ async function aesCbcNoPad(key, iv, data) {
36
+ const k = await subtle.importKey("raw", key, "AES-CBC", false, ["encrypt"]);
37
+ return new Uint8Array(await subtle.encrypt({
38
+ name: "AES-CBC",
39
+ iv
40
+ }, k, data)).slice(0, data.length);
41
+ }
42
+ async function hardHash(password, salt, userData) {
43
+ let k = await sha("SHA-256", concat(password, salt, userData));
44
+ for (let round = 0;; round++) {
45
+ const block = concat(password, k, userData);
46
+ const k1 = new Uint8Array(block.length * 64);
47
+ for (let i = 0; i < 64; i++) k1.set(block, i * block.length);
48
+ const e = await aesCbcNoPad(k.slice(0, 16), k.slice(16, 32), k1);
49
+ let mod = 0;
50
+ for (let i = 0; i < 16; i++) mod = (mod + e[i]) % 3;
51
+ k = (await sha(mod === 0 ? "SHA-256" : mod === 1 ? "SHA-384" : "SHA-512", e)).slice(0, 64);
52
+ if (round >= 63 && e[e.length - 1] <= round - 31) break;
53
+ }
54
+ return k.slice(0, 32);
55
+ }
56
+ async function preparePdfEncryption(options) {
57
+ const user = passwordBytes(options.userPassword ?? "");
58
+ const owner = passwordBytes(options.ownerPassword ?? options.userPassword ?? "");
59
+ const fileKey = new Uint8Array(32);
60
+ globalThis.crypto.getRandomValues(fileKey);
61
+ const salts = new Uint8Array(36);
62
+ globalThis.crypto.getRandomValues(salts);
63
+ const uvs = salts.slice(0, 8);
64
+ const uks = salts.slice(8, 16);
65
+ const ovs = salts.slice(16, 24);
66
+ const oks = salts.slice(24, 32);
67
+ const U = concat(await hardHash(user, uvs, new Uint8Array(0)), uvs, uks);
68
+ const UE = await aesCbcNoPad(await hardHash(user, uks, new Uint8Array(0)), new Uint8Array(16), fileKey);
69
+ const O = concat(await hardHash(owner, ovs, U), ovs, oks);
70
+ const OE = await aesCbcNoPad(await hardHash(owner, oks, U), new Uint8Array(16), fileKey);
71
+ const p = permissionBits(options.permissions);
72
+ const permsPlain = new Uint8Array(16);
73
+ new DataView(permsPlain.buffer).setInt32(0, p, true);
74
+ permsPlain.set([
75
+ 255,
76
+ 255,
77
+ 255,
78
+ 255
79
+ ], 4);
80
+ permsPlain.set([
81
+ 84,
82
+ 97,
83
+ 100,
84
+ 98
85
+ ], 8);
86
+ permsPlain.set(salts.slice(32, 36), 12);
87
+ const perms = await aesCbcNoPad(fileKey, new Uint8Array(16), permsPlain);
88
+ return {
89
+ fileKey,
90
+ encryptDict: dict({
91
+ Filter: name("Standard"),
92
+ V: 5,
93
+ R: 6,
94
+ Length: 256,
95
+ CF: dict({ StdCF: dict({
96
+ CFM: name("AESV3"),
97
+ AuthEvent: name("DocOpen"),
98
+ Length: 32
99
+ }) }),
100
+ StmF: name("StdCF"),
101
+ StrF: name("StdCF"),
102
+ U: new PdfHexString(U),
103
+ UE: new PdfHexString(UE),
104
+ O: new PdfHexString(O),
105
+ OE: new PdfHexString(OE),
106
+ P: p,
107
+ Perms: new PdfHexString(perms),
108
+ EncryptMetadata: true
109
+ })
110
+ };
111
+ }
112
+ async function encryptBytes(fileKey, data) {
113
+ const iv = new Uint8Array(16);
114
+ globalThis.crypto.getRandomValues(iv);
115
+ const k = await subtle.importKey("raw", fileKey, "AES-CBC", false, ["encrypt"]);
116
+ return concat(iv, new Uint8Array(await subtle.encrypt({
117
+ name: "AES-CBC",
118
+ iv
119
+ }, k, data)));
120
+ }
121
+ async function encryptObjectGraph(value, fileKey) {
122
+ if (typeof value === "string") return new PdfHexString(await encryptBytes(fileKey, encoder.encode(value)));
123
+ if (value instanceof PdfHexString) return new PdfHexString(await encryptBytes(fileKey, value.bytes));
124
+ if (value instanceof PdfStream) {
125
+ const data = await encryptBytes(fileKey, value.data);
126
+ const d = new Map(value.dict);
127
+ d.set("Length", data.length);
128
+ return new PdfStream(d, data);
129
+ }
130
+ if (Array.isArray(value)) {
131
+ const out = [];
132
+ for (const v of value) out.push(await encryptObjectGraph(v, fileKey));
133
+ return out;
134
+ }
135
+ if (value instanceof Map) {
136
+ const out = /* @__PURE__ */ new Map();
137
+ for (const [k, v] of value) out.set(k, await encryptObjectGraph(v, fileKey));
138
+ return out;
139
+ }
140
+ return value;
141
+ }
142
+ //#endregion
143
+ export { encryptObjectGraph, preparePdfEncryption };
@@ -1,6 +1,7 @@
1
1
  export { renderPlainTextPdf } from './text-page-renderer.js';
2
2
  export type { TextRenderOptions, FontInput } from './text-page-renderer.js';
3
- export { renderStyledPdf } from './styled-page-renderer.js';
3
+ export { renderStyledPdf, renderStyledPdfEncrypted } from './styled-page-renderer.js';
4
+ export type { PdfEncryptOptions, PdfPermissions } from './encryption.js';
4
5
  export type { DocumentInfo, StyledRenderOptions, PdfALevel } from './styled-page-renderer.js';
5
6
  export { signPdf } from './signature.js';
6
7
  export type { SignaturePlaceholder, SignerCredentials, SignatureOptions } from './signature.js';
@@ -1,7 +1,9 @@
1
1
  import { LaidOutPdfDocument, StyledRenderOptions } from '../layout/styled-layout.js';
2
2
  import { PdfDocument } from './writer.js';
3
+ import { PdfEncryptOptions } from './encryption.js';
3
4
  type EmitOptions = Pick<StyledRenderOptions, 'attachments' | 'info' | 'language' | 'pdfUA' | 'signaturePlaceholder'>;
4
5
  export declare function emitStyledPdf(laid: LaidOutPdfDocument, options: EmitOptions, doc: PdfDocument): Uint8Array;
6
+ export declare function emitStyledPdfEncrypted(laid: LaidOutPdfDocument, options: EmitOptions, doc: PdfDocument, encrypt: PdfEncryptOptions): Promise<Uint8Array>;
5
7
  export interface PageTagging {
6
8
  next: number;
7
9
  assigned: boolean;
@@ -1,5 +1,6 @@
1
1
  import { dict, name, ref, stream, unicodeString } from "./objects.js";
2
2
  import { embedTtfFont } from "./cid-font.js";
3
+ import { preparePdfEncryption } from "./encryption.js";
3
4
  import { reorderVisual } from "../core/bidi/algorithm.js";
4
5
  import { reverseByCodePoint } from "../core/bidi/index.js";
5
6
  import "../layout/styled-layout.js";
@@ -14,6 +15,21 @@ import { buildXmpPacket } from "./xmp.js";
14
15
  //#region src/pdf/styled-page-emitter.ts
15
16
  var encoder = new TextEncoder();
16
17
  function emitStyledPdf(laid, options, doc) {
18
+ const a = assembleStyledPdf(laid, options, doc);
19
+ return doc.build(a.catalogRef, a.infoRef, a.buildOptions);
20
+ }
21
+ async function emitStyledPdfEncrypted(laid, options, doc, encrypt) {
22
+ const a = assembleStyledPdf(laid, options, doc);
23
+ const prepared = await preparePdfEncryption(encrypt);
24
+ await doc.encryptAll(prepared.fileKey);
25
+ const encryptRef = doc.add(prepared.encryptDict);
26
+ return doc.build(a.catalogRef, a.infoRef, {
27
+ ...a.buildOptions,
28
+ id: true,
29
+ encrypt: encryptRef
30
+ });
31
+ }
32
+ function assembleStyledPdf(laid, options, doc) {
17
33
  const { pages: renderedPages } = laid;
18
34
  const { structBuilder, sectionCtxs, pdfaProfile, tagged } = laid.pdf;
19
35
  const docInfo = options.pdfUA && !options.info?.title ? {
@@ -210,12 +226,14 @@ function emitStyledPdf(laid, options, doc) {
210
226
  else firstPageDict.set("Annots", [ref(fieldRef.id)]);
211
227
  catalogEntries["AcroForm"] = acroForm;
212
228
  }
213
- const catalogRef = doc.add(dict(catalogEntries));
214
- const infoRef = buildInfoDict(doc, docInfo);
215
- return doc.build(catalogRef, infoRef, { ...pdfaProfile ? {
216
- version: pdfaProfile.version,
217
- id: true
218
- } : {} });
229
+ return {
230
+ catalogRef: doc.add(dict(catalogEntries)),
231
+ infoRef: buildInfoDict(doc, docInfo),
232
+ buildOptions: { ...pdfaProfile ? {
233
+ version: pdfaProfile.version,
234
+ id: true
235
+ } : {} }
236
+ };
219
237
  }
220
238
  var DEFAULT_PRODUCER = "Ream";
221
239
  function xmpFromInfo(info, p, pdfUA = false) {
@@ -686,4 +704,4 @@ function formatNumber(n) {
686
704
  return n.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
687
705
  }
688
706
  //#endregion
689
- export { emitStyledPdf };
707
+ export { emitStyledPdf, emitStyledPdfEncrypted };
@@ -1,5 +1,6 @@
1
1
  import { BodyElement } from '../core/document-model/index.js';
2
2
  import { StyledRenderOptions } from '../layout/styled-layout.js';
3
3
  export declare function renderStyledPdf(body: ReadonlyArray<BodyElement>, options: StyledRenderOptions): Uint8Array;
4
+ export declare function renderStyledPdfEncrypted(body: ReadonlyArray<BodyElement>, options: StyledRenderOptions): Promise<Uint8Array>;
4
5
  export * from '../layout/page-doc.js';
5
6
  export * from '../layout/styled-layout.js';
@@ -1,10 +1,25 @@
1
1
  import { PdfDocument } from "./writer.js";
2
2
  import { layoutStyledDocument } from "../layout/styled-layout.js";
3
3
  import "../layout/page-doc.js";
4
- import { emitStyledPdf } from "./styled-page-emitter.js";
4
+ import { emitStyledPdf, emitStyledPdfEncrypted } from "./styled-page-emitter.js";
5
5
  //#region src/pdf/styled-page-renderer.ts
6
6
  function renderStyledPdf(body, options) {
7
+ if (options.encrypt) throw new Error("PDF encryption requires the async conversion path (WebCrypto)");
7
8
  return emitStyledPdf(layoutStyledDocument(body, options), options, new PdfDocument());
8
9
  }
10
+ async function renderStyledPdfEncrypted(body, options) {
11
+ const encrypt = options.encrypt;
12
+ if (!encrypt) return renderStyledPdf(body, options);
13
+ if (options.pdfA) throw new Error("PDF/A forbids encryption (ISO 19005)");
14
+ if (options.signaturePlaceholder) throw new Error("encryption combined with a digital signature is not supported (v1)");
15
+ const effective = options.pdfUA === true ? {
16
+ ...encrypt,
17
+ permissions: {
18
+ ...encrypt.permissions,
19
+ contentAccessibility: true
20
+ }
21
+ } : encrypt;
22
+ return emitStyledPdfEncrypted(layoutStyledDocument(body, options), options, new PdfDocument(), effective);
23
+ }
9
24
  //#endregion
10
- export { renderStyledPdf };
25
+ export { renderStyledPdf, renderStyledPdfEncrypted };
@@ -2,9 +2,11 @@ import { PdfValue, PdfRef } from './objects.js';
2
2
  export interface BuildOptions {
3
3
  readonly version?: string;
4
4
  readonly id?: boolean;
5
+ readonly encrypt?: PdfRef;
5
6
  }
6
7
  export declare class PdfDocument {
7
8
  private readonly objects;
8
9
  add(value: PdfValue): PdfRef;
10
+ encryptAll(fileKey: Uint8Array): Promise<void>;
9
11
  build(root: PdfRef, info?: PdfRef, options?: BuildOptions): Uint8Array;
10
12
  }
@@ -1,4 +1,5 @@
1
1
  import { PdfRef } from "./objects.js";
2
+ import { encryptObjectGraph } from "./encryption.js";
2
3
  import { serializeIndirectObject } from "./serialize.js";
3
4
  //#region src/pdf/writer.ts
4
5
  var encoder = new TextEncoder();
@@ -20,6 +21,9 @@ var PdfDocument = class {
20
21
  });
21
22
  return new PdfRef(id);
22
23
  }
24
+ async encryptAll(fileKey) {
25
+ for (const obj of this.objects) obj.value = await encryptObjectGraph(obj.value, fileKey);
26
+ }
23
27
  build(root, info, options = {}) {
24
28
  const parts = [];
25
29
  const offsets = new Array(this.objects.length + 1).fill(0);
@@ -43,12 +47,13 @@ var PdfDocument = class {
43
47
  }
44
48
  push(encoder.encode(xref));
45
49
  const infoEntry = info ? ` /Info ${info.id} ${info.generation} R` : "";
50
+ const encryptEntry = options.encrypt ? ` /Encrypt ${options.encrypt.id} ${options.encrypt.generation} R` : "";
46
51
  let idEntry = "";
47
52
  if (options.id) {
48
53
  const hashHex = hash128Hex(parts);
49
54
  idEntry = ` /ID [<${hashHex}> <${hashHex}>]`;
50
55
  }
51
- const trailer = `trailer\n<</Size ${totalObjects} /Root ${root.id} ${root.generation} R${infoEntry}${idEntry}>>\nstartxref\n${xrefOffset}\n%%EOF\n`;
56
+ const trailer = `trailer\n<</Size ${totalObjects} /Root ${root.id} ${root.generation} R${infoEntry}${encryptEntry}${idEntry}>>\nstartxref\n${xrefOffset}\n%%EOF\n`;
52
57
  push(encoder.encode(trailer));
53
58
  const out = new Uint8Array(pos);
54
59
  let off = 0;
@@ -1,8 +1,8 @@
1
1
  import { twipsToPt } from "../core/ir/units.js";
2
- import { poAttr, poChildren, poFindByPath, poIntAttr, poIs, poText } from "../core/po-helpers.js";
2
+ import { poAttr, poChildren, poFindByPath, poFindDescendant, poIntAttr, poIs, poText } from "../core/po-helpers.js";
3
3
  import { defaultColorResolver } from "../core/drawingml/colors.js";
4
4
  import { parseOMath } from "./omml-parser.js";
5
- import { expandMcChildren, parseDrawing } from "./drawing-parser.js";
5
+ import { expandMcChildren, parseDrawing, parseVmlPicture } from "./drawing-parser.js";
6
6
  import { parseRunProperties } from "./run-properties.js";
7
7
  import { parseParagraphProperties } from "./paragraph-properties.js";
8
8
  import { poElementToFlat } from "./po-to-flat.js";
@@ -172,22 +172,50 @@ function parseBodyElements(children, ctx = DEFAULT_PARSE_CONTEXT) {
172
172
  kind: "table",
173
173
  table: parseTable(child, ctx)
174
174
  });
175
+ else if (poIs(child, "w:sdt")) {
176
+ const content = poChildren(child).find((c) => poIs(c, "w:sdtContent"));
177
+ if (content) out.push(...parseBodyElements(poChildren(content), ctx));
178
+ }
175
179
  return out;
176
180
  }
177
- function tryExtractDrawingFromParagraph(p, ctx) {
178
- let drawing;
179
- let hasText = false;
180
- for (const child of poChildren(p)) {
181
- if (!poIs(child, "w:r")) continue;
182
- for (const runChild of expandMcChildren(poChildren(child))) if (poIs(runChild, "w:drawing")) {
183
- if (!drawing) drawing = runChild;
184
- } else if (poIs(runChild, "w:t") && poText(runChild).length > 0) hasText = true;
185
- else if (poIs(runChild, "w:tab") || poIs(runChild, "w:br") || poIs(runChild, "w:noBreakHyphen")) hasText = true;
181
+ var COLLAPSE_TRANSPARENT_TAGS = new Set([
182
+ "w:ins",
183
+ "w:moveTo",
184
+ "w:sdt",
185
+ "w:sdtContent",
186
+ "w:smartTag"
187
+ ]);
188
+ function scanForLoneDrawing(container, acc) {
189
+ for (const child of expandMcChildren(poChildren(container))) {
190
+ if (poIs(child, "w:pPr")) continue;
191
+ if (poIs(child, "w:r")) {
192
+ for (const rc of expandMcChildren(poChildren(child))) if (poIs(rc, "w:drawing")) {
193
+ if (!acc.drawing) acc.drawing = rc;
194
+ } else if (poIs(rc, "w:pict") || poIs(rc, "w:object")) {
195
+ if (!acc.vml && poFindDescendant(rc, "v:imagedata") !== void 0) acc.vml = rc;
196
+ } else if (poIs(rc, "w:t") && poText(rc).length > 0) acc.hasOther = true;
197
+ else if (poIs(rc, "w:tab") || poIs(rc, "w:br") || poIs(rc, "w:noBreakHyphen")) acc.hasOther = true;
198
+ continue;
199
+ }
200
+ if (poIs(child, "m:oMath") || poIs(child, "m:oMathPara")) {
201
+ acc.hasOther = true;
202
+ continue;
203
+ }
204
+ const tag = elementTag(child);
205
+ if (tag && COLLAPSE_TRANSPARENT_TAGS.has(tag)) scanForLoneDrawing(child, acc);
206
+ else if (tag && RUN_CONTAINER_TAGS.has(tag)) acc.hasOther = true;
186
207
  }
187
- if (!drawing || hasText) return null;
208
+ }
209
+ function tryExtractDrawingFromParagraph(p, ctx) {
210
+ const scan = { hasOther: false };
211
+ scanForLoneDrawing(p, scan);
212
+ const { drawing, vml } = scan;
213
+ if (scan.hasOther || !drawing && !vml) return null;
214
+ const fromVml = drawing === void 0;
188
215
  const parseBody = (children) => parseBodyElements(children, ctx);
189
- const content = parseDrawing(drawing, ctx.resolveColor, parseBody);
216
+ const content = fromVml ? parseVmlPicture(vml) : parseDrawing(drawing, ctx.resolveColor, parseBody);
190
217
  if (!content) return null;
218
+ if (fromVml && content.kind === "image" && ctx.resolveImage?.(content.imageId) === void 0) return null;
191
219
  const pPrNode = poChildren(p).find((c) => poIs(c, "w:pPr"));
192
220
  const paragraphProperties = pPrNode ? parseParagraphProperties(poElementToFlat(pPrNode)) : {};
193
221
  if (content.kind === "image") {
@@ -431,6 +459,16 @@ function parseRun(r, ctx) {
431
459
  height: content.height
432
460
  };
433
461
  }
462
+ } else if (poIs(child, "w:pict") || poIs(child, "w:object")) {
463
+ const content = parseVmlPicture(child);
464
+ if (content && content.kind === "image") {
465
+ const resource = ctx.resolveImage?.(content.imageId);
466
+ if (resource) inlineImage = {
467
+ resource,
468
+ width: content.width,
469
+ height: content.height
470
+ };
471
+ }
434
472
  }
435
473
  }
436
474
  return {
@@ -6,8 +6,9 @@ import { resolveTableStyles } from "../core/style-cascade/table.js";
6
6
  import "../core/style-cascade/index.js";
7
7
  import { bytesInclude } from "../core/bytes.js";
8
8
  import { DEFAULT_THEME_PALETTE, makeColorResolver } from "../core/drawingml/colors.js";
9
- import { parseChart } from "../core/drawingml/chart-parser.js";
9
+ import { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
10
10
  import { parseTheme } from "../core/drawingml/theme-parser.js";
11
+ import { isOoxmlRel } from "../core/opc/relationship-types.js";
11
12
  import { OpcPackage } from "../core/opc/package.js";
12
13
  import { parseCoreProperties } from "../core/opc/core-properties.js";
13
14
  import "../core/opc/index.js";
@@ -25,12 +26,7 @@ var NUMBERING_PART = "word/numbering.xml";
25
26
  var SETTINGS_PART = "word/settings.xml";
26
27
  var CORE_PROPS_PART = "docProps/core.xml";
27
28
  var MAIN_DOCUMENT_PART = "word/document.xml";
28
- var REL_IMAGE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
29
29
  var REL_HYPERLINK = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
30
- var REL_CHART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart";
31
- var REL_THEME = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
32
- var REL_HEADER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header";
33
- var REL_FOOTER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer";
34
30
  var THEME_PART = "word/theme/theme1.xml";
35
31
  function readDocx(docx) {
36
32
  const pkg = OpcPackage.open(docx);
@@ -132,7 +128,7 @@ function infoFromCore(core) {
132
128
  function makeImageResolver(pkg, store, partName = MAIN_DOCUMENT_PART) {
133
129
  const byRelId = /* @__PURE__ */ new Map();
134
130
  for (const rel of pkg.getPartRelationships(partName)) {
135
- if (rel.type !== REL_IMAGE) continue;
131
+ if (!isOoxmlRel(rel.type, "image")) continue;
136
132
  const resolved = pkg.resolveRelatedPart(partName, rel);
137
133
  if (resolved) byRelId.set(rel.id, resolved.data);
138
134
  }
@@ -157,11 +153,11 @@ function makeHyperlinkResolver(pkg, partName = MAIN_DOCUMENT_PART) {
157
153
  function loadCharts(pkg, resolveColor) {
158
154
  const out = /* @__PURE__ */ new Map();
159
155
  for (const rel of pkg.getPartRelationships(MAIN_DOCUMENT_PART)) {
160
- if (rel.type !== REL_CHART) continue;
156
+ if (!isOoxmlRel(rel.type, "chart")) continue;
161
157
  const resolved = pkg.resolveRelatedPart(MAIN_DOCUMENT_PART, rel);
162
158
  if (!resolved) continue;
163
159
  const chart = parseChart(resolved.data, resolveColor);
164
- if (chart) out.set(rel.id, chart);
160
+ if (chart) out.set(rel.id, withChartColorStyle(chart, pkg, resolved.path, resolveColor));
165
161
  }
166
162
  return out;
167
163
  }
@@ -174,7 +170,7 @@ function buildColorResolver(pkg) {
174
170
  }
175
171
  function loadTheme(pkg) {
176
172
  for (const rel of pkg.getPartRelationships(MAIN_DOCUMENT_PART)) {
177
- if (rel.type !== REL_THEME) continue;
173
+ if (!isOoxmlRel(rel.type, "theme")) continue;
178
174
  const resolved = pkg.resolveRelatedPart(MAIN_DOCUMENT_PART, rel);
179
175
  if (resolved) return resolved.data;
180
176
  }
@@ -192,7 +188,7 @@ function loadHeadersFootersForSections(pkg, sections, ctx, store) {
192
188
  const out = /* @__PURE__ */ new Map();
193
189
  for (const rel of rels) {
194
190
  if (!wanted.has(rel.id)) continue;
195
- if (rel.type !== REL_HEADER && rel.type !== REL_FOOTER) continue;
191
+ if (!isOoxmlRel(rel.type, "header") && !isOoxmlRel(rel.type, "footer")) continue;
196
192
  const resolved = pkg.resolveRelatedPart(MAIN_DOCUMENT_PART, rel);
197
193
  if (!resolved) continue;
198
194
  const hfCtx = {
@@ -1,7 +1,7 @@
1
1
  import { fetchFontSet, resolveFamilyKey } from "../core/fonts/remote-fonts.js";
2
2
  import { FontRegistry } from "../core/font/font-registry.js";
3
3
  import { signPdf } from "../pdf/signature.js";
4
- import { renderStyledPdf } from "../pdf/styled-page-renderer.js";
4
+ import { renderStyledPdf, renderStyledPdfEncrypted } from "../pdf/styled-page-renderer.js";
5
5
  import "../pdf/index.js";
6
6
  import { OpcPackage } from "../core/opc/package.js";
7
7
  import "../core/opc/index.js";
@@ -16,6 +16,10 @@ var MAIN_DOCUMENT_PART = "word/document.xml";
16
16
  * Internal since 1.0 — see the async variant above.
17
17
  */
18
18
  function convertDocxToPdfSync(docx, options) {
19
+ const args = prepareDocxStyledRender(docx, options);
20
+ return renderStyledPdf(args.body, args.styled);
21
+ }
22
+ function prepareDocxStyledRender(docx, options) {
19
23
  const fonts = options.fonts ?? (options.fontBytes ? { regular: options.fontBytes } : void 0);
20
24
  if (!fonts) throw new Error("convertDocxToPdfSync requires options.fonts or options.fontBytes");
21
25
  const registry = FontRegistry.fromBytes(fonts);
@@ -33,13 +37,16 @@ function convertDocxToPdfSync(docx, options) {
33
37
  relationship: "Source",
34
38
  description: "Source Word document"
35
39
  });
36
- return renderStyledPdf(flow.body, {
37
- registry,
38
- ...flowRenderOptions(flow),
39
- ...info ? { info } : {},
40
- ...attachments.length > 0 ? { attachments } : {},
41
- ...renderOptions
42
- });
40
+ return {
41
+ body: flow.body,
42
+ styled: {
43
+ registry,
44
+ ...flowRenderOptions(flow),
45
+ ...info ? { info } : {},
46
+ ...attachments.length > 0 ? { attachments } : {},
47
+ ...renderOptions
48
+ }
49
+ };
43
50
  }
44
51
  function detectDocxFamilyKeys(docx) {
45
52
  const keys = new Set(["roboto"]);
@@ -76,12 +83,15 @@ async function convertDocxToPdf(docx, options = {}) {
76
83
  return signature ? signPdf(pdf, signature) : pdf;
77
84
  }
78
85
  async function buildUnsignedDocxPdf(docx, options) {
79
- if (options.fonts ?? options.fontBytes) return convertDocxToPdfSync(docx, options);
80
- const auto = await resolveDocxAutoFonts(docx, options);
81
- return convertDocxToPdfSync(docx, {
86
+ const withFonts = options.fonts ?? options.fontBytes ? options : {
82
87
  ...options,
83
- ...auto
84
- });
88
+ ...await resolveDocxAutoFonts(docx, options)
89
+ };
90
+ if (withFonts.encrypt) {
91
+ const args = prepareDocxStyledRender(docx, withFonts);
92
+ return renderStyledPdfEncrypted(args.body, args.styled);
93
+ }
94
+ return convertDocxToPdfSync(docx, withFonts);
85
95
  }
86
96
  async function resolveDocxAutoFonts(docx, options = {}) {
87
97
  const fetchOpt = options.fontFetch ? { fetch: options.fontFetch } : {};
@@ -0,0 +1,4 @@
1
+ import { DocumentWriter, WriteResult } from '../core/ir/adapters.js';
2
+ import { FlowDoc } from '../core/ir/flow.js';
3
+ export declare function writeDocx(flow: FlowDoc): WriteResult;
4
+ export declare const docxWriter: DocumentWriter<FlowDoc>;