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,7 +1,7 @@
1
1
  import { fetchFontSet } 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 { readXlsx } from "./xlsx-reader.js";
7
7
  import { flowRenderOptions } from "../core/converter/project.js";
@@ -18,18 +18,18 @@ async function convertXlsxToPdf(xlsx, options = {}) {
18
18
  ...options,
19
19
  signaturePlaceholder: signature
20
20
  } : options;
21
- let pdf;
22
- if (renderOptions.fonts ?? renderOptions.fontBytes) pdf = convertXlsxToPdfSync(xlsx, renderOptions);
23
- else {
24
- const fonts = await fetchFontSet({
21
+ const withFonts = renderOptions.fonts ?? renderOptions.fontBytes ? renderOptions : {
22
+ ...renderOptions,
23
+ fonts: await fetchFontSet({
25
24
  ...options.fontFamily ? { family: options.fontFamily } : {},
26
25
  ...options.fontFetch ? { fetch: options.fontFetch } : {}
27
- });
28
- pdf = convertXlsxToPdfSync(xlsx, {
29
- ...renderOptions,
30
- fonts
31
- });
26
+ })
27
+ };
28
+ if (withFonts.encrypt) {
29
+ const args = prepareXlsxStyledRender(xlsx, withFonts);
30
+ return renderStyledPdfEncrypted(args.body, args.styled);
32
31
  }
32
+ const pdf = convertXlsxToPdfSync(xlsx, withFonts);
33
33
  return signature ? signPdf(pdf, signature) : pdf;
34
34
  }
35
35
  /**
@@ -38,6 +38,10 @@ async function convertXlsxToPdf(xlsx, options = {}) {
38
38
  * Internal since 1.0 — see the async variant above.
39
39
  */
40
40
  function convertXlsxToPdfSync(xlsx, options) {
41
+ const args = prepareXlsxStyledRender(xlsx, options);
42
+ return renderStyledPdf(args.body, args.styled);
43
+ }
44
+ function prepareXlsxStyledRender(xlsx, options) {
41
45
  const fonts = options.fonts ?? (options.fontBytes ? { regular: options.fontBytes } : void 0);
42
46
  if (!fonts) throw new Error("convertXlsxToPdfSync requires options.fonts or options.fontBytes");
43
47
  const registry = FontRegistry.fromBytes(fonts);
@@ -56,14 +60,17 @@ function convertXlsxToPdfSync(xlsx, options) {
56
60
  relationship: "Source",
57
61
  description: "Source Excel workbook"
58
62
  });
59
- return renderStyledPdf(flow.body, {
60
- registry,
61
- ...flowRenderOptions(flow),
62
- ...section ? { section } : {},
63
- ...info ? { info } : {},
64
- ...attachments.length > 0 ? { attachments } : {},
65
- ...renderOptions
66
- });
63
+ return {
64
+ body: flow.body,
65
+ styled: {
66
+ registry,
67
+ ...flowRenderOptions(flow),
68
+ ...section ? { section } : {},
69
+ ...info ? { info } : {},
70
+ ...attachments.length > 0 ? { attachments } : {},
71
+ ...renderOptions
72
+ }
73
+ };
67
74
  }
68
75
  //#endregion
69
76
  export { convertXlsxToPdf };
@@ -2,14 +2,23 @@ import { FEATURES } from "../core/ir/features.js";
2
2
  import { detectImageFormat } from "../core/images.js";
3
3
  import { EMPTY_STYLE_SHEET, resolveParagraphProperties, resolveRunProperties } from "../core/style-cascade/resolver.js";
4
4
  import "../core/style-cascade/index.js";
5
+ import { PathBuilder, flipTransform, svgPathData } from "../core/vector.js";
6
+ import { arcPoint, arcToBeziers } from "../core/arc-to-bezier.js";
7
+ import { buildChartScene } from "../core/drawingml/chart-geometry.js";
8
+ import { buildShapePaths, buildShapeTransform, buildStroke } from "../core/drawingml/shape-render.js";
9
+ import { sanitizeHref } from "../core/links.js";
5
10
  import { toBase64 } from "../core/bytes.js";
6
11
  //#region src/html/html-writer.ts
