reamkit 1.1.0 → 1.3.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 (68) hide show
  1. package/README.md +11 -5
  2. package/dist/esm/{layout → core}/arc-to-bezier.d.ts +1 -1
  3. package/dist/esm/{layout → core}/arc-to-bezier.js +2 -2
  4. package/dist/esm/core/converter/project.js +2 -0
  5. package/dist/esm/core/converter/ream.js +4 -3
  6. package/dist/esm/core/document-model/index.d.ts +1 -1
  7. package/dist/esm/core/document-model/types.d.ts +60 -0
  8. package/dist/esm/{layout → core/drawingml}/chart-geometry.d.ts +2 -2
  9. package/dist/esm/{layout → core/drawingml}/chart-geometry.js +79 -117
  10. package/dist/esm/core/drawingml/chart-parser.d.ts +3 -0
  11. package/dist/esm/core/drawingml/chart-parser.js +28 -1
  12. package/dist/esm/{layout → core/drawingml}/preset-geometry.d.ts +2 -2
  13. package/dist/esm/{layout → core/drawingml}/preset-geometry.js +3 -3
  14. package/dist/esm/core/drawingml/shape-render.d.ts +8 -0
  15. package/dist/esm/core/drawingml/shape-render.js +71 -0
  16. package/dist/esm/core/ir/features.d.ts +1 -0
  17. package/dist/esm/core/ir/features.js +1 -0
  18. package/dist/esm/core/ir/flow.d.ts +3 -0
  19. package/dist/esm/core/links.d.ts +6 -0
  20. package/dist/esm/core/links.js +19 -0
  21. package/dist/esm/core/numbering/apply.js +5 -2
  22. package/dist/esm/core/opc/index.d.ts +1 -0
  23. package/dist/esm/core/opc/package.js +3 -2
  24. package/dist/esm/core/opc/relationship-types.d.ts +1 -0
  25. package/dist/esm/core/opc/relationship-types.js +5 -2
  26. package/dist/esm/core/style-cascade/index.d.ts +1 -0
  27. package/dist/esm/core/style-cascade/index.js +1 -0
  28. package/dist/esm/core/style-cascade/table.d.ts +2 -0
  29. package/dist/esm/core/style-cascade/table.js +161 -0
  30. package/dist/esm/core/vector.d.ts +2 -0
  31. package/dist/esm/core/vector.js +19 -1
  32. package/dist/esm/excel/print-model.d.ts +3 -0
  33. package/dist/esm/excel/print-model.js +4 -8
  34. package/dist/esm/excel/sheet-drawing.d.ts +9 -0
  35. package/dist/esm/excel/sheet-drawing.js +113 -0
  36. package/dist/esm/excel/worksheet-parser.d.ts +1 -0
  37. package/dist/esm/excel/worksheet-parser.js +4 -1
  38. package/dist/esm/excel/xlsx-reader.js +41 -0
  39. package/dist/esm/excel/xlsx-to-pdf.js +25 -18
  40. package/dist/esm/html/html-writer.js +188 -16
  41. package/dist/esm/index.d.ts +1 -0
  42. package/dist/esm/layout/page-doc.d.ts +4 -0
  43. package/dist/esm/layout/styled-layout.d.ts +21 -3
  44. package/dist/esm/layout/styled-layout.js +745 -259
  45. package/dist/esm/pdf/encryption.d.ts +34 -0
  46. package/dist/esm/pdf/encryption.js +143 -0
  47. package/dist/esm/pdf/index.d.ts +2 -1
  48. package/dist/esm/pdf/struct-tree.d.ts +7 -1
  49. package/dist/esm/pdf/struct-tree.js +22 -6
  50. package/dist/esm/pdf/styled-page-emitter.d.ts +3 -1
  51. package/dist/esm/pdf/styled-page-emitter.js +185 -35
  52. package/dist/esm/pdf/styled-page-renderer.d.ts +1 -0
  53. package/dist/esm/pdf/styled-page-renderer.js +17 -2
  54. package/dist/esm/pdf/writer.d.ts +2 -0
  55. package/dist/esm/pdf/writer.js +6 -1
  56. package/dist/esm/pdf/xmp.d.ts +1 -0
  57. package/dist/esm/pdf/xmp.js +4 -0
  58. package/dist/esm/svg/svg-writer.js +2 -6
  59. package/dist/esm/word/document-parser.d.ts +3 -0
  60. package/dist/esm/word/document-parser.js +214 -23
  61. package/dist/esm/word/docx-reader.js +37 -15
  62. package/dist/esm/word/docx-to-pdf.js +23 -13
  63. package/dist/esm/word/drawing-parser.d.ts +4 -1
  64. package/dist/esm/word/drawing-parser.js +53 -2
  65. package/dist/esm/word/index.d.ts +2 -2
  66. package/dist/esm/word/styles-parser.js +134 -1
  67. package/dist/esm/word/table-parser.js +35 -0
  68. package/package.json +1 -1
