@segnavia/render 0.5.0 → 0.6.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/browser-jpeg.js +42 -0
- package/dist/color/black-point.js +11 -8
- package/dist/color/engine.js +9 -2
- package/dist/color/interpolation.js +11 -1
- package/dist/color/profile.js +17 -7
- package/dist/color/tables.js +47 -13
- package/dist/color/types.js +4 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +19 -6
- package/dist/press/adapter.js +97 -42
- package/dist/press/color-policy.js +12 -3
- package/dist/press/content.js +28 -20
- package/dist/press/image.js +327 -0
- package/dist/press/policy.js +27 -10
- package/dist/press/report.js +24 -13
- package/dist/press/sample-shading.js +9 -19
- package/dist/press/types.d.ts +7 -2
- package/dist/press/visitor.js +32 -30
- package/package.json +4 -4
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region src/browser-jpeg.ts
|
|
2
|
+
function browserJpegDecoder(tab) {
|
|
3
|
+
return async (bytes) => {
|
|
4
|
+
const encoded = Buffer.from(bytes).toString("base64");
|
|
5
|
+
const decoded = await tab.evaluate(async (base64) => {
|
|
6
|
+
const binary = atob(base64);
|
|
7
|
+
const source = Uint8Array.from(binary, (value) => value.charCodeAt(0));
|
|
8
|
+
const bitmap = await createImageBitmap(new Blob([source], { type: "image/jpeg" }), {
|
|
9
|
+
colorSpaceConversion: "none",
|
|
10
|
+
premultiplyAlpha: "none"
|
|
11
|
+
});
|
|
12
|
+
try {
|
|
13
|
+
const context = new OffscreenCanvas(bitmap.width, bitmap.height).getContext("2d");
|
|
14
|
+
if (!context) throw new Error("Chromium could not create a 2D canvas for JPEG decoding");
|
|
15
|
+
context.drawImage(bitmap, 0, 0);
|
|
16
|
+
const rgba = context.getImageData(0, 0, bitmap.width, bitmap.height).data;
|
|
17
|
+
const rgb = new Uint8Array(bitmap.width * bitmap.height * 3);
|
|
18
|
+
for (let pixel = 0; pixel < bitmap.width * bitmap.height; pixel++) {
|
|
19
|
+
rgb[pixel * 3] = rgba[pixel * 4] ?? 0;
|
|
20
|
+
rgb[pixel * 3 + 1] = rgba[pixel * 4 + 1] ?? 0;
|
|
21
|
+
rgb[pixel * 3 + 2] = rgba[pixel * 4 + 2] ?? 0;
|
|
22
|
+
}
|
|
23
|
+
let value = "";
|
|
24
|
+
for (let offset = 0; offset < rgb.length; offset += 32768) value += String.fromCharCode(...rgb.subarray(offset, Math.min(rgb.length, offset + 32768)));
|
|
25
|
+
return {
|
|
26
|
+
width: bitmap.width,
|
|
27
|
+
height: bitmap.height,
|
|
28
|
+
rgb: btoa(value)
|
|
29
|
+
};
|
|
30
|
+
} finally {
|
|
31
|
+
bitmap.close();
|
|
32
|
+
}
|
|
33
|
+
}, encoded);
|
|
34
|
+
return {
|
|
35
|
+
width: decoded.width,
|
|
36
|
+
height: decoded.height,
|
|
37
|
+
rgb: new Uint8Array(Buffer.from(decoded.rgb, "base64"))
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { browserJpegDecoder };
|
|
@@ -2,7 +2,7 @@ import { checked } from "./types.js";
|
|
|
2
2
|
import { D50, labToXyz, matrix3, tableDecode, tableEncode, tryInvert3, xyzToLab } from "./spaces.js";
|
|
3
3
|
//#region src/color/black-point.ts
|
|
4
4
|
function detectSourceBlackPoint(colorSpace, pcs, major, relativeA2b, perceptualB2a) {
|
|
5
|
-
if (colorSpace !== "CMYK") return [
|
|
5
|
+
if (colorSpace !== "CMYK" && colorSpace !== "GRAY") return [
|
|
6
6
|
0,
|
|
7
7
|
0,
|
|
8
8
|
0
|
|
@@ -19,11 +19,12 @@ function detectSourceBlackPoint(colorSpace, pcs, major, relativeA2b, perceptualB
|
|
|
19
19
|
checked(result, 2, "black-point A2B result")
|
|
20
20
|
]);
|
|
21
21
|
const lab = pcs === "Lab " ? returned : xyzToLab(returned);
|
|
22
|
-
|
|
22
|
+
const black = [
|
|
23
23
|
Math.min(50, lab[0]),
|
|
24
24
|
0,
|
|
25
25
|
0
|
|
26
26
|
];
|
|
27
|
+
return pcs === "Lab " ? black : labToXyz(black);
|
|
27
28
|
}
|
|
28
29
|
function quadraticRoot(x, y) {
|
|
29
30
|
if (x.length < 4) return 0;
|
|
@@ -61,14 +62,15 @@ function quadraticRoot(x, y) {
|
|
|
61
62
|
return discriminant <= 0 ? 0 : Math.max(0, Math.min(50, (-b + Math.sqrt(discriminant)) / (2 * a)));
|
|
62
63
|
}
|
|
63
64
|
function destinationBlackPoint(colorSpace, pcs, major, relativeA2b, relativeB2a, sourceBlack) {
|
|
64
|
-
if (colorSpace !== "CMYK") return [...sourceBlack];
|
|
65
|
+
if (colorSpace !== "CMYK" && colorSpace !== "GRAY") return [...sourceBlack];
|
|
66
|
+
const sourceLab = pcs === "Lab " ? sourceBlack : xyzToLab(sourceBlack);
|
|
65
67
|
const inputRamp = [], outputRamp = [];
|
|
66
68
|
for (let index = 0; index < 256; index++) {
|
|
67
69
|
const lightness = index * 100 / 255;
|
|
68
70
|
const lab = [
|
|
69
71
|
lightness,
|
|
70
|
-
Math.max(-50, Math.min(50,
|
|
71
|
-
Math.max(-50, Math.min(50,
|
|
72
|
+
Math.max(-50, Math.min(50, sourceLab[1])),
|
|
73
|
+
Math.max(-50, Math.min(50, sourceLab[2]))
|
|
72
74
|
];
|
|
73
75
|
const value = pcs === "Lab " ? lab : labToXyz(lab);
|
|
74
76
|
const device = relativeB2a.run(tableEncode(pcs, relativeB2a.encoding, major, value, false));
|
|
@@ -97,11 +99,12 @@ function destinationBlackPoint(colorSpace, pcs, major, relativeA2b, relativeB2a,
|
|
|
97
99
|
y.push(normalized);
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
|
-
|
|
102
|
+
const result = [
|
|
101
103
|
x.length < 3 ? 0 : quadraticRoot(x, y),
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
sourceLab[1],
|
|
105
|
+
sourceLab[2]
|
|
104
106
|
];
|
|
107
|
+
return pcs === "Lab " ? result : labToXyz(result);
|
|
105
108
|
}
|
|
106
109
|
function compensateBlackPoint(profile, lab, reverse) {
|
|
107
110
|
const point = reverse ? profile.sourceBlackPoint : profile.destinationBlackPoint;
|
package/dist/color/engine.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { clamp, color4, refuse } from "./types.js";
|
|
1
|
+
import { clamp, color1, color4, refuse } from "./types.js";
|
|
2
2
|
import { labToXyz, srgbToLab, tableEncode } from "./spaces.js";
|
|
3
3
|
import { compensateBlackPoint } from "./black-point.js";
|
|
4
4
|
//#region src/color/engine.ts
|
|
@@ -14,5 +14,12 @@ function toPress(profile, rgb) {
|
|
|
14
14
|
clamp(black)
|
|
15
15
|
];
|
|
16
16
|
}
|
|
17
|
+
function toGray(profile, rgb) {
|
|
18
|
+
if (profile.colorSpace !== "GRAY") refuse(`destination color space ${profile.colorSpace}`, "GRAY");
|
|
19
|
+
const lab = compensateBlackPoint(profile, srgbToLab(rgb), false);
|
|
20
|
+
const pcs = profile.pcs === "Lab " ? lab : labToXyz(lab);
|
|
21
|
+
const [gray] = color1(profile.b2a.run(tableEncode(profile.pcs, profile.b2a.encoding, profile.majorVersion, pcs, true)), "B2A result");
|
|
22
|
+
return [clamp(gray)];
|
|
23
|
+
}
|
|
17
24
|
//#endregion
|
|
18
|
-
export { toPress };
|
|
25
|
+
export { toGray, toPress };
|
|
@@ -34,6 +34,16 @@ function multilinear(grid, points, outputs, input) {
|
|
|
34
34
|
}
|
|
35
35
|
return result;
|
|
36
36
|
}
|
|
37
|
+
function linearGrid(grid, points, outputs, input) {
|
|
38
|
+
const count = checked(points, 0, "CLUT dimensions");
|
|
39
|
+
const position = clamp(checked(input, 0, "CLUT input")) * (count - 1);
|
|
40
|
+
const low = Math.min(count - 2, Math.floor(position));
|
|
41
|
+
const fraction = position - low;
|
|
42
|
+
return Array.from({ length: outputs }, (_, channel) => {
|
|
43
|
+
const lower = grid.get(low * outputs + channel);
|
|
44
|
+
return lower + (grid.get((low + 1) * outputs + channel) - lower) * fraction;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
37
47
|
function tetrahedral4(grid, points, outputs, input) {
|
|
38
48
|
const { base, fractions, stride } = layout(points, outputs, input);
|
|
39
49
|
const order = [
|
|
@@ -71,4 +81,4 @@ function matrixInput(values) {
|
|
|
71
81
|
];
|
|
72
82
|
}
|
|
73
83
|
//#endregion
|
|
74
|
-
export { interpolate, matrixInput, multilinear, tetrahedral4 };
|
|
84
|
+
export { interpolate, linearGrid, matrixInput, multilinear, tetrahedral4 };
|
package/dist/color/profile.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { REVERSE_LUT, checked, refuse } from "./types.js";
|
|
2
2
|
import { destinationBlackPoint, detectSourceBlackPoint } from "./black-point.js";
|
|
3
3
|
import { fixed, need, signature, u32 } from "./binary.js";
|
|
4
|
-
import { matrixTables, parseMab, parseMft } from "./tables.js";
|
|
4
|
+
import { grayTables, matrixTables, parseMab, parseMft } from "./tables.js";
|
|
5
5
|
//#region src/color/profile.ts
|
|
6
6
|
const MAX_PROFILE_BYTES = 33554432;
|
|
7
7
|
function header(bytes, view, bounds) {
|
|
@@ -12,7 +12,7 @@ function header(bytes, view, bounds) {
|
|
|
12
12
|
const profileClass = signature(bytes, 12, bounds);
|
|
13
13
|
if (profileClass !== "prtr" && profileClass !== "mntr") refuse(`profile class ${JSON.stringify(profileClass)}`, "prtr or mntr");
|
|
14
14
|
const colorSpace = signature(bytes, 16, bounds);
|
|
15
|
-
if (colorSpace !== "CMYK" && colorSpace !== "RGB ") refuse(`color space ${JSON.stringify(colorSpace)}`, "CMYK or RGB");
|
|
15
|
+
if (colorSpace !== "CMYK" && colorSpace !== "GRAY" && colorSpace !== "RGB ") refuse(`color space ${JSON.stringify(colorSpace)}`, "CMYK, GRAY, or RGB");
|
|
16
16
|
const pcs = signature(bytes, 20, bounds);
|
|
17
17
|
if (pcs !== "Lab " && pcs !== "XYZ ") refuse(`PCS ${JSON.stringify(pcs)}`, "Lab or XYZ");
|
|
18
18
|
u32(view, 0, bounds);
|
|
@@ -55,6 +55,13 @@ function table(bytes, view, all, names, inputs, outputs) {
|
|
|
55
55
|
if (!record) throw new Error(`ICC profile is missing ${name} after its tag set was validated`);
|
|
56
56
|
return recordTable(bytes, view, record, inputs, outputs);
|
|
57
57
|
}
|
|
58
|
+
function optionalTable(bytes, view, all, names, inputs, outputs) {
|
|
59
|
+
const name = names.find((candidate) => all[candidate]);
|
|
60
|
+
if (!name) return void 0;
|
|
61
|
+
const record = all[name];
|
|
62
|
+
if (!record) throw new Error(`ICC profile is missing ${name} after its tag set was validated`);
|
|
63
|
+
return recordTable(bytes, view, record, inputs, outputs);
|
|
64
|
+
}
|
|
58
65
|
function readProfile(bytes) {
|
|
59
66
|
if (bytes.length > 33554432) refuse(`profile length ${bytes.length}`, `at most ${MAX_PROFILE_BYTES} bytes`);
|
|
60
67
|
const bounds = {
|
|
@@ -63,12 +70,15 @@ function readProfile(bytes) {
|
|
|
63
70
|
}, view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
64
71
|
const parsed = header(bytes, view, bounds), all = records(bytes, view, bounds);
|
|
65
72
|
if (all.wtpt?.type !== "XYZ ") refuse(`white point tag type ${JSON.stringify(all.wtpt?.type ?? "missing")}`, "XYZ ");
|
|
66
|
-
const channels = parsed.colorSpace === "CMYK" ? 4 : 3;
|
|
73
|
+
const channels = parsed.colorSpace === "CMYK" ? 4 : parsed.colorSpace === "GRAY" ? 1 : 3;
|
|
67
74
|
const matrix = parsed.colorSpace === "RGB " ? matrixTables(bytes, view, all) : void 0;
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
const
|
|
75
|
+
const trcRecord = all.kTRC;
|
|
76
|
+
const gray = parsed.colorSpace === "GRAY" && trcRecord ? grayTables(bytes, view, trcRecord, parsed.pcs) : void 0;
|
|
77
|
+
const select = (names, inputs, outputs, fallback) => optionalTable(bytes, view, all, names, inputs, outputs) ?? fallback ?? table(bytes, view, all, names, inputs, outputs);
|
|
78
|
+
const a2b = matrix?.a2b ?? select(["A2B1", "A2B0"], channels, 3, gray?.a2b);
|
|
79
|
+
const b2a = matrix?.b2a ?? select(["B2A1", "B2A0"], 3, channels, gray?.b2a);
|
|
80
|
+
const perceptualA2b = matrix?.a2b ?? select(["A2B0", "A2B1"], channels, 3, gray?.a2b);
|
|
81
|
+
const perceptualB2a = matrix?.b2a ?? select(["B2A0", "B2A1"], 3, channels, gray?.b2a);
|
|
72
82
|
const gamutRecord = all.gamt, gamut = gamutRecord ? recordTable(bytes, view, gamutRecord, 3, 1) : void 0;
|
|
73
83
|
const white = all.wtpt;
|
|
74
84
|
if (!white) throw new Error("ICC profile is missing wtpt after its tag set was validated");
|
package/dist/color/tables.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { REVERSE_LUT, checked, clamp, refuse } from "./types.js";
|
|
2
2
|
import { invert3, matrix3 } from "./spaces.js";
|
|
3
3
|
import { align4, fixed, need, signature, u16, u32 } from "./binary.js";
|
|
4
|
-
import { interpolate, matrixInput, multilinear, tetrahedral4 } from "./interpolation.js";
|
|
4
|
+
import { interpolate, linearGrid, matrixInput, multilinear, tetrahedral4 } from "./interpolation.js";
|
|
5
5
|
//#region src/color/tables.ts
|
|
6
6
|
function packed(bytes, view, offset, length, width, bounds) {
|
|
7
7
|
need(offset, length * width, bounds);
|
|
@@ -73,6 +73,16 @@ function parseCurve(bytes, view, offset, bounds) {
|
|
|
73
73
|
size: 12 + count * 4
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
|
+
function invertCurve(curve, target) {
|
|
77
|
+
let low = 0, high = 1;
|
|
78
|
+
const descending = curve(0) > curve(1);
|
|
79
|
+
for (let index = 0; index < 24; index++) {
|
|
80
|
+
const middle = (low + high) / 2;
|
|
81
|
+
if (descending ? curve(middle) > target : curve(middle) < target) low = middle;
|
|
82
|
+
else high = middle;
|
|
83
|
+
}
|
|
84
|
+
return (low + high) / 2;
|
|
85
|
+
}
|
|
76
86
|
function curvesAt(bytes, view, offset, count, bounds) {
|
|
77
87
|
const curves = [];
|
|
78
88
|
let cursor = offset;
|
|
@@ -132,7 +142,7 @@ function parseMft(bytes, view, record, type, inputs, outputs) {
|
|
|
132
142
|
];
|
|
133
143
|
current = matrix3(fixedMatrix, matrixInput(current));
|
|
134
144
|
}
|
|
135
|
-
current = inputs === 3 ? multilinear(grid, points, outputs, current) : tetrahedral4(grid, points, outputs, current);
|
|
145
|
+
current = inputs === 1 ? linearGrid(grid, points, outputs, current) : inputs === 3 ? multilinear(grid, points, outputs, current) : tetrahedral4(grid, points, outputs, current);
|
|
136
146
|
return current.map((value, channel) => interpolate(checked(outputTables, channel, `${type} output tables`), value));
|
|
137
147
|
}
|
|
138
148
|
};
|
|
@@ -179,7 +189,7 @@ function parseMab(bytes, view, record, type, inputs, outputs) {
|
|
|
179
189
|
2
|
|
180
190
|
].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
191
|
};
|
|
182
|
-
const clut = (values) => !grid ? [...values] : inputs === 3 ? multilinear(grid, points, outputs, values) : tetrahedral4(grid, points, outputs, values);
|
|
192
|
+
const clut = (values) => !grid ? [...values] : inputs === 1 ? linearGrid(grid, points, outputs, values) : inputs === 3 ? multilinear(grid, points, outputs, values) : tetrahedral4(grid, points, outputs, values);
|
|
183
193
|
return {
|
|
184
194
|
inputChannels: inputs,
|
|
185
195
|
outputChannels: outputs,
|
|
@@ -227,15 +237,6 @@ function matrixTables(bytes, view, records) {
|
|
|
227
237
|
checked(checked(columns, 2, "colorants"), 2, "blue")
|
|
228
238
|
];
|
|
229
239
|
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
240
|
return {
|
|
240
241
|
a2b: {
|
|
241
242
|
inputChannels: 3,
|
|
@@ -251,5 +252,38 @@ function matrixTables(bytes, view, records) {
|
|
|
251
252
|
}
|
|
252
253
|
};
|
|
253
254
|
}
|
|
255
|
+
function grayTables(bytes, view, record, pcs) {
|
|
256
|
+
const curve = parseCurve(bytes, view, record.offset, record).curve;
|
|
257
|
+
const scale = 32768 / 65535;
|
|
258
|
+
return {
|
|
259
|
+
a2b: {
|
|
260
|
+
inputChannels: 1,
|
|
261
|
+
outputChannels: 3,
|
|
262
|
+
encoding: "mAB ",
|
|
263
|
+
run(input) {
|
|
264
|
+
const luminance = curve(checked(input, 0, "gray input"));
|
|
265
|
+
if (pcs === "XYZ ") return [
|
|
266
|
+
luminance * .9642 * scale,
|
|
267
|
+
luminance * scale,
|
|
268
|
+
luminance * .8249 * scale
|
|
269
|
+
];
|
|
270
|
+
return [
|
|
271
|
+
luminance,
|
|
272
|
+
128 / 255,
|
|
273
|
+
128 / 255
|
|
274
|
+
];
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
b2a: {
|
|
278
|
+
inputChannels: 3,
|
|
279
|
+
outputChannels: 1,
|
|
280
|
+
encoding: REVERSE_LUT,
|
|
281
|
+
run(input) {
|
|
282
|
+
const luminance = pcs === "XYZ " ? checked(input, 1, "gray PCS input") / scale : checked(input, 0, "gray PCS input");
|
|
283
|
+
return [invertCurve(curve, luminance)];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
}
|
|
254
288
|
//#endregion
|
|
255
|
-
export { matrixTables, parseCurve, parseMab, parseMft };
|
|
289
|
+
export { grayTables, invertCurve, matrixTables, parseCurve, parseMab, parseMft };
|
package/dist/color/types.js
CHANGED
|
@@ -8,6 +8,9 @@ function checked(values, index, structure) {
|
|
|
8
8
|
if (value === void 0) throw new Error(`${structure} index ${index} is outside length ${values.length}`);
|
|
9
9
|
return value;
|
|
10
10
|
}
|
|
11
|
+
function color1(values, structure) {
|
|
12
|
+
return [checked(values, 0, structure)];
|
|
13
|
+
}
|
|
11
14
|
function color4(values, structure) {
|
|
12
15
|
return [
|
|
13
16
|
checked(values, 0, structure),
|
|
@@ -18,4 +21,4 @@ function color4(values, structure) {
|
|
|
18
21
|
}
|
|
19
22
|
const clamp = (value) => Math.max(0, Math.min(1, value));
|
|
20
23
|
//#endregion
|
|
21
|
-
export { REVERSE_LUT, checked, clamp, color4, refuse };
|
|
24
|
+
export { REVERSE_LUT, checked, clamp, color1, color4, refuse };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EffectsReport } from "./effects/types.js";
|
|
2
|
-
import { PressImage, PressReport } from "./press/types.js";
|
|
2
|
+
import { PressDestination, PressImage, PressReport } from "./press/types.js";
|
|
3
3
|
import { Font, ImageOptions, RenderLimits, RenderOptions } from "./options.js";
|
|
4
4
|
import { RenderRefusedError, RendererUnavailableError } from "./errors.js";
|
|
5
5
|
import { BookInput, Deck, ImpositionInput, PageInput } from "@segnavia/format";
|
|
@@ -33,4 +33,4 @@ export declare function fontFaces(fonts?: Font[]): string;
|
|
|
33
33
|
export declare function captureImage(tab: Page$1, options: Pick<ImageOptions, 'format' | 'quality' | 'limits'>, fullPage: boolean): Promise<Uint8Array>;
|
|
34
34
|
export declare function openRenderer(options: RendererOptions): Promise<Renderer>;
|
|
35
35
|
//#endregion
|
|
36
|
-
export { type EffectsReport, type Font, type ImageOptions, type PressImage, type PressReport, type RenderLimits, type RenderOptions, RenderRefusedError, RendererUnavailableError };
|
|
36
|
+
export { type EffectsReport, type Font, type ImageOptions, type PressDestination, type PressImage, type PressReport, type RenderLimits, type RenderOptions, RenderRefusedError, RendererUnavailableError };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RenderRefusedError, RendererUnavailableError } from "./errors.js";
|
|
2
2
|
import { renderBrowserBookPdf } from "./book-render.js";
|
|
3
|
+
import { browserJpegDecoder } from "./browser-jpeg.js";
|
|
3
4
|
import { containFixedPageOverflow } from "./overflow-containment.js";
|
|
4
5
|
import { renderDeckPdf } from "./deck-pdf.js";
|
|
5
6
|
import { assertNeutralTolerance } from "./press/color-policy.js";
|
|
@@ -55,7 +56,7 @@ async function enforce(pdf, limits) {
|
|
|
55
56
|
}
|
|
56
57
|
return pdf;
|
|
57
58
|
}
|
|
58
|
-
async function finishPdf(pdf, options) {
|
|
59
|
+
async function finishPdf(pdf, options, decodeJpeg) {
|
|
59
60
|
if (options.limits?.maxBytes !== void 0 && pdf.byteLength > options.limits.maxBytes) throw new RenderRefusedError(`The PDF is ${pdf.byteLength} bytes; the limit is ${options.limits.maxBytes}.`);
|
|
60
61
|
let output = pdf;
|
|
61
62
|
if (options.effects !== false) {
|
|
@@ -66,7 +67,7 @@ async function finishPdf(pdf, options) {
|
|
|
66
67
|
if (options.press) {
|
|
67
68
|
const neutralTolerance = options.press.neutralTolerance ?? 3;
|
|
68
69
|
assertNeutralTolerance(neutralTolerance);
|
|
69
|
-
const converted = await rewritePressPdf(output, options.press.profile, neutralTolerance);
|
|
70
|
+
const converted = await rewritePressPdf(output, options.press.profile, neutralTolerance, decodeJpeg);
|
|
70
71
|
output = converted.bytes;
|
|
71
72
|
options.press.report?.(converted.report);
|
|
72
73
|
}
|
|
@@ -205,6 +206,18 @@ async function openRenderer(options) {
|
|
|
205
206
|
reportHeight: false
|
|
206
207
|
});
|
|
207
208
|
}
|
|
209
|
+
async function finishWithBrowser(pdf, options) {
|
|
210
|
+
if (!options.press || new TextDecoder().decode(options.press.profile.subarray(16, 20)) !== "GRAY") return finishPdf(pdf, options);
|
|
211
|
+
return withTab({
|
|
212
|
+
viewport: {
|
|
213
|
+
width: 1,
|
|
214
|
+
height: 1
|
|
215
|
+
},
|
|
216
|
+
scripts: true,
|
|
217
|
+
html: "<!doctype html>",
|
|
218
|
+
waitForReady: false
|
|
219
|
+
}, (tab) => finishPdf(pdf, options, browserJpegDecoder(tab)));
|
|
220
|
+
}
|
|
208
221
|
function pageViewport(page, width, printing = false) {
|
|
209
222
|
if (page.print) return printViewport(page.print, printing);
|
|
210
223
|
if (page.size === "fluid") return {
|
|
@@ -275,11 +288,11 @@ async function openRenderer(options) {
|
|
|
275
288
|
return renderBrowserBookPdf(input, opts, {
|
|
276
289
|
render: (setup, pdf) => withTab(setup, (tab) => tab.pdf(pdf)),
|
|
277
290
|
fontFaces: (options) => fontFaces(options.fonts),
|
|
278
|
-
finish:
|
|
291
|
+
finish: finishWithBrowser
|
|
279
292
|
});
|
|
280
293
|
},
|
|
281
294
|
async pagePdf(input, opts = {}) {
|
|
282
|
-
return
|
|
295
|
+
return finishWithBrowser(await renderPagePdf(Page.parse(input), opts), opts);
|
|
283
296
|
},
|
|
284
297
|
async pageImage(input, opts = {}) {
|
|
285
298
|
const page = Page.parse(input);
|
|
@@ -303,7 +316,7 @@ async function openRenderer(options) {
|
|
|
303
316
|
pagePdf: renderPagePdf,
|
|
304
317
|
piecePdf: renderPiecePdf,
|
|
305
318
|
fontFaces: (options) => fontFaces(options.fonts),
|
|
306
|
-
finish:
|
|
319
|
+
finish: finishWithBrowser
|
|
307
320
|
});
|
|
308
321
|
},
|
|
309
322
|
deckPdf(deck, opts = {}) {
|
|
@@ -313,7 +326,7 @@ async function openRenderer(options) {
|
|
|
313
326
|
waitForReady: false
|
|
314
327
|
}, (tab) => tab.pdf(pdf)),
|
|
315
328
|
fontFaces: (options) => fontFaces(options.fonts),
|
|
316
|
-
finish:
|
|
329
|
+
finish: finishWithBrowser
|
|
317
330
|
});
|
|
318
331
|
},
|
|
319
332
|
async slideImage(deck, index, opts = {}) {
|