7
12
  function writeHtml(flow) {
8
13
  const losses = [];
9
14
  const out = [];
15
+ const notes = collectNoteNumbers(flow);
10
16
  const ctx = {
11
17
  resources: flow.resources,
12
- losses
18
+ losses,
19
+ notes,
20
+ referencedAnchors: collectReferencedAnchors(flow.body),
21
+ ...flow.charts ? { charts: flow.charts } : {}
13
22
  };
14
23
  const lang = flow.language ?? "en-US";
15
24
  const title = flow.info?.title ?? "Document";
@@ -29,6 +38,8 @@ function writeHtml(flow) {
29
38
  detail: "headers/footers have no fixed pages to band onto in flowed HTML"
30
39
  });
31
40
  for (const el of flow.body) emitBlock(out, el, ctx);
41
+ emitNotesSection(out, flow.footnotes, ctx.notes.footnotes, "fn", ctx);
42
+ emitNotesSection(out, flow.endnotes, ctx.notes.endnotes, "en", ctx);
32
43
  out.push("</article>");
33
44
  out.push("</body>");
34
45
  out.push("</html>");
@@ -46,6 +57,9 @@ var htmlWriter = {
46
57
  FEATURES.tablesNested,
47
58
  FEATURES.lists,
48
59
  FEATURES.images,
60
+ FEATURES.hyperlinks,
61
+ FEATURES.charts,
62
+ FEATURES.shapes,
49
63
  FEATURES.rtl,
50
64
  FEATURES.trackedChanges
51
65
  ]),
@@ -59,36 +73,176 @@ var BASE_CSS = [
59
73
  "td,th{vertical-align:top}",
60
74
  "th{text-align:inherit}",
61
75
  ".tab{display:inline-block;min-width:18pt}",
62
- "img{vertical-align:baseline}"
76
+ "img{vertical-align:baseline}",
77
+ ".notes{margin-top:18pt;font-size:smaller}",
78
+ ".notes hr{margin:0 0 6pt;border:none;border-top:0.75pt solid #000;width:144pt;margin-left:0}"
63
79
  ].join("");