@@ -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;
@@ -8,6 +8,7 @@ export interface XmpInput {
8
8
  readonly createDate?: Date;
9
9
  readonly modifyDate?: Date;
10
10
  readonly pdfaPart?: '1' | '2' | '3';
11
+ readonly pdfuaPart?: '1';
11
12
  readonly pdfaConformance?: 'A' | 'B' | 'U';
12
13
  }
13
14
  export declare function buildXmpPacket(input: XmpInput): Uint8Array;
@@ -9,6 +9,10 @@ function xmpDate(d) {
9
9
  function buildXmpPacket(input) {
10
10
  const props = [];
11
11
  if (input.pdfaPart) props.push(` <rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">`, ` <pdfaid:part>${input.pdfaPart}</pdfaid:part>`, ` <pdfaid:conformance>${input.pdfaConformance ?? "B"}</pdfaid:conformance>`, ` </rdf:Description>`);
12
+ if (input.pdfuaPart) {
13
+ props.push(` <rdf:Description rdf:about="" xmlns:pdfuaid="http://www.aiim.org/pdfua/ns/id/">`, ` <pdfuaid:part>${input.pdfuaPart}</pdfuaid:part>`, ` </rdf:Description>`);
14
+ if (input.pdfaPart) props.push(` <rdf:Description rdf:about="" xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/" xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#" xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">`, ` <pdfaExtension:schemas>`, ` <rdf:Bag>`, ` <rdf:li rdf:parseType="Resource">`, ` <pdfaSchema:namespaceURI>http://www.aiim.org/pdfua/ns/id/</pdfaSchema:namespaceURI>`, ` <pdfaSchema:prefix>pdfuaid</pdfaSchema:prefix>`, ` <pdfaSchema:schema>PDF/UA identification schema</pdfaSchema:schema>`, ` <pdfaSchema:property>`, ` <rdf:Seq>`, ` <rdf:li rdf:parseType="Resource">`, ` <pdfaProperty:category>internal</pdfaProperty:category>`, ` <pdfaProperty:description>PDF/UA version identifier</pdfaProperty:description>`, ` <pdfaProperty:name>part</pdfaProperty:name>`, ` <pdfaProperty:valueType>Integer</pdfaProperty:valueType>`, ` </rdf:li>`, ` </rdf:Seq>`, ` </pdfaSchema:property>`, ` </rdf:li>`, ` </rdf:Bag>`, ` </pdfaExtension:schemas>`, ` </rdf:Description>`);
15
+ }
12
16
  const dc = [];
13
17
  if (input.title) dc.push(` <dc:title><rdf:Alt><rdf:li xml:lang="x-default">${escapeXml(input.title)}</rdf:li></rdf:Alt></dc:title>`);
14
18
  if (input.author) dc.push(` <dc:creator><rdf:Seq><rdf:li>${escapeXml(input.author)}</rdf:li></rdf:Seq></dc:creator>`);
@@ -1,4 +1,5 @@
1
1
  import { FEATURES } from "../core/ir/features.js";
2
+ import { svgPathData } from "../core/vector.js";
2
3
  import { paintPlan } from "../layout/page-doc.js";
3
4
  import { toBase64 } from "../core/bytes.js";
4
5
  //#region src/svg/svg-writer.ts
@@ -106,12 +107,7 @@ function emitShape(out, shape) {
106
107
  }
107
108
  }
108
109
  function pathData(segments) {
109
- const parts = [];
110
- for (const s of segments) if (s.op === "move") parts.push(`M ${fmt(s.x)} ${fmt(s.y)}`);
111
- else if (s.op === "line") parts.push(`L ${fmt(s.x)} ${fmt(s.y)}`);
112
- else if (s.op === "cubic") parts.push(`C ${fmt(s.x1)} ${fmt(s.y1)} ${fmt(s.x2)} ${fmt(s.y2)} ${fmt(s.x)} ${fmt(s.y)}`);
113
- else parts.push("Z");
114
- return parts.join(" ");
110
+ return svgPathData(segments, fmt);
115
111
  }
116
112
  function imageHref(resourceName, laid) {
117
113
  if (!resourceName) return void 0;
@@ -3,9 +3,11 @@ import { ColorResolver } from '../core/drawingml/colors.js';
3
3
  import { ResourceId } from '../core/ir/index.js';
4
4
  import { PoNode } from '../core/po-helpers.js';
5
5
  export type ImageResolver = (relId: string) => ResourceId | undefined;
6
+ export type HyperlinkResolver = (relId: string) => string | undefined;
6
7
  export interface ParseContext {
7
8
  readonly resolveColor: ColorResolver;
8
9
  readonly resolveImage?: ImageResolver;
10
+ readonly resolveHyperlink?: HyperlinkResolver;
9
11
  }
10
12
  export declare const DEFAULT_PARSE_CONTEXT: ParseContext;
11
13
  export declare function parseDocument(documentXml: Uint8Array, ctx?: ParseContext): Array<BodyElement>;
@@ -14,3 +16,4 @@ export declare function parseSection(documentXml: Uint8Array): SectionProperties
14
16
  export declare function parseSections(documentXml: Uint8Array): Array<Section>;
15
17
  export declare function parseHeaderFooter(xml: Uint8Array, ctx?: ParseContext): Array<BodyElement>;
16
18
  export declare function parseBodyElements(children: ReadonlyArray<PoNode>, ctx?: ParseContext): Array<BodyElement>;
19
+ export declare function parseNotes(notesXml: Uint8Array, rootTag: 'w:footnotes' | 'w:endnotes', noteTag: 'w:footnote' | 'w:endnote', ctx?: ParseContext): Map<string, Array<BodyElement>>;
@@ -78,6 +78,7 @@ function parseSectPrNode(sectPr) {
78
78
  let pageSize;
79
79
  let margins;
80
80
  let titlePg = false;
81
+ let columns;
81
82
  const headers = [];
82
83
  const footers = [];
83
84
  for (const child of poChildren(sectPr)) if (poIs(child, "w:pgSz")) {
@@ -109,13 +110,34 @@ function parseSectPrNode(sectPr) {
109
110
  else if (poIs(child, "w:titlePg")) {
110
111
  const val = poAttr(child, "val");
111
112
  titlePg = val === void 0 || val === "" || val !== "0" && val !== "false";
112
- }
113
+ } else if (poIs(child, "w:cols")) columns = parseColumns(child);
113
114
  return {
114
115
  ...pageSize ? { pageSize } : {},
115
116
  ...margins ? { margins } : {},
116
117
  headers,
117
118
  footers,
118
- ...titlePg ? { titlePg: true } : {}
119
+ ...titlePg ? { titlePg: true } : {},
120
+ ...columns ? { columns } : {}
121
+ };
122
+ }
123
+ function parseColumns(cols) {
124
+ const explicit = [];
125
+ for (const col of poChildren(cols)) {
126
+ if (!poIs(col, "w:col")) continue;
127
+ const w = poIntAttr(col, "w");
128
+ if (w === void 0) continue;
129
+ explicit.push({
130
+ widthPt: twipsToPt(w),
131
+ spacePt: twipsToPt(poIntAttr(col, "space") ?? 0)
132
+ });
133
+ }
134
+ const num = poIntAttr(cols, "num");
135
+ const count = explicit.length > 0 ? explicit.length : num ?? 1;
136
+ if (count <= 1) return void 0;
137
+ return {
138
+ count,
139
+ spacePt: twipsToPt(poIntAttr(cols, "space") ?? 720),
140
+ ...explicit.length > 0 ? { explicit } : {}
119
141
  };
120
142
  }
121
143
  function pushHeaderFooter(node, list) {
@@ -135,16 +157,25 @@ function parseHeaderFooter(xml, ctx = DEFAULT_PARSE_CONTEXT) {
135
157
  }
136
158
  function parseBodyElements(children, ctx = DEFAULT_PARSE_CONTEXT) {
137
159
  const out = [];
138
- for (const child of children) if (poIs(child, "w:p")) {
160
+ let pendingBookmarks;
161
+ for (const child of children) if (poIs(child, "w:bookmarkStart")) {
162
+ const bookmarkName = poAttr(child, "name");
163
+ if (bookmarkName !== void 0 && bookmarkName !== "" && bookmarkName !== "_GoBack") (pendingBookmarks ??= []).push(bookmarkName);
164
+ } else if (poIs(child, "w:p")) {
139
165
  const drawing = tryExtractDrawingFromParagraph(child, ctx);
140
166
  out.push(drawing ?? {
141
167
  kind: "paragraph",
142
- paragraph: parseParagraph(child, ctx)
168
+ paragraph: parseParagraph(child, ctx, pendingBookmarks)
143
169
  });
170
+ if (!drawing) pendingBookmarks = void 0;
144
171
  } else if (poIs(child, "w:tbl")) out.push({
145
172
  kind: "table",
146
173
  table: parseTable(child, ctx)
147
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
+ }
148
179
  return out;
149
180
  }
150
181
  function tryExtractDrawingFromParagraph(p, ctx) {
@@ -172,7 +203,8 @@ function tryExtractDrawingFromParagraph(p, ctx) {
172
203
  width: content.width,
173
204
  height: content.height,
174
205
  paragraphProperties,
175
- ...content.altText ? { altText: content.altText } : {}
206
+ ...content.altText ? { altText: content.altText } : {},
207
+ ...content.float ? { float: content.float } : {}
176
208
  }
177
209
  };
178
210
  }
@@ -183,7 +215,8 @@ function tryExtractDrawingFromParagraph(p, ctx) {
183
215
  width: content.width,
184
216
  height: content.height,
185
217
  paragraphProperties,
186
- ...content.altText ? { altText: content.altText } : {}
218
+ ...content.altText ? { altText: content.altText } : {},
219
+ ...content.float ? { float: content.float } : {}
187
220
  }
188
221
  };
189
222
  return {
@@ -191,11 +224,18 @@ function tryExtractDrawingFromParagraph(p, ctx) {
191
224
  shape: {
192
225
  ...content.data,
193
226
  paragraphProperties,
194
- ...content.altText ? { altText: content.altText } : {}
227
+ ...content.altText ? { altText: content.altText } : {},
228
+ ...content.float ? { float: content.float } : {}
195
229
  }
196
230
  };
197
231
  }
198
- function parseParagraph(p, ctx) {
232
+ function parseParagraph(p, ctx, extraBookmarks) {
233
+ const bookmarks = [...extraBookmarks ?? []];
234
+ for (const child of poChildren(p)) {
235
+ if (!poIs(child, "w:bookmarkStart")) continue;
236
+ const bookmarkName = poAttr(child, "name");
237
+ if (bookmarkName !== void 0 && bookmarkName !== "" && bookmarkName !== "_GoBack") bookmarks.push(bookmarkName);
238
+ }
199
239
  const pPr = poChildren(p).find((c) => poIs(c, "w:pPr"));
200
240
  let properties = parseParagraphProperties(pPr ? poElementToFlat(pPr) : void 0);
201
241
  const mathPara = poChildren(p).find((c) => poIs(c, "m:oMathPara"));
@@ -208,38 +248,139 @@ function parseParagraph(p, ctx) {
208
248
  alignment
209
249
  };
210
250
  }
211
- const runs = [];
212
- collectRuns(p, runs, ctx);
251
+ const collected = [];
252
+ collectRuns(p, collected, ctx);
213
253
  return {
214
254
  properties,
215
- runs
255
+ runs: applyFieldFsm(collected),
256
+ ...bookmarks.length > 0 ? { bookmarks } : {}
216
257
  };
217
258
  }
218
- function collectRuns(container, out, ctx) {
259
+ function parseFieldInstr(instr) {
260
+ if (!instr) return void 0;
261
+ const kw = /^\s*([A-Za-z]+)/.exec(instr)?.[1]?.toUpperCase();
262
+ return kw === "PAGE" ? "PAGE" : kw === "NUMPAGES" ? "NUMPAGES" : void 0;
263
+ }
264
+ function synthesizeFieldRun(result, field, href) {
265
+ const first = result[0];
266
+ const linked = href ?? result.find((r) => r.href !== void 0)?.href;
267
+ return {
268
+ text: result.map((r) => r.text).join(""),
269
+ properties: first?.properties ?? {},
270
+ field,
271
+ ...linked !== void 0 ? { href: linked } : {}
272
+ };
273
+ }
274
+ function applyFieldFsm(collected) {
275
+ const out = [];
276
+ let st = null;
277
+ for (const c of collected) {
278
+ if (c.fldChar === "begin") {
279
+ if (st) if (st.phase === "instr") st.depth++;
280
+ else {
281
+ out.push(...st.result);
282
+ st = {
283
+ phase: "instr",
284
+ instr: "",
285
+ result: [],
286
+ depth: 0
287
+ };
288
+ }
289
+ else st = {
290
+ phase: "instr",
291
+ instr: "",
292
+ result: [],
293
+ depth: 0
294
+ };
295
+ continue;
296
+ }
297
+ if (!st) {
298
+ out.push(c.run);
299
+ continue;
300
+ }
301
+ if (c.fldChar === "separate") {
302
+ if (st.depth === 0) st.phase = "result";
303
+ continue;
304
+ }
305
+ if (c.fldChar === "end") {
306
+ if (st.depth > 0) {
307
+ st.depth--;
308
+ continue;
309
+ }
310
+ const field = parseFieldInstr(st.instr);
311
+ if (field) out.push(synthesizeFieldRun(st.result, field));
312
+ else out.push(...st.result);
313
+ st = null;
314
+ continue;
315
+ }
316
+ if (st.phase === "instr") {
317
+ if (c.instrText !== void 0) st.instr += c.instrText;
318
+ continue;
319
+ }
320
+ st.result.push(c.run);
321
+ }
322
+ if (st) out.push(...st.result);
323
+ return out;
324
+ }
325
+ function collectRuns(container, out, ctx, href, anchor) {
219
326
  for (const child of poChildren(container)) {
220
327
  if (poIs(child, "w:pPr")) continue;
221
328
  if (poIs(child, "w:r")) {
222
- out.push(parseRun(child, ctx));
329
+ const parsed = parseRun(child, ctx);
330
+ const run = href !== void 0 || anchor !== void 0 ? {
331
+ ...parsed.run,
332
+ ...href !== void 0 ? { href } : {},
333
+ ...anchor !== void 0 ? { anchor } : {}
334
+ } : parsed.run;
335
+ out.push({
336
+ run,
337
+ ...parsed.fldChar ? { fldChar: parsed.fldChar } : {},
338
+ ...parsed.instrText !== void 0 ? { instrText: parsed.instrText } : {}
339
+ });
223
340
  continue;
224
341
  }
225
342
  if (poIs(child, "m:oMath")) {
226
- out.push({
343
+ out.push({ run: {
227
344
  text: "",
228
345
  properties: {},
229
346
  math: parseOMath(child)
230
- });
347
+ } });
231
348
  continue;
232
349
  }
233
350
  if (poIs(child, "m:oMathPara")) {
234
- for (const om of poChildren(child)) if (poIs(om, "m:oMath")) out.push({
351
+ for (const om of poChildren(child)) if (poIs(om, "m:oMath")) out.push({ run: {
235
352
  text: "",
236
353
  properties: {},
237
354
  math: parseOMath(om)
238
- });
355
+ } });
239
356
  continue;
240
357
  }
241
358
  const tag = elementTag(child);
242
- if (tag && RUN_CONTAINER_TAGS.has(tag)) collectRuns(child, out, ctx);
359
+ if (tag === "w:fldSimple") {
360
+ const field = parseFieldInstr(poAttr(child, "instr"));
361
+ if (field) {
362
+ const inner = [];
363
+ collectRuns(child, inner, ctx, href, anchor);
364
+ out.push({ run: synthesizeFieldRun(applyFieldFsm(inner), field, href) });
365
+ continue;
366
+ }
367
+ collectRuns(child, out, ctx, href, anchor);
368
+ continue;
369
+ }
370
+ if (tag && RUN_CONTAINER_TAGS.has(tag)) {
371
+ let childHref = href;
372
+ let childAnchor = anchor;
373
+ if (tag === "w:hyperlink") {
374
+ const rId = poAttr(child, "id");
375
+ const resolved = rId ? ctx.resolveHyperlink?.(rId) : void 0;
376
+ if (resolved !== void 0) childHref = resolved;
377
+ else {
378
+ const bookmark = poAttr(child, "anchor");
379
+ if (bookmark !== void 0 && bookmark !== "") childAnchor = bookmark;
380
+ }
381
+ }
382
+ collectRuns(child, out, ctx, childHref, childAnchor);
383
+ }
243
384
  }
244
385
  }
245
386
  function parseRun(r, ctx) {
@@ -248,8 +389,36 @@ function parseRun(r, ctx) {
248
389
  let text = "";
249
390
  let pageBreak = false;
250
391
  let inlineImage;
392
+ let fldChar;
393
+ let instrText;
394
+ let footnoteRef;
395
+ let endnoteRef;
396
+ let noteNumber = false;
251
397
  for (const child of expandMcChildren(poChildren(r))) {
252
398
  if (poIs(child, "w:rPr")) continue;
399
+ if (poIs(child, "w:fldChar")) {
400
+ const t = poAttr(child, "fldCharType");
401
+ if (t === "begin" || t === "separate" || t === "end") fldChar = t;
402
+ continue;
403
+ }
404
+ if (poIs(child, "w:instrText")) {
405
+ instrText = (instrText ?? "") + poText(child);
406
+ continue;
407
+ }
408
+ if (poIs(child, "w:footnoteReference")) {
409
+ const id = poAttr(child, "id");
410
+ if (id !== void 0) footnoteRef = id;
411
+ continue;
412
+ }
413
+ if (poIs(child, "w:endnoteReference")) {
414
+ const id = poAttr(child, "id");
415
+ if (id !== void 0) endnoteRef = id;
416
+ continue;
417
+ }
418
+ if (poIs(child, "w:footnoteRef") || poIs(child, "w:endnoteRef")) {
419
+ noteNumber = true;
420
+ continue;
421
+ }
253
422
  if (poIs(child, "w:t")) text += poText(child);
254
423
  else if (poIs(child, "w:tab")) text += " ";
255
424
  else if (poIs(child, "w:br")) if (poAttr(child, "type") === "page") pageBreak = true;
@@ -269,14 +438,36 @@ function parseRun(r, ctx) {
269
438
  }
270
439
  }
271
440
  return {
272
- text,
273
- properties,
274
- ...inlineImage ? { inlineImage } : {},
275
- ...pageBreak ? { pageBreak: true } : {}
441
+ run: {
442
+ text,
443
+ properties,
444
+ ...inlineImage ? { inlineImage } : {},
445
+ ...pageBreak ? { pageBreak: true } : {},
446
+ ...footnoteRef !== void 0 ? { footnoteRef } : {},
447
+ ...endnoteRef !== void 0 ? { endnoteRef } : {},
448
+ ...noteNumber ? { noteNumber: true } : {}
449
+ },
450
+ ...fldChar ? { fldChar } : {},
451
+ ...instrText !== void 0 ? { instrText } : {}
276
452
  };
277
453
  }
278
454
  function elementTag(node) {
279
455
  for (const key of Object.keys(node)) if (key !== ":@" && key !== "#text") return key;
280
456
  }
457
+ function parseNotes(notesXml, rootTag, noteTag, ctx = DEFAULT_PARSE_CONTEXT) {
458
+ const xml = decoder.decode(notesXml);
459
+ const root = poFindByPath(parser.parse(xml), [rootTag]);
460
+ const out = /* @__PURE__ */ new Map();
461
+ if (!root) return out;
462
+ for (const note of poChildren(root)) {
463
+ if (!poIs(note, noteTag)) continue;
464
+ const type = poAttr(note, "type");
465
+ if (type !== void 0 && type !== "normal") continue;
466
+ const id = poAttr(note, "id");
467
+ if (id === void 0) continue;
468
+ out.set(id, parseBodyElements(poChildren(note), ctx));
469
+ }
470
+ return out;
471
+ }
281
472
  //#endregion
282
- export { DEFAULT_PARSE_CONTEXT, EMPTY_SECTION, parseBodyElements, parseDocument, parseHeaderFooter, parseSections };
473
+ export { DEFAULT_PARSE_CONTEXT, EMPTY_SECTION, parseBodyElements, parseDocument, parseHeaderFooter, parseNotes, parseSections };
@@ -2,15 +2,17 @@ import { ResourceStore } from "../core/ir/resources.js";
2
2
  import { FEATURES } from "../core/ir/features.js";
3
3
  import { applyNumbering, applyNumberingToHeadersFooters } from "../core/numbering/apply.js";
4
4
  import { EMPTY_STYLE_SHEET, resolveBodyStyles, resolveHeadersFootersStyles } from "../core/style-cascade/resolver.js";
5
+ import { resolveTableStyles } from "../core/style-cascade/table.js";
5
6
  import "../core/style-cascade/index.js";
6
7
  import { bytesInclude } from "../core/bytes.js";
7
8
  import { DEFAULT_THEME_PALETTE, makeColorResolver } from "../core/drawingml/colors.js";
8
- import { parseChart } from "../core/drawingml/chart-parser.js";
9
+ import { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
9
10
  import { parseTheme } from "../core/drawingml/theme-parser.js";
11
+ import { isOoxmlRel } from "../core/opc/relationship-types.js";
10
12
  import { OpcPackage } from "../core/opc/package.js";
11
13
  import { parseCoreProperties } from "../core/opc/core-properties.js";
12
14
  import "../core/opc/index.js";
13
- import { EMPTY_SECTION, parseDocument, parseHeaderFooter, parseSections } from "./document-parser.js";
15
+ import { EMPTY_SECTION, parseDocument, parseHeaderFooter, parseNotes, parseSections } from "./document-parser.js";
14
16
  import { parseStyles } from "./styles-parser.js";
15
17
  import { loadEmbeddedFonts } from "./font-table.js";
16
18
  import { EMPTY_NUMBERING, parseNumbering } from "./numbering-parser.js";
@@ -18,15 +20,13 @@ import { EMPTY_SETTINGS, parseSettings } from "./settings-parser.js";
18
20
  import "./index.js";
19
21
  //#region src/word/docx-reader.ts
20
22
  var STYLES_PART = "word/styles.xml";
23
+ var FOOTNOTES_PART = "word/footnotes.xml";
24
+ var ENDNOTES_PART = "word/endnotes.xml";
21
25
  var NUMBERING_PART = "word/numbering.xml";
22
26
  var SETTINGS_PART = "word/settings.xml";
23
27
  var CORE_PROPS_PART = "docProps/core.xml";
24
28
  var MAIN_DOCUMENT_PART = "word/document.xml";
25
- var REL_IMAGE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
26
- var REL_CHART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart";
27
- var REL_THEME = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
28
- var REL_HEADER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header";
29
- var REL_FOOTER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer";
29
+ var REL_HYPERLINK = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
30
30
  var THEME_PART = "word/theme/theme1.xml";
31
31
  function readDocx(docx) {
32
32
  const pkg = OpcPackage.open(docx);
@@ -35,7 +35,8 @@ function readDocx(docx) {
35
35
  const resources = new ResourceStore();
36
36
  const ctx = {
37
37
  resolveColor,
38
- resolveImage: makeImageResolver(pkg, resources)
38
+ resolveImage: makeImageResolver(pkg, resources),
39
+ resolveHyperlink: makeHyperlinkResolver(pkg)
39
40
  };
40
41
  const body = parseDocument(main.data, ctx);
41
42
  const rawSections = parseSections(main.data);
@@ -43,6 +44,15 @@ function readDocx(docx) {
43
44
  const styles = stylesData ? parseStyles(stylesData) : EMPTY_STYLE_SHEET;
44
45
  const numberingData = pkg.getPart(NUMBERING_PART);
45
46
  const numbering = numberingData ? parseNumbering(numberingData) : EMPTY_NUMBERING;
47
+ const noteCtx = (part) => ({
48
+ resolveColor,
49
+ resolveImage: makeImageResolver(pkg, resources, part),
50
+ resolveHyperlink: makeHyperlinkResolver(pkg, part)
51
+ });
52
+ const footnotesData = pkg.getPart(FOOTNOTES_PART);
53
+ const rawFootnotes = footnotesData ? parseNotes(footnotesData, "w:footnotes", "w:footnote", noteCtx(FOOTNOTES_PART)) : void 0;
54
+ const endnotesData = pkg.getPart(ENDNOTES_PART);
55
+ const rawEndnotes = endnotesData ? parseNotes(endnotesData, "w:endnotes", "w:endnote", noteCtx(ENDNOTES_PART)) : void 0;
46
56
  const settingsData = pkg.getPart(SETTINGS_PART);
47
57
  const settings = settingsData ? parseSettings(settingsData) : EMPTY_SETTINGS;
48
58
  const sections = rawSections.length > 0 ? rawSections.map((s) => ({
@@ -67,10 +77,12 @@ function readDocx(docx) {
67
77
  return {
68
78
  doc: {
69
79
  kind: "flow",
70
- body: resolveBodyStyles(applyNumbering(body, numbering), styles),
80
+ body: resolveBodyStyles(applyNumbering(resolveTableStyles(body, styles), numbering), styles),
71
81
  sections,
72
82
  styles,
73
83
  numbering,
84
+ ...rawFootnotes && rawFootnotes.size > 0 ? { footnotes: transformNotes(rawFootnotes, styles, numbering) } : {},
85
+ ...rawEndnotes && rawEndnotes.size > 0 ? { endnotes: transformNotes(rawEndnotes, styles, numbering) } : {},
74
86
  headersFooters: resolveHeadersFootersStyles(applyNumberingToHeadersFooters(headersFooters, numbering), styles),
75
87
  charts,
76
88
  resources,
@@ -116,7 +128,7 @@ function infoFromCore(core) {
116
128
  function makeImageResolver(pkg, store, partName = MAIN_DOCUMENT_PART) {
117
129
  const byRelId = /* @__PURE__ */ new Map();
118
130
  for (const rel of pkg.getPartRelationships(partName)) {
119
- if (rel.type !== REL_IMAGE) continue;
131
+ if (!isOoxmlRel(rel.type, "image")) continue;
120
132
  const resolved = pkg.resolveRelatedPart(partName, rel);
121
133
  if (resolved) byRelId.set(rel.id, resolved.data);
122
134
  }
@@ -129,14 +141,23 @@ function makeImageResolver(pkg, store, partName = MAIN_DOCUMENT_PART) {
129
141
  return id;
130
142
  };
131
143
  }
144
+ function transformNotes(notes, styles, numbering) {
145
+ for (const content of notes.values()) resolveTableStyles(content, styles);
146
+ return resolveHeadersFootersStyles(applyNumberingToHeadersFooters(notes, numbering), styles);
147
+ }
148
+ function makeHyperlinkResolver(pkg, partName = MAIN_DOCUMENT_PART) {
149
+ const byRelId = /* @__PURE__ */ new Map();
150
+ for (const rel of pkg.getPartRelationships(partName)) if (rel.type === REL_HYPERLINK && rel.targetMode === "External") byRelId.set(rel.id, rel.target);
151
+ return (relId) => byRelId.get(relId);
152
+ }
132
153
  function loadCharts(pkg, resolveColor) {
133
154
  const out = /* @__PURE__ */ new Map();
134
155
  for (const rel of pkg.getPartRelationships(MAIN_DOCUMENT_PART)) {
135
- if (rel.type !== REL_CHART) continue;
156
+ if (!isOoxmlRel(rel.type, "chart")) continue;
136
157
  const resolved = pkg.resolveRelatedPart(MAIN_DOCUMENT_PART, rel);
137
158
  if (!resolved) continue;
138
159
  const chart = parseChart(resolved.data, resolveColor);
139
- if (chart) out.set(rel.id, chart);
160
+ if (chart) out.set(rel.id, withChartColorStyle(chart, pkg, resolved.path, resolveColor));
140
161
  }
141
162
  return out;
142
163
  }
@@ -149,7 +170,7 @@ function buildColorResolver(pkg) {
149
170
  }
150
171
  function loadTheme(pkg) {
151
172
  for (const rel of pkg.getPartRelationships(MAIN_DOCUMENT_PART)) {
152
- if (rel.type !== REL_THEME) continue;
173
+ if (!isOoxmlRel(rel.type, "theme")) continue;
153
174
  const resolved = pkg.resolveRelatedPart(MAIN_DOCUMENT_PART, rel);
154
175
  if (resolved) return resolved.data;
155
176
  }
@@ -167,12 +188,13 @@ function loadHeadersFootersForSections(pkg, sections, ctx, store) {
167
188
  const out = /* @__PURE__ */ new Map();
168
189
  for (const rel of rels) {
169
190
  if (!wanted.has(rel.id)) continue;
170
- if (rel.type !== REL_HEADER && rel.type !== REL_FOOTER) continue;
191
+ if (!isOoxmlRel(rel.type, "header") && !isOoxmlRel(rel.type, "footer")) continue;
171
192
  const resolved = pkg.resolveRelatedPart(MAIN_DOCUMENT_PART, rel);
172
193
  if (!resolved) continue;
173
194
  const hfCtx = {
174
195
  resolveColor: ctx.resolveColor,
175
- resolveImage: makeImageResolver(pkg, store, resolved.path)
196
+ resolveImage: makeImageResolver(pkg, store, resolved.path),
197
+ resolveHyperlink: makeHyperlinkResolver(pkg, resolved.path)
176
198
  };
177
199
  out.set(rel.id, parseHeaderFooter(resolved.data, hfCtx));
178
200
  }
@@ -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 } : {};