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.
- package/README.md +11 -5
- package/dist/esm/{layout → core}/arc-to-bezier.d.ts +1 -1
- package/dist/esm/{layout → core}/arc-to-bezier.js +2 -2
- package/dist/esm/core/converter/project.js +2 -0
- package/dist/esm/core/converter/ream.js +4 -3
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +60 -0
- package/dist/esm/{layout → core/drawingml}/chart-geometry.d.ts +2 -2
- package/dist/esm/{layout → core/drawingml}/chart-geometry.js +79 -117
- package/dist/esm/core/drawingml/chart-parser.d.ts +3 -0
- package/dist/esm/core/drawingml/chart-parser.js +28 -1
- package/dist/esm/{layout → core/drawingml}/preset-geometry.d.ts +2 -2
- package/dist/esm/{layout → core/drawingml}/preset-geometry.js +3 -3
- package/dist/esm/core/drawingml/shape-render.d.ts +8 -0
- package/dist/esm/core/drawingml/shape-render.js +71 -0
- package/dist/esm/core/ir/features.d.ts +1 -0
- package/dist/esm/core/ir/features.js +1 -0
- package/dist/esm/core/ir/flow.d.ts +3 -0
- package/dist/esm/core/links.d.ts +6 -0
- package/dist/esm/core/links.js +19 -0
- package/dist/esm/core/numbering/apply.js +5 -2
- package/dist/esm/core/opc/index.d.ts +1 -0
- package/dist/esm/core/opc/package.js +3 -2
- package/dist/esm/core/opc/relationship-types.d.ts +1 -0
- package/dist/esm/core/opc/relationship-types.js +5 -2
- package/dist/esm/core/style-cascade/index.d.ts +1 -0
- package/dist/esm/core/style-cascade/index.js +1 -0
- package/dist/esm/core/style-cascade/table.d.ts +2 -0
- package/dist/esm/core/style-cascade/table.js +161 -0
- package/dist/esm/core/vector.d.ts +2 -0
- package/dist/esm/core/vector.js +19 -1
- package/dist/esm/excel/print-model.d.ts +3 -0
- package/dist/esm/excel/print-model.js +4 -8
- package/dist/esm/excel/sheet-drawing.d.ts +9 -0
- package/dist/esm/excel/sheet-drawing.js +113 -0
- package/dist/esm/excel/worksheet-parser.d.ts +1 -0
- package/dist/esm/excel/worksheet-parser.js +4 -1
- package/dist/esm/excel/xlsx-reader.js +41 -0
- package/dist/esm/excel/xlsx-to-pdf.js +25 -18
- package/dist/esm/html/html-writer.js +188 -16
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/layout/page-doc.d.ts +4 -0
- package/dist/esm/layout/styled-layout.d.ts +21 -3
- package/dist/esm/layout/styled-layout.js +745 -259
- package/dist/esm/pdf/encryption.d.ts +34 -0
- package/dist/esm/pdf/encryption.js +143 -0
- package/dist/esm/pdf/index.d.ts +2 -1
- package/dist/esm/pdf/struct-tree.d.ts +7 -1
- package/dist/esm/pdf/struct-tree.js +22 -6
- package/dist/esm/pdf/styled-page-emitter.d.ts +3 -1
- package/dist/esm/pdf/styled-page-emitter.js +185 -35
- package/dist/esm/pdf/styled-page-renderer.d.ts +1 -0
- package/dist/esm/pdf/styled-page-renderer.js +17 -2
- package/dist/esm/pdf/writer.d.ts +2 -0
- package/dist/esm/pdf/writer.js +6 -1
- package/dist/esm/pdf/xmp.d.ts +1 -0
- package/dist/esm/pdf/xmp.js +4 -0
- package/dist/esm/svg/svg-writer.js +2 -6
- package/dist/esm/word/document-parser.d.ts +3 -0
- package/dist/esm/word/document-parser.js +214 -23
- package/dist/esm/word/docx-reader.js +37 -15
- package/dist/esm/word/docx-to-pdf.js +23 -13
- package/dist/esm/word/drawing-parser.d.ts +4 -1
- package/dist/esm/word/drawing-parser.js +53 -2
- package/dist/esm/word/index.d.ts +2 -2
- package/dist/esm/word/styles-parser.js +134 -1
- package/dist/esm/word/table-parser.js +35 -0
- 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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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.
|
|
77
|
-
detail:
|
|
152
|
+
feature: FEATURES.charts,
|
|
153
|
+
detail: `chart part '${block.chartRelId}' not supplied`
|
|
78
154
|
});
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
|
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("") || " "}</${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) {
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -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:
|
|
82
|
-
readonly first:
|
|
83
|
-
readonly even:
|
|
99
|
+
readonly default: HfBandEntry;
|
|
100
|
+
readonly first: HfBandEntry;
|
|
101
|
+
readonly even: HfBandEntry;
|
|
84
102
|
}
|