@segnavia/render 0.2.0 → 0.4.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/profile.js +1 -1
- package/dist/color/tables.js +1 -1
- package/dist/deck-pdf.js +4 -2
- 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/index.d.ts +5 -1
- package/dist/index.js +18 -6
- package/dist/overflow-containment.js +7 -0
- package/dist/pages-pdf.js +1 -0
- package/dist/press/adapter.js +141 -11
- 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/content.js +24 -1
- package/dist/press/pattern-bounds.js +275 -0
- package/dist/press/pattern-path.js +102 -0
- package/dist/press/policy.js +7 -1
- package/dist/press/report.js +13 -3
- package/dist/press/sample-shading.js +266 -0
- package/dist/press/types.d.ts +2 -0
- package/dist/press/visitor.js +55 -32
- package/package.json +3 -3
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { RenderRefusedError } from "../errors.js";
|
|
2
|
+
import { IDENTITY_MATRIX, applyAffine, invertAffine, multiplyAffine } from "./affine.js";
|
|
3
|
+
import { MAX_DOCUMENT_CONTENT_BYTES, arrayNumbers, decodeContent, rawStreamOf } from "./pdf-objects.js";
|
|
4
|
+
import { evaluateCalculator, parseCalculator, staticOperatorCount } from "./calculator.js";
|
|
5
|
+
import { PDFArray, PDFName, PDFNumber, PDFRef } from "pdf-lib";
|
|
6
|
+
import { deflateSync } from "node:zlib";
|
|
7
|
+
//#region src/press/sample-shading.ts
|
|
8
|
+
const SHADING_SAMPLE_SPACING_PT = .5;
|
|
9
|
+
const MAX_SHADING_SAMPLES = 1048576;
|
|
10
|
+
const MAX_DOCUMENT_SHADING_SAMPLES = 4 * MAX_SHADING_SAMPLES;
|
|
11
|
+
const MAX_DOCUMENT_CALCULATOR_OPERATIONS = 8 * MAX_SHADING_SAMPLES * 52;
|
|
12
|
+
const MAX_SAMPLING_COLOR_CACHE = 4096;
|
|
13
|
+
const SAMPLE_CHANNELS = 4;
|
|
14
|
+
const SAMPLE_BYTES = 1;
|
|
15
|
+
function refuse(found, supported) {
|
|
16
|
+
throw new RenderRefusedError(`The PDF has unsupported ${found}; supported: ${supported}.`);
|
|
17
|
+
}
|
|
18
|
+
function clamp01(value) {
|
|
19
|
+
if (value < 0) return 0;
|
|
20
|
+
if (value > 1) return 1;
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
function requireFinite(values, found) {
|
|
24
|
+
if (values.some((value) => !Number.isFinite(value))) refuse(found, "finite numbers");
|
|
25
|
+
}
|
|
26
|
+
function shadingNumbers(dictionary, key, count) {
|
|
27
|
+
const expected = count === 4 ? "four finite numbers" : "six finite numbers";
|
|
28
|
+
const array = dictionary.lookupMaybe(PDFName.of(key), PDFArray);
|
|
29
|
+
if (!array || array.size() !== count) refuse(`shading ${key}`, expected);
|
|
30
|
+
const values = Array.from({ length: count }, (_, index) => array.lookupMaybe(index, PDFNumber)?.asNumber());
|
|
31
|
+
if (values.some((value) => value === void 0 || !Number.isFinite(value))) refuse(`shading ${key}`, expected);
|
|
32
|
+
return values;
|
|
33
|
+
}
|
|
34
|
+
function shadingDomainOf(shading) {
|
|
35
|
+
return shadingNumbers(shading, "Domain", 4);
|
|
36
|
+
}
|
|
37
|
+
function axisSamples(span, columnA, columnB) {
|
|
38
|
+
if (![
|
|
39
|
+
span,
|
|
40
|
+
columnA,
|
|
41
|
+
columnB
|
|
42
|
+
].every(Number.isFinite)) refuse("shading axis length", "a finite length");
|
|
43
|
+
const extent = Math.hypot(columnA, columnB) * Math.abs(span) / SHADING_SAMPLE_SPACING_PT;
|
|
44
|
+
if (!Number.isFinite(extent)) refuse("shading axis length", "a finite length");
|
|
45
|
+
return Math.min(MAX_SHADING_SAMPLES, Math.max(1, Math.ceil(extent)));
|
|
46
|
+
}
|
|
47
|
+
function packedRgb8(rgb) {
|
|
48
|
+
return Math.round(clamp01(rgb[0]) * 255) << 16 | Math.round(clamp01(rgb[1]) * 255) << 8 | Math.round(clamp01(rgb[2]) * 255);
|
|
49
|
+
}
|
|
50
|
+
function unpackRgb8(key) {
|
|
51
|
+
return [
|
|
52
|
+
(key >> 16) / 255,
|
|
53
|
+
(key >> 8 & 255) / 255,
|
|
54
|
+
(key & 255) / 255
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
function createSamplingColorConverter(convert) {
|
|
58
|
+
const cache = /* @__PURE__ */ new Map();
|
|
59
|
+
return {
|
|
60
|
+
size: () => cache.size,
|
|
61
|
+
convert: (rgb) => {
|
|
62
|
+
const key = packedRgb8(rgb);
|
|
63
|
+
const cached = cache.get(key);
|
|
64
|
+
if (cached) return cached;
|
|
65
|
+
const converted = convert(unpackRgb8(key));
|
|
66
|
+
if (cache.size < 4096) cache.set(key, converted);
|
|
67
|
+
return converted;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function sampleDomainForPainted(domain, matrix, painted) {
|
|
72
|
+
const original = [
|
|
73
|
+
domain[0] ?? 0,
|
|
74
|
+
domain[1] ?? 0,
|
|
75
|
+
domain[2] ?? 0,
|
|
76
|
+
domain[3] ?? 0
|
|
77
|
+
];
|
|
78
|
+
if (!painted || painted === "unbounded") return original;
|
|
79
|
+
const inverse = invertAffine(matrix);
|
|
80
|
+
if (!inverse) return original;
|
|
81
|
+
const corners = [
|
|
82
|
+
applyAffine(inverse, painted.x0, painted.y0),
|
|
83
|
+
applyAffine(inverse, painted.x1, painted.y0),
|
|
84
|
+
applyAffine(inverse, painted.x0, painted.y1),
|
|
85
|
+
applyAffine(inverse, painted.x1, painted.y1)
|
|
86
|
+
];
|
|
87
|
+
const xs = corners.map((point) => point.x);
|
|
88
|
+
const ys = corners.map((point) => point.y);
|
|
89
|
+
const x0 = Math.max(original[0] ?? 0, Math.min(...xs));
|
|
90
|
+
const x1 = Math.min(original[1] ?? 0, Math.max(...xs));
|
|
91
|
+
const y0 = Math.max(original[2] ?? 0, Math.min(...ys));
|
|
92
|
+
const y1 = Math.min(original[3] ?? 0, Math.max(...ys));
|
|
93
|
+
if (!(x0 < x1) || !(y0 < y1)) return original;
|
|
94
|
+
return [
|
|
95
|
+
x0,
|
|
96
|
+
x1,
|
|
97
|
+
y0,
|
|
98
|
+
y1
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
function unionSampleDomain(left, right) {
|
|
102
|
+
return [
|
|
103
|
+
Math.min(left[0] ?? 0, right[0] ?? 0),
|
|
104
|
+
Math.max(left[1] ?? 0, right[1] ?? 0),
|
|
105
|
+
Math.min(left[2] ?? 0, right[2] ?? 0),
|
|
106
|
+
Math.max(left[3] ?? 0, right[3] ?? 0)
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
function sampleDomainForUses(domain, uses) {
|
|
110
|
+
const original = [
|
|
111
|
+
domain[0] ?? 0,
|
|
112
|
+
domain[1] ?? 0,
|
|
113
|
+
domain[2] ?? 0,
|
|
114
|
+
domain[3] ?? 0
|
|
115
|
+
];
|
|
116
|
+
if (uses.length === 0) return original;
|
|
117
|
+
let union;
|
|
118
|
+
for (const use of uses) {
|
|
119
|
+
const next = sampleDomainForPainted(domain, use.matrix, use.painted);
|
|
120
|
+
if (!use.painted || use.painted === "unbounded") return original;
|
|
121
|
+
union = union ? unionSampleDomain(union, next) : next;
|
|
122
|
+
}
|
|
123
|
+
return union ?? original;
|
|
124
|
+
}
|
|
125
|
+
function matrixOf(dictionary) {
|
|
126
|
+
if (!dictionary?.has(PDFName.of("Matrix"))) return IDENTITY_MATRIX.slice();
|
|
127
|
+
return arrayNumbers(dictionary, "Matrix", 6);
|
|
128
|
+
}
|
|
129
|
+
function shadingDeviceMatrix(shading, pattern) {
|
|
130
|
+
const shadingMatrix = shading.has(PDFName.of("Matrix")) ? shadingNumbers(shading, "Matrix", 6) : IDENTITY_MATRIX.slice();
|
|
131
|
+
return pattern ? multiplyAffine(matrixOf(pattern), shadingMatrix) : shadingMatrix;
|
|
132
|
+
}
|
|
133
|
+
function capSampleGrid(n, m) {
|
|
134
|
+
if (n * m <= 1048576) return {
|
|
135
|
+
n,
|
|
136
|
+
m
|
|
137
|
+
};
|
|
138
|
+
const scale = Math.sqrt(MAX_SHADING_SAMPLES / (n * m));
|
|
139
|
+
let nextN = Math.max(1, Math.floor(n * scale));
|
|
140
|
+
let nextM = Math.max(1, Math.floor(m * scale));
|
|
141
|
+
while (nextN * nextM > MAX_SHADING_SAMPLES) if (nextN >= nextM && nextN > 1) nextN--;
|
|
142
|
+
else if (nextM > 1) nextM--;
|
|
143
|
+
else break;
|
|
144
|
+
return {
|
|
145
|
+
n: nextN,
|
|
146
|
+
m: nextM
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function shadingSampleGrid(domain, matrix) {
|
|
150
|
+
const x0 = domain[0] ?? 0;
|
|
151
|
+
const x1 = domain[1] ?? 0;
|
|
152
|
+
const y0 = domain[2] ?? 0;
|
|
153
|
+
const y1 = domain[3] ?? 0;
|
|
154
|
+
requireFinite([
|
|
155
|
+
x0,
|
|
156
|
+
x1,
|
|
157
|
+
y0,
|
|
158
|
+
y1
|
|
159
|
+
], "shading Domain");
|
|
160
|
+
const columnAx = matrix[0] ?? 1;
|
|
161
|
+
const columnAy = matrix[1] ?? 0;
|
|
162
|
+
const columnBx = matrix[2] ?? 0;
|
|
163
|
+
const columnBy = matrix[3] ?? 1;
|
|
164
|
+
requireFinite([
|
|
165
|
+
columnAx,
|
|
166
|
+
columnAy,
|
|
167
|
+
columnBx,
|
|
168
|
+
columnBy,
|
|
169
|
+
matrix[4] ?? 0,
|
|
170
|
+
matrix[5] ?? 0
|
|
171
|
+
], "shading Matrix");
|
|
172
|
+
return capSampleGrid(axisSamples(x1 - x0, columnAx, columnAy), axisSamples(y1 - y0, columnBx, columnBy));
|
|
173
|
+
}
|
|
174
|
+
function shadingSampleGridForUses(domain, matrices) {
|
|
175
|
+
if (matrices.length === 0) return shadingSampleGrid(domain, IDENTITY_MATRIX);
|
|
176
|
+
let n = 1;
|
|
177
|
+
let m = 1;
|
|
178
|
+
for (const matrix of matrices) {
|
|
179
|
+
const grid = shadingSampleGrid(domain, matrix);
|
|
180
|
+
n = Math.max(n, grid.n);
|
|
181
|
+
m = Math.max(m, grid.m);
|
|
182
|
+
}
|
|
183
|
+
return capSampleGrid(n, m);
|
|
184
|
+
}
|
|
185
|
+
function domainValue(min, max, index, size) {
|
|
186
|
+
if (size <= 1) return min;
|
|
187
|
+
return min + (max - min) * index / (size - 1);
|
|
188
|
+
}
|
|
189
|
+
function sampleShadingToCmyk(program, domain, size, convert) {
|
|
190
|
+
const x0 = domain[0] ?? 0;
|
|
191
|
+
const x1 = domain[1] ?? 0;
|
|
192
|
+
const y0 = domain[2] ?? 0;
|
|
193
|
+
const y1 = domain[3] ?? 0;
|
|
194
|
+
const samples = new Uint8Array(size.n * size.m * SAMPLE_CHANNELS * SAMPLE_BYTES);
|
|
195
|
+
const toCmyk = createSamplingColorConverter(convert).convert;
|
|
196
|
+
let offset = 0;
|
|
197
|
+
for (let j = 0; j < size.m; j++) {
|
|
198
|
+
const y = domainValue(y0, y1, j, size.m);
|
|
199
|
+
for (let i = 0; i < size.n; i++) {
|
|
200
|
+
const x = domainValue(x0, x1, i, size.n);
|
|
201
|
+
const rgb = evaluateCalculator(program, [x, y]);
|
|
202
|
+
if (rgb.length !== 3) refuse("shading function outputs", "3 numbers");
|
|
203
|
+
const cmyk = toCmyk([
|
|
204
|
+
clamp01(rgb[0] ?? 0),
|
|
205
|
+
clamp01(rgb[1] ?? 0),
|
|
206
|
+
clamp01(rgb[2] ?? 0)
|
|
207
|
+
]);
|
|
208
|
+
for (const channel of cmyk) {
|
|
209
|
+
samples[offset] = Math.round(clamp01(channel) * 255);
|
|
210
|
+
offset += SAMPLE_BYTES;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return samples;
|
|
215
|
+
}
|
|
216
|
+
function chargeShadingSamples(budget, samples, operations) {
|
|
217
|
+
const bytes = samples * SAMPLE_CHANNELS * SAMPLE_BYTES;
|
|
218
|
+
if (!Number.isFinite(samples) || samples < 0 || !Number.isFinite(bytes) || bytes < 0) refuse(`document shading samples ${samples}`, "a finite non-negative sample count and byte charge");
|
|
219
|
+
if (!Number.isFinite(operations) || operations < 0) refuse(`document calculator operations ${operations}`, "a finite non-negative operator count");
|
|
220
|
+
const totalSamples = (budget.shadingSamples ?? 0) + samples;
|
|
221
|
+
const totalBytes = budget.decodedBytes + bytes;
|
|
222
|
+
const totalOperations = (budget.calculatorOperations ?? 0) + operations;
|
|
223
|
+
if (!Number.isFinite(totalSamples) || totalSamples > 4194304) refuse(`document shading samples ${totalSamples}`, `at most ${MAX_DOCUMENT_SHADING_SAMPLES} samples`);
|
|
224
|
+
if (!Number.isFinite(totalOperations) || totalOperations > 436207616) refuse(`document calculator operations ${totalOperations}`, `at most ${MAX_DOCUMENT_CALCULATOR_OPERATIONS} operators`);
|
|
225
|
+
if (!Number.isFinite(totalBytes) || totalBytes > 134217728) refuse(`total decoded content length ${totalBytes}`, `at most ${MAX_DOCUMENT_CONTENT_BYTES} bytes`);
|
|
226
|
+
budget.shadingSamples = totalSamples;
|
|
227
|
+
budget.decodedBytes = totalBytes;
|
|
228
|
+
budget.calculatorOperations = totalOperations;
|
|
229
|
+
}
|
|
230
|
+
const resolved = (document, object) => object instanceof PDFRef ? document.context.lookup(object) : object;
|
|
231
|
+
function replaceType1Function(document, shading, pattern, convert, budget, painted, sampleDomain, sampleSize) {
|
|
232
|
+
const existing = shading.get(PDFName.of("Function"));
|
|
233
|
+
const stream = rawStreamOf(resolved(document, existing));
|
|
234
|
+
if (!stream) return refuse("shading FunctionType 4 stream", "a FunctionType 4 stream");
|
|
235
|
+
const shadingDomain = shadingDomainOf(shading);
|
|
236
|
+
const matrix = shadingDeviceMatrix(shading, pattern);
|
|
237
|
+
requireFinite(matrix, "shading Matrix");
|
|
238
|
+
const domain = sampleDomain ? [...sampleDomain] : sampleDomainForPainted(shadingDomain, matrix, painted);
|
|
239
|
+
const size = sampleSize ?? shadingSampleGrid(domain, matrix);
|
|
240
|
+
const program = parseCalculator(new TextDecoder("latin1").decode(decodeContent(stream, budget)));
|
|
241
|
+
const samples = size.n * size.m;
|
|
242
|
+
chargeShadingSamples(budget, samples, samples * staticOperatorCount(program));
|
|
243
|
+
const data = sampleShadingToCmyk(program, domain, size, convert);
|
|
244
|
+
const replacement = document.context.stream(deflateSync(data), {
|
|
245
|
+
FunctionType: 0,
|
|
246
|
+
Domain: domain,
|
|
247
|
+
Range: [
|
|
248
|
+
0,
|
|
249
|
+
1,
|
|
250
|
+
0,
|
|
251
|
+
1,
|
|
252
|
+
0,
|
|
253
|
+
1,
|
|
254
|
+
0,
|
|
255
|
+
1
|
|
256
|
+
],
|
|
257
|
+
Size: [size.n, size.m],
|
|
258
|
+
BitsPerSample: 8,
|
|
259
|
+
Order: 1,
|
|
260
|
+
Filter: "FlateDecode"
|
|
261
|
+
});
|
|
262
|
+
shading.set(PDFName.of("Function"), document.context.register(replacement));
|
|
263
|
+
return size;
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
export { MAX_DOCUMENT_CALCULATOR_OPERATIONS, MAX_DOCUMENT_SHADING_SAMPLES, MAX_SAMPLING_COLOR_CACHE, MAX_SHADING_SAMPLES, SHADING_SAMPLE_SPACING_PT, chargeShadingSamples, createSamplingColorConverter, domainValue, matrixOf, packedRgb8, replaceType1Function, sampleDomainForPainted, sampleDomainForUses, sampleShadingToCmyk, shadingDeviceMatrix, shadingDomainOf, shadingSampleGrid, shadingSampleGridForUses, unionSampleDomain, unpackRgb8 };
|
package/dist/press/types.d.ts
CHANGED
package/dist/press/visitor.js
CHANGED
|
@@ -6,29 +6,38 @@ const key = (name) => PDFName.of(name);
|
|
|
6
6
|
const resolved = (state, object) => object instanceof PDFRef ? state.document.context.lookup(object) : object;
|
|
7
7
|
const dictionary = (state, object) => dictionaryOf(resolved(state, object));
|
|
8
8
|
const stream = (state, object) => rawStreamOf(resolved(state, object));
|
|
9
|
-
function
|
|
9
|
+
function rememberRef(state, object) {
|
|
10
|
+
if (!(object instanceof PDFRef)) return false;
|
|
11
|
+
const label = object.toString();
|
|
12
|
+
if (state.references.has(label)) return true;
|
|
13
|
+
state.references.add(label);
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
function visitContent(state, object, replace, enclosing, replay = false, nestResources = true) {
|
|
10
17
|
if (!object) return;
|
|
11
|
-
|
|
12
|
-
const label = object.toString();
|
|
13
|
-
if (state.references.has(label)) return;
|
|
14
|
-
state.references.add(label);
|
|
15
|
-
}
|
|
18
|
+
const seenRef = rememberRef(state, object);
|
|
16
19
|
const found = stream(state, object);
|
|
17
|
-
if (!found
|
|
18
|
-
state.streams.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
if (!found) return;
|
|
21
|
+
const seenStream = state.streams.has(found);
|
|
22
|
+
if (!seenStream) state.streams.add(found);
|
|
23
|
+
const seen = seenRef || seenStream;
|
|
24
|
+
if (seen && !replay) return;
|
|
25
|
+
const own = found.dict.lookupMaybe(key("Resources"), PDFDict);
|
|
26
|
+
if (!seen) state.actions?.group(found.dict, found, state.maskSources.has(found));
|
|
27
|
+
state.actions?.content(object, found, replace, own ?? enclosing);
|
|
28
|
+
if (nestResources && !seen) visitResources(state, own);
|
|
22
29
|
}
|
|
23
|
-
function visitSoftMask(state, object) {
|
|
24
|
-
const
|
|
30
|
+
function visitSoftMask(state, object, enclosing) {
|
|
31
|
+
const graphicsState = dictionary(state, object);
|
|
32
|
+
const mask = dictionary(state, graphicsState?.get(key("SMask")));
|
|
25
33
|
const sourceObject = mask?.get(key("G"));
|
|
26
34
|
const source = stream(state, sourceObject);
|
|
27
|
-
if (!source || !sourceObject) return;
|
|
35
|
+
if (!graphicsState || !mask || !source || !sourceObject) return;
|
|
36
|
+
state.actions?.softMask?.(graphicsState, mask, source, source.dict.lookupMaybe(key("Resources"), PDFDict) ?? enclosing);
|
|
28
37
|
state.maskSources.add(source);
|
|
29
|
-
visitContent(state, sourceObject, (replacement) => mask?.set(key("G"), replacement));
|
|
38
|
+
visitContent(state, sourceObject, (replacement) => mask?.set(key("G"), replacement), enclosing);
|
|
30
39
|
}
|
|
31
|
-
function visitAppearances(state, page) {
|
|
40
|
+
function visitAppearances(state, page, enclosing) {
|
|
32
41
|
const annotations = resolved(state, page.get(key("Annots")));
|
|
33
42
|
if (!(annotations instanceof PDFArray)) return;
|
|
34
43
|
for (let index = 0; index < annotations.size(); index++) {
|
|
@@ -40,10 +49,10 @@ function visitAppearances(state, page) {
|
|
|
40
49
|
]) {
|
|
41
50
|
const object = appearance?.get(key(name));
|
|
42
51
|
if (!object) continue;
|
|
43
|
-
if (stream(state, object)) visitContent(state, object, (replacement) => appearance?.set(key(name), replacement));
|
|
52
|
+
if (stream(state, object)) visitContent(state, object, (replacement) => appearance?.set(key(name), replacement), enclosing);
|
|
44
53
|
else {
|
|
45
54
|
const states = dictionary(state, object);
|
|
46
|
-
for (const [stateName, stateObject] of states?.entries() ?? []) visitContent(state, stateObject, (replacement) => states?.set(stateName, replacement));
|
|
55
|
+
for (const [stateName, stateObject] of states?.entries() ?? []) visitContent(state, stateObject, (replacement) => states?.set(stateName, replacement), enclosing);
|
|
47
56
|
}
|
|
48
57
|
}
|
|
49
58
|
}
|
|
@@ -58,8 +67,8 @@ function visitFunction(state, object) {
|
|
|
58
67
|
state.actions?.function(fn, decision);
|
|
59
68
|
for (let index = 0; index < (functions?.size() ?? 0); index++) visitFunction(state, functions?.get(index));
|
|
60
69
|
}
|
|
61
|
-
function visitShading(state, shading) {
|
|
62
|
-
if (state.actions?.shading(shading)) visitFunction(state, shading.get(key("Function")));
|
|
70
|
+
function visitShading(state, shading, pattern) {
|
|
71
|
+
if (state.actions?.shading(shading, pattern)) visitFunction(state, shading.get(key("Function")));
|
|
63
72
|
}
|
|
64
73
|
function visitShadings(state, resources) {
|
|
65
74
|
const shadings = resources.lookupMaybe(key("Shading"), PDFDict);
|
|
@@ -75,14 +84,14 @@ function visitPatterns(state, resources) {
|
|
|
75
84
|
if (!found) continue;
|
|
76
85
|
if (numberOf(found, "PatternType") === 2) {
|
|
77
86
|
const shading = dictionary(state, found.get(key("Shading")));
|
|
78
|
-
if (shading) visitShading(state, shading);
|
|
79
|
-
} else visitContent(state, object, (replacement) => patterns?.set(name, replacement));
|
|
87
|
+
if (shading) visitShading(state, shading, found);
|
|
88
|
+
} else visitContent(state, object, (replacement) => patterns?.set(name, replacement), found.lookupMaybe(key("Resources"), PDFDict) ?? resources);
|
|
80
89
|
visitResources(state, found.lookupMaybe(key("Resources"), PDFDict));
|
|
81
90
|
}
|
|
82
91
|
}
|
|
83
92
|
function visitGraphicsStates(state, resources) {
|
|
84
93
|
const graphicsStates = resources.lookupMaybe(key("ExtGState"), PDFDict);
|
|
85
|
-
for (const [, object] of graphicsStates?.entries() ?? []) visitSoftMask(state, object);
|
|
94
|
+
for (const [, object] of graphicsStates?.entries() ?? []) visitSoftMask(state, object, resources);
|
|
86
95
|
}
|
|
87
96
|
function visitXObjects(state, resources) {
|
|
88
97
|
const xObjects = resources.lookupMaybe(key("XObject"), PDFDict);
|
|
@@ -90,7 +99,7 @@ function visitXObjects(state, resources) {
|
|
|
90
99
|
const found = stream(state, object);
|
|
91
100
|
if (!found) continue;
|
|
92
101
|
if (nameOf(found.dict.get(key("Subtype"))) === "Image") state.actions?.image(found);
|
|
93
|
-
else visitContent(state, object, (replacement) => xObjects?.set(name, replacement));
|
|
102
|
+
else visitContent(state, object, (replacement) => xObjects?.set(name, replacement), found.dict.lookupMaybe(key("Resources"), PDFDict) ?? resources);
|
|
94
103
|
}
|
|
95
104
|
}
|
|
96
105
|
function visitFonts(state, resources) {
|
|
@@ -99,18 +108,33 @@ function visitFonts(state, resources) {
|
|
|
99
108
|
const font = dictionary(state, object);
|
|
100
109
|
if (!font || nameOf(font.get(key("Subtype"))) !== "Type3") continue;
|
|
101
110
|
const charProcs = font.lookupMaybe(key("CharProcs"), PDFDict);
|
|
102
|
-
for (const [name, glyph] of charProcs?.entries() ?? []) visitContent(state, glyph, (replacement) => charProcs?.set(name, replacement));
|
|
111
|
+
for (const [name, glyph] of charProcs?.entries() ?? []) visitContent(state, glyph, (replacement) => charProcs?.set(name, replacement), font.lookupMaybe(key("Resources"), PDFDict) ?? resources);
|
|
103
112
|
visitResources(state, font.lookupMaybe(key("Resources"), PDFDict));
|
|
104
113
|
}
|
|
105
114
|
}
|
|
106
115
|
function visitResources(state, resources) {
|
|
107
116
|
if (!resources || state.resources.has(resources)) return;
|
|
108
117
|
state.resources.add(resources);
|
|
109
|
-
visitShadings(state, resources);
|
|
110
|
-
visitPatterns(state, resources);
|
|
111
118
|
visitGraphicsStates(state, resources);
|
|
112
119
|
visitXObjects(state, resources);
|
|
113
120
|
visitFonts(state, resources);
|
|
121
|
+
visitShadings(state, resources);
|
|
122
|
+
visitPatterns(state, resources);
|
|
123
|
+
}
|
|
124
|
+
function visitPageContents(state, page, contents, pageResources) {
|
|
125
|
+
const replay = Boolean(state.actions?.replayPageContents);
|
|
126
|
+
const nested = [];
|
|
127
|
+
const feed = (object, replace) => {
|
|
128
|
+
visitContent(state, object, replace, pageResources, replay, false);
|
|
129
|
+
const own = stream(state, object)?.dict.lookupMaybe(key("Resources"), PDFDict);
|
|
130
|
+
if (own) nested.push(own);
|
|
131
|
+
};
|
|
132
|
+
if (contents instanceof PDFArray) {
|
|
133
|
+
state.actions?.beginContentsSequence?.();
|
|
134
|
+
for (let index = 0; index < contents.size(); index++) feed(contents.get(index), (replacement) => contents.set(index, replacement));
|
|
135
|
+
state.actions?.endContentsSequence?.();
|
|
136
|
+
} else feed(contents, (replacement) => page.set(key("Contents"), replacement));
|
|
137
|
+
for (const resources of nested) visitResources(state, resources);
|
|
114
138
|
}
|
|
115
139
|
function traverse(document, maskSources, actions) {
|
|
116
140
|
const state = {
|
|
@@ -124,11 +148,10 @@ function traverse(document, maskSources, actions) {
|
|
|
124
148
|
};
|
|
125
149
|
for (const page of document.getPages()) {
|
|
126
150
|
actions?.group(page.node, void 0, false);
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
visitAppearances(state, page.node);
|
|
151
|
+
const pageResources = page.node.Resources();
|
|
152
|
+
visitPageContents(state, page.node, page.node.Contents(), pageResources);
|
|
153
|
+
visitResources(state, pageResources);
|
|
154
|
+
visitAppearances(state, page.node, pageResources);
|
|
132
155
|
}
|
|
133
156
|
}
|
|
134
157
|
function walkPressGraph(document, actions) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@segnavia/render",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@segnavia/format": "0.
|
|
30
|
-
"@segnavia/frame": "0.
|
|
29
|
+
"@segnavia/format": "0.4.0",
|
|
30
|
+
"@segnavia/frame": "0.4.0",
|
|
31
31
|
"playwright-core": "1.55.1",
|
|
32
32
|
"pdf-lib": "^1.17.1"
|
|
33
33
|
}
|