@segnavia/render 0.1.0 → 0.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/dist/color/binary.js +27 -0
- package/dist/color/black-point.js +121 -0
- package/dist/color/engine.js +18 -0
- package/dist/color/interpolation.js +74 -0
- package/dist/color/profile.js +98 -0
- package/dist/color/spaces.js +138 -0
- package/dist/color/tables.js +255 -0
- package/dist/color/types.js +21 -0
- package/dist/deck-pdf.js +152 -0
- package/dist/effects/adapter.js +336 -0
- package/dist/effects/content-replacement.js +35 -0
- package/dist/effects/function-evaluator.js +72 -0
- package/dist/effects/policy.js +10 -0
- package/dist/effects/rule-1.js +40 -0
- package/dist/effects/rule-c.js +354 -0
- package/dist/effects/structure.js +86 -0
- package/dist/effects/types.d.ts +15 -0
- package/dist/effects/types.js +4 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +9 -0
- package/dist/index.d.ts +18 -8
- package/dist/index.js +96 -76
- package/dist/page-paper.js +94 -0
- package/dist/pages-imposition.js +65 -0
- package/dist/pages-pdf.js +67 -0
- package/dist/pdf-compose.js +35 -0
- package/dist/pdf-geometry.js +27 -0
- package/dist/press/adapter.js +275 -0
- package/dist/press/affine.js +60 -0
- package/dist/press/calculator-ops.js +277 -0
- package/dist/press/calculator.js +130 -0
- package/dist/press/color-policy.js +31 -0
- package/dist/press/content.js +292 -0
- package/dist/press/pattern-bounds.js +275 -0
- package/dist/press/pattern-path.js +102 -0
- package/dist/press/pdf-objects.js +88 -0
- package/dist/press/policy.js +126 -0
- package/dist/press/report.js +89 -0
- package/dist/press/sample-shading.js +266 -0
- package/dist/press/types.d.ts +18 -0
- package/dist/press/visitor.js +163 -0
- package/package.json +3 -3
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { REVERSE_LUT, checked, clamp, refuse } from "./types.js";
|
|
2
|
+
import { invert3, matrix3 } from "./spaces.js";
|
|
3
|
+
import { align4, fixed, need, signature, u16, u32 } from "./binary.js";
|
|
4
|
+
import { interpolate, matrixInput, multilinear, tetrahedral4 } from "./interpolation.js";
|
|
5
|
+
//#region src/color/tables.ts
|
|
6
|
+
function packed(bytes, view, offset, length, width, bounds) {
|
|
7
|
+
need(offset, length * width, bounds);
|
|
8
|
+
return {
|
|
9
|
+
length,
|
|
10
|
+
get(index) {
|
|
11
|
+
if (!Number.isInteger(index) || index < 0 || index >= length) throw new Error(`${bounds.name} sample index ${index} is outside length ${length}`);
|
|
12
|
+
return width === 1 ? checked(bytes, offset + index, `${bounds.name} samples`) / 255 : u16(view, offset + index * 2, bounds) / 65535;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const parametric = [
|
|
17
|
+
(p, x) => clamp(x) ** checked(p, 0, "parametric curve"),
|
|
18
|
+
(p, x) => {
|
|
19
|
+
const g = checked(p, 0, "parametric curve"), a = checked(p, 1, "parametric curve"), b = checked(p, 2, "parametric curve");
|
|
20
|
+
return x >= -b / a ? (a * x + b) ** g : 0;
|
|
21
|
+
},
|
|
22
|
+
(p, x) => {
|
|
23
|
+
const g = checked(p, 0, "parametric curve"), a = checked(p, 1, "parametric curve"), b = checked(p, 2, "parametric curve"), c = checked(p, 3, "parametric curve");
|
|
24
|
+
return x >= -b / a ? (a * x + b) ** g + c : c;
|
|
25
|
+
},
|
|
26
|
+
(p, x) => {
|
|
27
|
+
const g = checked(p, 0, "parametric curve"), a = checked(p, 1, "parametric curve"), b = checked(p, 2, "parametric curve"), c = checked(p, 3, "parametric curve");
|
|
28
|
+
return x >= checked(p, 4, "parametric curve") ? (a * x + b) ** g : c * x;
|
|
29
|
+
},
|
|
30
|
+
(p, x) => {
|
|
31
|
+
const g = checked(p, 0, "parametric curve"), a = checked(p, 1, "parametric curve"), b = checked(p, 2, "parametric curve"), c = checked(p, 3, "parametric curve"), d = checked(p, 4, "parametric curve"), e = checked(p, 5, "parametric curve"), f = checked(p, 6, "parametric curve");
|
|
32
|
+
return x >= d ? (a * x + b) ** g + e : c * x + f;
|
|
33
|
+
}
|
|
34
|
+
];
|
|
35
|
+
function parseCurve(bytes, view, offset, bounds) {
|
|
36
|
+
need(offset, 12, bounds);
|
|
37
|
+
const type = signature(bytes, offset, bounds);
|
|
38
|
+
if (type === "curv") {
|
|
39
|
+
const count = u32(view, offset + 8, bounds);
|
|
40
|
+
need(offset + 12, count * 2, bounds);
|
|
41
|
+
if (count === 0) return {
|
|
42
|
+
curve: (value) => value,
|
|
43
|
+
size: 12
|
|
44
|
+
};
|
|
45
|
+
if (count === 1) {
|
|
46
|
+
const gamma = u16(view, offset + 12, bounds) / 256;
|
|
47
|
+
return {
|
|
48
|
+
curve: (value) => clamp(value) ** gamma,
|
|
49
|
+
size: 14
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const values = packed(bytes, view, offset + 12, count, 2, bounds);
|
|
53
|
+
return {
|
|
54
|
+
curve: (value) => interpolate(values, value),
|
|
55
|
+
size: 12 + count * 2
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (type !== "para") return refuse(`curve tag type ${JSON.stringify(type)}`, "curv or para");
|
|
59
|
+
const kind = u16(view, offset + 8, bounds), counts = [
|
|
60
|
+
1,
|
|
61
|
+
3,
|
|
62
|
+
4,
|
|
63
|
+
5,
|
|
64
|
+
7
|
|
65
|
+
];
|
|
66
|
+
if (kind > 4) refuse(`parametric curve function ${kind}`, "functions 0 through 4");
|
|
67
|
+
const count = checked(counts, kind, "parametric curve function");
|
|
68
|
+
need(offset + 12, count * 4, bounds);
|
|
69
|
+
const parameters = Array.from({ length: count }, (_, index) => fixed(view, offset + 12 + index * 4, bounds));
|
|
70
|
+
const evaluate = checked(parametric, kind, "parametric curve function");
|
|
71
|
+
return {
|
|
72
|
+
curve: (value) => evaluate(parameters, value),
|
|
73
|
+
size: 12 + count * 4
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function curvesAt(bytes, view, offset, count, bounds) {
|
|
77
|
+
const curves = [];
|
|
78
|
+
let cursor = offset;
|
|
79
|
+
for (let index = 0; index < count; index++) {
|
|
80
|
+
const parsed = parseCurve(bytes, view, cursor, bounds);
|
|
81
|
+
curves.push(parsed.curve);
|
|
82
|
+
cursor = align4(cursor + parsed.size);
|
|
83
|
+
}
|
|
84
|
+
return curves;
|
|
85
|
+
}
|
|
86
|
+
function channels(bytes, offset, expectedInputs, expectedOutputs, bounds) {
|
|
87
|
+
need(offset + 8, 2, bounds);
|
|
88
|
+
const inputs = checked(bytes, offset + 8, `${bounds.name} header`), outputs = checked(bytes, offset + 9, `${bounds.name} header`);
|
|
89
|
+
if (inputs !== expectedInputs || outputs !== expectedOutputs) refuse(`${bounds.name} channel count ${inputs}->${outputs}`, `${expectedInputs}->${expectedOutputs}`);
|
|
90
|
+
}
|
|
91
|
+
function parseMft(bytes, view, record, type, inputs, outputs) {
|
|
92
|
+
const { offset } = record;
|
|
93
|
+
need(offset, type === "mft1" ? 48 : 52, record);
|
|
94
|
+
channels(bytes, offset, inputs, outputs, record);
|
|
95
|
+
const pointCount = checked(bytes, offset + 10, `${type} header`);
|
|
96
|
+
if (pointCount < 2) refuse(`${type} grid point count ${pointCount}`, "at least 2");
|
|
97
|
+
const matrix = Array.from({ length: 9 }, (_, index) => fixed(view, offset + 12 + index * 4, record));
|
|
98
|
+
const inputEntries = type === "mft1" ? 256 : u16(view, offset + 48, record), outputEntries = type === "mft1" ? 256 : u16(view, offset + 50, record);
|
|
99
|
+
if (inputEntries < 2 || outputEntries < 2) refuse(`${type} table entry counts ${inputEntries}/${outputEntries}`, "at least 2 entries");
|
|
100
|
+
const width = type === "mft1" ? 1 : 2;
|
|
101
|
+
let cursor = offset + (type === "mft1" ? 48 : 52);
|
|
102
|
+
const inputTables = Array.from({ length: inputs }, () => {
|
|
103
|
+
const result = packed(bytes, view, cursor, inputEntries, width, record);
|
|
104
|
+
cursor += inputEntries * width;
|
|
105
|
+
return result;
|
|
106
|
+
});
|
|
107
|
+
const gridLength = pointCount ** inputs * outputs, grid = packed(bytes, view, cursor, gridLength, width, record);
|
|
108
|
+
cursor += gridLength * width;
|
|
109
|
+
const outputTables = Array.from({ length: outputs }, () => {
|
|
110
|
+
const result = packed(bytes, view, cursor, outputEntries, width, record);
|
|
111
|
+
cursor += outputEntries * width;
|
|
112
|
+
return result;
|
|
113
|
+
});
|
|
114
|
+
const points = Array.from({ length: inputs }, () => pointCount);
|
|
115
|
+
return {
|
|
116
|
+
inputChannels: inputs,
|
|
117
|
+
outputChannels: outputs,
|
|
118
|
+
encoding: type,
|
|
119
|
+
run(values) {
|
|
120
|
+
let current = values.map((value, channel) => interpolate(checked(inputTables, channel, `${type} input tables`), value));
|
|
121
|
+
if (inputs === 3) {
|
|
122
|
+
const fixedMatrix = [
|
|
123
|
+
checked(matrix, 0, `${type} matrix`),
|
|
124
|
+
checked(matrix, 1, `${type} matrix`),
|
|
125
|
+
checked(matrix, 2, `${type} matrix`),
|
|
126
|
+
checked(matrix, 3, `${type} matrix`),
|
|
127
|
+
checked(matrix, 4, `${type} matrix`),
|
|
128
|
+
checked(matrix, 5, `${type} matrix`),
|
|
129
|
+
checked(matrix, 6, `${type} matrix`),
|
|
130
|
+
checked(matrix, 7, `${type} matrix`),
|
|
131
|
+
checked(matrix, 8, `${type} matrix`)
|
|
132
|
+
];
|
|
133
|
+
current = matrix3(fixedMatrix, matrixInput(current));
|
|
134
|
+
}
|
|
135
|
+
current = inputs === 3 ? multilinear(grid, points, outputs, current) : tetrahedral4(grid, points, outputs, current);
|
|
136
|
+
return current.map((value, channel) => interpolate(checked(outputTables, channel, `${type} output tables`), value));
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function mabGrid(bytes, view, record, type, offset, inputs, outputs) {
|
|
141
|
+
if (!offset) return {
|
|
142
|
+
points: [],
|
|
143
|
+
grid: void 0
|
|
144
|
+
};
|
|
145
|
+
need(offset, 20, record);
|
|
146
|
+
const points = Array.from(bytes.subarray(offset, offset + inputs));
|
|
147
|
+
const badPoint = points.find((point) => point < 2);
|
|
148
|
+
if (badPoint !== void 0) refuse(`${type} CLUT grid point count ${badPoint}`, "at least 2");
|
|
149
|
+
const precision = checked(bytes, offset + 16, `${type} CLUT`);
|
|
150
|
+
if (precision !== 1 && precision !== 2) refuse(`${type} CLUT precision ${precision}`, "1 or 2 bytes");
|
|
151
|
+
return {
|
|
152
|
+
points,
|
|
153
|
+
grid: packed(bytes, view, offset + 20, points.reduce((a, b) => a * b, outputs), precision, record)
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function parseMab(bytes, view, record, type, inputs, outputs) {
|
|
157
|
+
const { offset } = record;
|
|
158
|
+
need(offset, 32, record);
|
|
159
|
+
channels(bytes, offset, inputs, outputs, record);
|
|
160
|
+
const relative = (at) => {
|
|
161
|
+
const value = u32(view, offset + at, record), absolute = value === 0 ? 0 : offset + value;
|
|
162
|
+
if (absolute) need(absolute, 1, record);
|
|
163
|
+
return absolute;
|
|
164
|
+
};
|
|
165
|
+
const bOffset = relative(12), matrixOffset = relative(16), mOffset = relative(20), clutOffset = relative(24), aOffset = relative(28);
|
|
166
|
+
const bCurves = bOffset ? curvesAt(bytes, view, bOffset, type === "mAB " ? outputs : inputs, record) : [];
|
|
167
|
+
const mCurves = mOffset ? curvesAt(bytes, view, mOffset, type === "mAB " ? outputs : inputs, record) : [];
|
|
168
|
+
const aCurves = aOffset ? curvesAt(bytes, view, aOffset, type === "mAB " ? inputs : outputs, record) : [];
|
|
169
|
+
if (matrixOffset) need(matrixOffset, 48, record);
|
|
170
|
+
const matrix = matrixOffset ? Array.from({ length: 12 }, (_, index) => fixed(view, matrixOffset + index * 4, record)) : [];
|
|
171
|
+
const { points, grid } = mabGrid(bytes, view, record, type, clutOffset, inputs, outputs);
|
|
172
|
+
const apply = (curves, values) => curves.length ? values.map((value, index) => checked(curves, index, `${type} curves`)(value)) : [...values];
|
|
173
|
+
const applyMatrix = (values) => {
|
|
174
|
+
if (!matrix.length) return [...values];
|
|
175
|
+
const [x, y, z] = matrixInput(values);
|
|
176
|
+
return [
|
|
177
|
+
0,
|
|
178
|
+
1,
|
|
179
|
+
2
|
|
180
|
+
].map((row) => checked(matrix, row * 3, `${type} matrix`) * x + checked(matrix, row * 3 + 1, `${type} matrix`) * y + checked(matrix, row * 3 + 2, `${type} matrix`) * z + checked(matrix, 9 + row, `${type} matrix`));
|
|
181
|
+
};
|
|
182
|
+
const clut = (values) => !grid ? [...values] : inputs === 3 ? multilinear(grid, points, outputs, values) : tetrahedral4(grid, points, outputs, values);
|
|
183
|
+
return {
|
|
184
|
+
inputChannels: inputs,
|
|
185
|
+
outputChannels: outputs,
|
|
186
|
+
encoding: type,
|
|
187
|
+
run(values) {
|
|
188
|
+
return type === "mAB " ? apply(bCurves, applyMatrix(apply(mCurves, clut(apply(aCurves, values))))) : apply(aCurves, clut(apply(mCurves, applyMatrix(apply(bCurves, values)))));
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function matrixTables(bytes, view, records) {
|
|
193
|
+
const names = [
|
|
194
|
+
"rXYZ",
|
|
195
|
+
"gXYZ",
|
|
196
|
+
"bXYZ",
|
|
197
|
+
"rTRC",
|
|
198
|
+
"gTRC",
|
|
199
|
+
"bTRC"
|
|
200
|
+
];
|
|
201
|
+
if (!names.every((name) => records[name])) return void 0;
|
|
202
|
+
const record = (name) => {
|
|
203
|
+
const found = records[name];
|
|
204
|
+
if (!found) throw new Error(`ICC profile is missing ${name} after its tag set was validated`);
|
|
205
|
+
return found;
|
|
206
|
+
};
|
|
207
|
+
for (const name of names.slice(0, 3)) if (record(name).type !== "XYZ ") refuse(`${name} tag type ${record(name).type}`, "XYZ ");
|
|
208
|
+
const columns = names.slice(0, 3).map((name) => {
|
|
209
|
+
const tag = record(name);
|
|
210
|
+
need(tag.offset, 20, tag);
|
|
211
|
+
return [
|
|
212
|
+
fixed(view, tag.offset + 8, tag),
|
|
213
|
+
fixed(view, tag.offset + 12, tag),
|
|
214
|
+
fixed(view, tag.offset + 16, tag)
|
|
215
|
+
];
|
|
216
|
+
});
|
|
217
|
+
const curves = names.slice(3).map((name) => parseCurve(bytes, view, record(name).offset, record(name)).curve);
|
|
218
|
+
const matrix = [
|
|
219
|
+
checked(checked(columns, 0, "colorants"), 0, "red"),
|
|
220
|
+
checked(checked(columns, 1, "colorants"), 0, "green"),
|
|
221
|
+
checked(checked(columns, 2, "colorants"), 0, "blue"),
|
|
222
|
+
checked(checked(columns, 0, "colorants"), 1, "red"),
|
|
223
|
+
checked(checked(columns, 1, "colorants"), 1, "green"),
|
|
224
|
+
checked(checked(columns, 2, "colorants"), 1, "blue"),
|
|
225
|
+
checked(checked(columns, 0, "colorants"), 2, "red"),
|
|
226
|
+
checked(checked(columns, 1, "colorants"), 2, "green"),
|
|
227
|
+
checked(checked(columns, 2, "colorants"), 2, "blue")
|
|
228
|
+
];
|
|
229
|
+
const inverse = invert3(matrix);
|
|
230
|
+
const invertCurve = (curve, target) => {
|
|
231
|
+
let low = 0, high = 1;
|
|
232
|
+
for (let index = 0; index < 24; index++) {
|
|
233
|
+
const middle = (low + high) / 2;
|
|
234
|
+
if (curve(middle) < target) low = middle;
|
|
235
|
+
else high = middle;
|
|
236
|
+
}
|
|
237
|
+
return (low + high) / 2;
|
|
238
|
+
};
|
|
239
|
+
return {
|
|
240
|
+
a2b: {
|
|
241
|
+
inputChannels: 3,
|
|
242
|
+
outputChannels: 3,
|
|
243
|
+
encoding: "mAB ",
|
|
244
|
+
run: (input) => matrix3(matrix, matrixInput(input.map((value, index) => checked(curves, index, "RGB curves")(value))))
|
|
245
|
+
},
|
|
246
|
+
b2a: {
|
|
247
|
+
inputChannels: 3,
|
|
248
|
+
outputChannels: 3,
|
|
249
|
+
encoding: REVERSE_LUT,
|
|
250
|
+
run: (input) => matrix3(inverse, matrixInput(input)).map((value, index) => invertCurve(checked(curves, index, "RGB curves"), value))
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
//#endregion
|
|
255
|
+
export { matrixTables, parseCurve, parseMab, parseMft };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/color/types.ts
|
|
2
|
+
const REVERSE_LUT = "mBA ";
|
|
3
|
+
function refuse(found, supported) {
|
|
4
|
+
throw new Error(`ICC profile has unsupported ${found}; supported: ${supported}`);
|
|
5
|
+
}
|
|
6
|
+
function checked(values, index, structure) {
|
|
7
|
+
const value = values[index];
|
|
8
|
+
if (value === void 0) throw new Error(`${structure} index ${index} is outside length ${values.length}`);
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
function color4(values, structure) {
|
|
12
|
+
return [
|
|
13
|
+
checked(values, 0, structure),
|
|
14
|
+
checked(values, 1, structure),
|
|
15
|
+
checked(values, 2, structure),
|
|
16
|
+
checked(values, 3, structure)
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
const clamp = (value) => Math.max(0, Math.min(1, value));
|
|
20
|
+
//#endregion
|
|
21
|
+
export { REVERSE_LUT, checked, clamp, color4, refuse };
|
package/dist/deck-pdf.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { RenderRefusedError } from "./errors.js";
|
|
2
|
+
import { pdfPixelBox } from "./pdf-geometry.js";
|
|
3
|
+
import { composeImposedPdf, drawClippedPage, embedOnePage } from "./pdf-compose.js";
|
|
4
|
+
import { Imposition, fitPage, impose, pagePlacementLayout } from "@segnavia/format";
|
|
5
|
+
import { newNonce } from "@segnavia/frame/bridge";
|
|
6
|
+
import { htmlSlideDocument, notesLayoutGeometry, printableDeck } from "@segnavia/frame/print-document";
|
|
7
|
+
import { PDFDocument } from "pdf-lib";
|
|
8
|
+
//#region src/deck-pdf.ts
|
|
9
|
+
function nameSlideError(error, id) {
|
|
10
|
+
if (error instanceof Error) {
|
|
11
|
+
error.message = `Slide "${id}" could not be rendered: ${error.message}`;
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
throw new Error(`Slide "${id}" could not be rendered: ${String(error)}`);
|
|
15
|
+
}
|
|
16
|
+
async function regularDeckPdf(deck, opts, services) {
|
|
17
|
+
const htmlSlides = deck.slides.flatMap((slide, index) => slide.content.kind === "html" ? [{
|
|
18
|
+
index,
|
|
19
|
+
id: slide.id,
|
|
20
|
+
html: slide.content.html
|
|
21
|
+
}] : []);
|
|
22
|
+
const css = `${services.fontFaces(opts)}${opts.css ?? ""}`;
|
|
23
|
+
const notesGeometry = opts.layout === "notes" ? notesLayoutGeometry(deck.size, opts.notesPaper) : void 0;
|
|
24
|
+
const [printable, rendered] = await Promise.all([services.renderPdf({
|
|
25
|
+
viewport: notesGeometry ? {
|
|
26
|
+
width: Math.round(notesGeometry.sheet.width),
|
|
27
|
+
height: Math.round(notesGeometry.sheet.height)
|
|
28
|
+
} : deck.size,
|
|
29
|
+
scripts: deck.scripts === "sandboxed",
|
|
30
|
+
html: printableDeck(deck, {
|
|
31
|
+
...opts,
|
|
32
|
+
css,
|
|
33
|
+
omitHtmlSlideFrames: true
|
|
34
|
+
}),
|
|
35
|
+
boundMs: opts.boundMs,
|
|
36
|
+
beforeOutput: opts.beforeOutput
|
|
37
|
+
}, {
|
|
38
|
+
preferCSSPageSize: true,
|
|
39
|
+
printBackground: true
|
|
40
|
+
}), Promise.all(htmlSlides.map(async ({ index, id, html }) => {
|
|
41
|
+
try {
|
|
42
|
+
return {
|
|
43
|
+
index,
|
|
44
|
+
bytes: await services.renderPdf({
|
|
45
|
+
viewport: deck.size,
|
|
46
|
+
scripts: deck.scripts === "sandboxed",
|
|
47
|
+
html: htmlSlideDocument(deck, html, {
|
|
48
|
+
nonce: newNonce(),
|
|
49
|
+
css
|
|
50
|
+
}),
|
|
51
|
+
boundMs: opts.boundMs
|
|
52
|
+
}, {
|
|
53
|
+
width: `${deck.size.width}px`,
|
|
54
|
+
height: `${deck.size.height}px`,
|
|
55
|
+
printBackground: true,
|
|
56
|
+
pageRanges: "1",
|
|
57
|
+
margin: {
|
|
58
|
+
top: "0px",
|
|
59
|
+
right: "0px",
|
|
60
|
+
bottom: "0px",
|
|
61
|
+
left: "0px"
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
};
|
|
65
|
+
} catch (error) {
|
|
66
|
+
nameSlideError(error, id);
|
|
67
|
+
}
|
|
68
|
+
}))]);
|
|
69
|
+
const output = await PDFDocument.load(printable);
|
|
70
|
+
const pages = output.getPages();
|
|
71
|
+
const slideBox = notesGeometry?.slide ?? {
|
|
72
|
+
x: 0,
|
|
73
|
+
y: 0,
|
|
74
|
+
width: deck.size.width,
|
|
75
|
+
height: deck.size.height
|
|
76
|
+
};
|
|
77
|
+
const geometry = pdfPixelBox(slideBox, notesGeometry?.sheet.height ?? deck.size.height);
|
|
78
|
+
for (const item of rendered) {
|
|
79
|
+
const sheet = pages[item.index];
|
|
80
|
+
if (!sheet) throw new Error(`The print document has no sheet for slide "${deck.slides[item.index]?.id ?? item.index}".`);
|
|
81
|
+
const embedded = await embedOnePage(output, item.bytes, `The rendered slide "${deck.slides[item.index]?.id ?? item.index}"`);
|
|
82
|
+
drawClippedPage(sheet, embedded, geometry, geometry);
|
|
83
|
+
}
|
|
84
|
+
return services.finish(await output.save(), opts);
|
|
85
|
+
}
|
|
86
|
+
async function imposedDeckPdf(deck, imposition, opts, services) {
|
|
87
|
+
const css = `${services.fontFaces(opts)}${opts.css ?? ""}`;
|
|
88
|
+
const imposed = impose(imposition, deck.slides.length);
|
|
89
|
+
const rendered = await Promise.all(deck.slides.map(async (slide, index) => {
|
|
90
|
+
const single = {
|
|
91
|
+
...deck,
|
|
92
|
+
slides: [slide]
|
|
93
|
+
};
|
|
94
|
+
try {
|
|
95
|
+
const html = slide.content.kind === "html" ? htmlSlideDocument(deck, slide.content.html, {
|
|
96
|
+
nonce: newNonce(),
|
|
97
|
+
css
|
|
98
|
+
}) : printableDeck(single, {
|
|
99
|
+
...opts,
|
|
100
|
+
layout: "slides",
|
|
101
|
+
imposition: void 0,
|
|
102
|
+
css
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
index,
|
|
106
|
+
bytes: await services.renderPdf({
|
|
107
|
+
viewport: deck.size,
|
|
108
|
+
scripts: deck.scripts === "sandboxed",
|
|
109
|
+
html,
|
|
110
|
+
boundMs: opts.boundMs
|
|
111
|
+
}, {
|
|
112
|
+
width: `${deck.size.width}px`,
|
|
113
|
+
height: `${deck.size.height}px`,
|
|
114
|
+
printBackground: true,
|
|
115
|
+
pageRanges: "1",
|
|
116
|
+
margin: {
|
|
117
|
+
top: "0px",
|
|
118
|
+
right: "0px",
|
|
119
|
+
bottom: "0px",
|
|
120
|
+
left: "0px"
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
};
|
|
124
|
+
} catch (error) {
|
|
125
|
+
nameSlideError(error, slide.id);
|
|
126
|
+
}
|
|
127
|
+
}));
|
|
128
|
+
const placements = /* @__PURE__ */ new Map();
|
|
129
|
+
for (const placement of imposed.placements) {
|
|
130
|
+
const fit = fitPage({ size: deck.size }, placement, imposition.fit);
|
|
131
|
+
const group = placements.get(placement.page) ?? [];
|
|
132
|
+
group.push({
|
|
133
|
+
placement,
|
|
134
|
+
layout: pagePlacementLayout(deck.size, placement, fit)
|
|
135
|
+
});
|
|
136
|
+
placements.set(placement.page, group);
|
|
137
|
+
}
|
|
138
|
+
const pieces = rendered.map((item) => ({
|
|
139
|
+
key: String(item.index),
|
|
140
|
+
name: `The rendered slide "${deck.slides[item.index]?.id ?? item.index}"`,
|
|
141
|
+
bytes: item.bytes,
|
|
142
|
+
placements: placements.get(item.index) ?? []
|
|
143
|
+
}));
|
|
144
|
+
return services.finish(await composeImposedPdf(imposed, pieces), opts);
|
|
145
|
+
}
|
|
146
|
+
async function renderDeckPdf(deck, opts, services) {
|
|
147
|
+
if (!opts.imposition) return regularDeckPdf(deck, opts, services);
|
|
148
|
+
if (opts.layout === "notes") throw new RenderRefusedError("layout: 'notes' and imposition cannot be combined. Remove either layout: 'notes' or imposition to clear this refusal.");
|
|
149
|
+
return imposedDeckPdf(deck, Imposition.parse(opts.imposition), opts, services);
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
export { renderDeckPdf };
|