80
+ function collectReferencedAnchors(body) {
81
+ const out = /* @__PURE__ */ new Set();
82
+ const visit = (els) => {
83
+ for (const el of els) if (el.kind === "paragraph") {
84
+ for (const r of el.paragraph.runs) if (r.anchor !== void 0) out.add(r.anchor);
85
+ } else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
86
+ else if (el.kind === "shape" && el.shape.text) visit(el.shape.text.content);
87
+ };
88
+ visit(body);
89
+ return out;
90
+ }
91
+ function collectNoteNumbers(flow) {
92
+ const footnotes = /* @__PURE__ */ new Map();
93
+ const endnotes = /* @__PURE__ */ new Map();
94
+ const visit = (els) => {
95
+ for (const el of els) if (el.kind === "paragraph") for (const r of el.paragraph.runs) {
96
+ if (r.footnoteRef !== void 0 && !footnotes.has(r.footnoteRef)) footnotes.set(r.footnoteRef, footnotes.size + 1);
97
+ if (r.endnoteRef !== void 0 && !endnotes.has(r.endnoteRef)) endnotes.set(r.endnoteRef, endnotes.size + 1);
98
+ }
99
+ else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
100
+ else if (el.kind === "shape" && el.shape.text) visit(el.shape.text.content);
101
+ };
102
+ visit(flow.body);
103
+ return {
104
+ footnotes,
105
+ endnotes
106
+ };
107
+ }
108
+ function emitNotesSection(out, content, numbers, prefix, ctx) {
109
+ if (!content || numbers.size === 0) return;
110
+ const ordered = [...numbers.entries()].sort((a, b) => a[1] - b[1]);
111
+ out.push("<section class=\"notes\"><hr/><ol>");
112
+ for (const [id, n] of ordered) {
113
+ const body = content.get(id);
114
+ out.push(`<li id="${prefix}-${n}">`);
115
+ if (body) for (const el of body) emitBlock(out, el, ctx);
116
+ out.push(`<a href="#${prefix}ref-${n}">\u21a9</a></li>`);
117
+ }
118
+ out.push("</ol></section>");
119
+ }
64
120
  function emitBlock(out, el, ctx) {
65
121
  if (el.kind === "paragraph") emitParagraph(out, el.paragraph, ctx);
66
122
  else if (el.kind === "table") emitTable(out, el.table, ctx);
67
123
  else if (el.kind === "image") emitImageBlock(out, el.image, ctx);
68
- else if (el.kind === "chart") ctx.losses.push({
69
- severity: "dropped",
70
- feature: FEATURES.charts,
71
- detail: "chart not rendered by the HTML writer (v0)"
72
- });
73
- else {
124
+ else if (el.kind === "chart") emitChartBlock(out, el.chart, ctx);
125
+ else emitShapeBlock(out, el.shape, ctx);
126
+ }
127
+ var approxTextWidth = (text, sizePt) => text.length * sizePt * .55;
128
+ var blockFigureCss = (pp) => {
129
+ const css = [];
130
+ const align = pp.alignment === "center" || pp.alignment === "right" ? pp.alignment : void 0;
131
+ if (align) css.push(`text-align:${align}`);
132
+ if (pp.spacingBefore !== void 0 && pp.spacingBefore > 0) css.push(`margin-top:${fmt(pp.spacingBefore)}pt`);
133
+ if (pp.spacingAfter !== void 0 && pp.spacingAfter > 0) css.push(`margin-bottom:${fmt(pp.spacingAfter)}pt`);
134
+ return css.length > 0 ? ` style="${css.join(";")}"` : "";
135
+ };
136
+ var svgOpen = (w, h, label) => `<svg viewBox="0 0 ${fmt(w)} ${fmt(h)}" width="${fmt(w)}pt" height="${fmt(h)}pt" style="max-width:100%;height:auto" overflow="visible" xmlns="http://www.w3.org/2000/svg" role="img"${label ? ` aria-label="${escapeAttr(label)}"` : ""}>`;
137
+ var strokeAttrs = (stroke) => {
138
+ if (!stroke) return "";
139
+ let out = ` stroke="#${stroke.colorHex}" stroke-width="${fmt(stroke.widthPt)}"`;
140
+ if (stroke.dash && stroke.dash.length > 0) out += ` stroke-dasharray="${stroke.dash.map(fmt).join(" ")}"`;
141
+ if (stroke.cap && stroke.cap !== "butt") out += ` stroke-linecap="${stroke.cap}"`;
142
+ if (stroke.join) out += ` stroke-linejoin="${stroke.join}"`;
143
+ return out;
144
+ };
145
+ function emitChartBlock(out, block, ctx) {
146
+ const chart = ctx.charts?.get(block.chartRelId);
147
+ const label = block.altText ?? chart?.title;
148
+ const figure = `<figure${blockFigureCss(block.paragraphProperties)}>`;
149
+ if (!chart) {
74
150
  ctx.losses.push({
75
151
  severity: "dropped",
76
- feature: FEATURES.shapes,
77
- detail: "shape geometry not rendered by the HTML writer (v0)"
152
+ feature: FEATURES.charts,
153
+ detail: `chart part '${block.chartRelId}' not supplied`
78
154
  });
79
- if (el.shape.text) {
80
- out.push("<div>");
81
- for (const child of el.shape.text.content) emitBlock(out, child, ctx);
82
- out.push("</div>");
83
- }
155
+ return;
156
+ }
157
+ const w = block.width;
158
+ const h = block.height;
159
+ const scene = buildChartScene(chart, w, h, approxTextWidth);
160
+ if (!scene) {
161
+ ctx.losses.push({
162
+ severity: "degraded",
163
+ feature: FEATURES.charts,
164
+ detail: `unsupported chart type '${chart.type}' — placeholder box`
165
+ });
166
+ out.push(figure + svgOpen(w, h, label) + `<rect x="0.5" y="0.5" width="${fmt(w - 1)}" height="${fmt(h - 1)}" fill="none" stroke="#D9D9D9"/></svg></figure>`);
167
+ return;
168
+ }
169
+ const svg = [figure, svgOpen(w, h, label)];
170
+ svg.push(`<g transform="matrix(1 0 0 -1 0 ${fmt(h)})">`);
171
+ for (const pg of scene.polygons ?? []) svg.push(polygonSvg(pg));
172
+ for (const r of scene.rects) svg.push(rectSvg(r));
173
+ for (const pl of scene.polylines) svg.push(polylineSvg(pl));
174
+ for (const wd of scene.wedges) svg.push(wedgeSvg(wd));
175
+ svg.push("</g>");
176
+ const anchor = {
177
+ left: "start",
178
+ center: "middle",
179
+ right: "end"
180
+ };
181
+ for (const l of scene.labels) svg.push(labelSvg(l, h, anchor[l.align]));
182
+ svg.push("</svg></figure>");
183
+ out.push(svg.join(""));
184
+ }
185
+ function rectSvg(r) {
186
+ const fill = r.fillHex ? `#${r.fillHex}` : "none";
187
+ const stroke = r.strokeHex ? ` stroke="#${r.strokeHex}" stroke-width="${fmt(r.strokeWidthPt ?? 1)}"` : "";
188
+ return `<rect x="${fmt(r.x)}" y="${fmt(r.y)}" width="${fmt(r.w)}" height="${fmt(r.h)}" fill="${fill}"${stroke}/>`;
189
+ }
190
+ var pointsAttr = (points) => points.map(([x, y]) => `${fmt(x)},${fmt(y)}`).join(" ");
191
+ function polylineSvg(p) {
192
+ return `<polyline points="${pointsAttr(p.points)}" fill="none" stroke="#${p.strokeHex}" stroke-width="${fmt(p.widthPt)}"/>`;
193
+ }
194
+ function polygonSvg(p) {
195
+ const stroke = p.strokeHex ? ` stroke="#${p.strokeHex}" stroke-width="${fmt(p.widthPt ?? 1)}"` : "";
196
+ return `<polygon points="${pointsAttr(p.points)}" fill="#${p.fillHex}"${stroke}/>`;
197
+ }
198
+ function wedgeSvg(w) {
199
+ const start = arcPoint(w.cx, w.cy, w.r, w.r, w.startRad);
200
+ const b = new PathBuilder().moveTo(w.cx, w.cy).lineTo(start[0], start[1]).append(arcToBeziers(w.cx, w.cy, w.r, w.r, w.startRad, w.sweepRad)).close();
201
+ const stroke = w.strokeHex ? ` stroke="#${w.strokeHex}" stroke-width="1"` : "";
202
+ return `<path d="${svgPathData(b.build().segments, fmt)}" fill="#${w.fillHex}"${stroke}/>`;
203
+ }
204
+ function labelSvg(l, sceneH, anchor) {
205
+ return `<text x="${fmt(l.x)}" y="${fmt(sceneH - l.y)}" font-size="${fmt(l.sizePt)}" font-family="sans-serif" fill="#${l.colorHex}" text-anchor="${anchor}">${escapeText(l.text)}</text>`;
206
+ }
207
+ function emitShapeBlock(out, shape, ctx) {
208
+ const w = shape.width;
209
+ const h = shape.height;
210
+ const paths = buildShapePaths(shape.geometry, w, h);
211
+ const fill = shape.fill.kind === "solid" && shape.fill.colorHex ? `#${shape.fill.colorHex}` : "none";
212
+ const stroke = strokeAttrs(buildStroke(shape.line));
213
+ const t = shape.transform;
214
+ const transform = ` transform="matrix(${flipTransform(buildShapeTransform(0, 0, w, h, t?.rotation60k ?? 0, t?.flipH ?? false, t?.flipV ?? false), h).map(fmt).join(" ")})"`;
215
+ const svg = [svgOpen(w, h, shape.altText)];
216
+ for (const path of paths) {
217
+ const rule = path.fillRule === "evenodd" ? " fill-rule=\"evenodd\"" : "";
218
+ svg.push(`<path d="${svgPathData(path.segments, fmt)}" fill="${fill}"${rule}${stroke}${transform}/>`);
84
219
  }
220
+ svg.push("</svg>");
221
+ const figureCss = blockFigureCss(shape.paragraphProperties);
222
+ const text = shape.text;
223
+ if (!text || text.content.length === 0) {
224
+ out.push(`<figure${figureCss}>${svg.join("")}</figure>`);
225
+ return;
226
+ }
227
+ const justify = text.anchor === "ctr" ? "center" : text.anchor === "b" ? "flex-end" : "flex-start";
228
+ const il = text.insetLeft ?? 7.2;
229
+ const ir = text.insetRight ?? 7.2;
230
+ const it = text.insetTop ?? 3.6;
231
+ const ib = text.insetBottom ?? 3.6;
232
+ out.push(`<figure${figureCss}><div style="position:relative;width:${fmt(w)}pt;height:${fmt(h)}pt;max-width:100%"><div style="position:absolute;left:0;top:0">${svg.join("")}</div><div style="position:absolute;left:${fmt(il)}pt;right:${fmt(ir)}pt;top:${fmt(it)}pt;bottom:${fmt(ib)}pt;display:flex;flex-direction:column;justify-content:${justify};overflow:hidden">`);
233
+ for (const child of text.content) emitBlock(out, child, ctx);
234
+ out.push("</div></div></figure>");
85
235
  }
86
236
  function emitParagraph(out, p, ctx) {
87
237
  const resolved = resolveParagraphProperties(p.properties, EMPTY_STYLE_SHEET);
88
238
  const lvl = resolved.outlineLevel;
89
239
  const tag = lvl !== void 0 && lvl >= 0 && lvl <= 8 ? `h${Math.min(lvl, 5) + 1}` : "p";
90
240
  const style = paragraphCss(resolved);
91
- const open = `<${tag}${resolved.bidi ? " dir=\"rtl\"" : ""}${style ? ` style="${style}"` : ""}>`;
241
+ const dir = resolved.bidi ? " dir=\"rtl\"" : "";
242
+ const anchored = (p.bookmarks ?? []).filter((b) => ctx.referencedAnchors.has(b));
243
+ const idAttr = anchored.length > 0 ? ` id="bm-${encodeURIComponent(anchored[0])}"` : "";
244
+ const extraAnchors = anchored.slice(1).map((b) => `<span id="bm-${encodeURIComponent(b)}"></span>`).join("");
245
+ const open = `<${tag}${dir}${idAttr}${style ? ` style="${style}"` : ""}>${extraAnchors}`;
92
246
  const parts = [];
93
247
  for (const run of p.runs) parts.push(runHtml(run, p, ctx));
94
248
  out.push(`${open}${parts.join("") || "&nbsp;"}</${tag}>`);
@@ -108,6 +262,14 @@ function paragraphCss(r) {
108
262
  return css.join(";");
109
263
  }
110
264
  function runHtml(run, p, ctx) {
265
+ if (run.footnoteRef !== void 0 || run.endnoteRef !== void 0) {
266
+ const isFoot = run.footnoteRef !== void 0;
267
+ const n = isFoot ? ctx.notes.footnotes.get(run.footnoteRef) : ctx.notes.endnotes.get(run.endnoteRef);
268
+ if (n === void 0) return "";
269
+ const prefix = isFoot ? "fn" : "en";
270
+ return `<sup><a href="#${prefix}-${n}" id="${prefix}ref-${n}">${n}</a></sup>`;
271
+ }
272
+ if (run.noteNumber) return "";
111
273
  if (run.math !== void 0) {
112
274
  ctx.losses.push({
113
275
  severity: "dropped",
@@ -128,6 +290,16 @@ function runHtml(run, p, ctx) {
128
290
  let html = `<span${resolved.rtl ? " dir=\"rtl\"" : ""}${style ? ` style="${style}"` : ""}>${textHtml(run.text)}</span>`;
129
291
  if (resolved.verticalAlign === "superscript") html = `<sup>${html}</sup>`;
130
292
  else if (resolved.verticalAlign === "subscript") html = `<sub>${html}</sub>`;
293
+ if (run.href === void 0 && run.anchor !== void 0) return `<a href="#bm-${encodeURIComponent(run.anchor)}">${html}</a>`;
294
+ if (run.href !== void 0) {
295
+ const safe = sanitizeHref(run.href);
296
+ if (safe !== void 0) html = `<a href="${escapeAttr(safe)}">${html}</a>`;
297
+ else ctx.losses.push({
298
+ severity: "degraded",
299
+ feature: FEATURES.hyperlinks,
300
+ detail: `hyperlink target with a disallowed scheme rendered as plain text`
301
+ });
302
+ }
131
303
  return html;
132
304
  }
133
305
  function runCss(r) {
@@ -4,6 +4,7 @@ export { renderStyledPdf } from './pdf/index.js';
4
4
  export type { StyledRenderOptions, DocumentInfo } from './pdf/index.js';
5
5
  export { signPdf } from './pdf/index.js';
6
6
  export type { SignaturePlaceholder, SignerCredentials, SignatureOptions } from './pdf/index.js';
7
+ export type { PdfEncryptOptions, PdfPermissions } from './pdf/index.js';
7
8
  export { FontRegistry, parseTtf, subsetTtf } from './core/font/index.js';
8
9
  export type { FontBytesByVariant, FontVariant, ParsedTtf } from './core/font/index.js';
9
10
  export { getHyphenator, createLanguageHyphenator, createHyphenator, splitPatternBundle, } from './core/hyphenation/index.js';
@@ -13,6 +13,10 @@ export interface TextToken {
13
13
  readonly kind: 'text';
14
14
  readonly text: string;
15
15
  readonly isSpace: boolean;
16
+ readonly href?: string;
17
+ readonly footnoteRef?: string;
18
+ readonly anchor?: string;
19
+ readonly listMarker?: true;
16
20
  readonly resolvedRun: ResolvedRunProperties;
17
21
  readonly font: FontResource;
18
22
  readonly fontSizePt: number;
@@ -5,6 +5,7 @@ import { Hyphenator } from '../core/hyphenation/index.js';
5
5
  import { LaidOutDocument, PageItem } from './page-doc.js';
6
6
  import { AttachedFile } from '../pdf/embedded-file.js';
7
7
  import { SignaturePlaceholder } from '../pdf/signature.js';
8
+ import { PdfEncryptOptions } from '../pdf/encryption.js';
8
9
  import { ResourceStore } from '../core/ir/index.js';
9
10
  import { StructTreeBuilder } from '../pdf/struct-tree.js';
10
11
  export type PdfALevel = 'PDF/A-1b' | 'PDF/A-1a' | 'PDF/A-2b' | 'PDF/A-2u' | 'PDF/A-2a' | 'PDF/A-3b' | 'PDF/A-3u' | 'PDF/A-3a';
@@ -23,6 +24,8 @@ export interface StyledRenderOptions {
23
24
  readonly section?: SectionProperties;
24
25
  readonly sections?: ReadonlyArray<Section>;
25
26
  readonly headersFooters?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
27
+ readonly footnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
28
+ readonly endnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
26
29
  readonly resources?: ResourceStore;
27
30
  readonly charts?: ReadonlyMap<string, Chart>;
28
31
  readonly pageWidth?: number;
@@ -35,18 +38,25 @@ export interface StyledRenderOptions {
35
38
  readonly info?: DocumentInfo;
36
39
  readonly pdfA?: PdfALevel;
37
40
  readonly tagged?: boolean;
41
+ readonly pdfUA?: boolean;
38
42
  readonly language?: string;
43
+ readonly encrypt?: PdfEncryptOptions;
39
44
  readonly attachments?: ReadonlyArray<AttachedFile>;
40
45
  readonly signaturePlaceholder?: SignaturePlaceholder;
41
46
  }
42
47
  export type { DocumentInfo } from '../core/document-model/index.js';
43
48
  export declare const A4_WIDTH = 595;
44
49
  export declare const A4_HEIGHT = 842;
50
+ export interface BookmarkPosition {
51
+ readonly pageIdx: number;
52
+ readonly yTopPt: number;
53
+ }
45
54
  export interface PdfLayoutAux {
46
55
  readonly structBuilder: StructTreeBuilder | undefined;
47
56
  readonly sectionCtxs: ReadonlyArray<SectionRenderCtx>;
48
57
  readonly pdfaProfile: PdfAProfile | undefined;
49
58
  readonly tagged: boolean;
59
+ readonly bookmarks: ReadonlyMap<string, BookmarkPosition>;
50
60
  }
51
61
  export interface LaidOutPdfDocument extends LaidOutDocument {
52
62
  readonly pdf: PdfLayoutAux;
@@ -72,13 +82,21 @@ export interface SectionRenderCtx {
72
82
  readonly marginBottom: number;
73
83
  readonly contentWidth: number;
74
84
  readonly pageContentHeight: number;
85
+ readonly columns?: ReadonlyArray<{
86
+ readonly xOffsetPt: number;
87
+ readonly widthPt: number;
88
+ }>;
75
89
  readonly headerSet: HeaderFooterSet;
76
90
  readonly footerSet: HeaderFooterSet;
77
91
  readonly titlePg: boolean;
78
92
  readonly evenAndOddHeaders: boolean;
79
93
  }
94
+ interface HfBandEntry {
95
+ readonly commands: Array<PageItem>;
96
+ readonly renderDynamic?: (pageNumber: number, totalPages: number) => Array<PageItem>;
97
+ }
80
98
  interface HeaderFooterSet {
81
- readonly default: Array<PageItem>;
82
- readonly first: Array<PageItem>;
83
- readonly even: Array<PageItem>;
99
+ readonly default: HfBandEntry;
100
+ readonly first: HfBandEntry;
101
+ readonly even: HfBandEntry;
84
102
  }