pdf-parser.js 4.7.0 → 4.8.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 +25 -3
- package/dist/glyf-contours.cjs +122 -0
- package/dist/glyf-contours.d.cts +13 -0
- package/dist/glyf-contours.d.ts +13 -0
- package/dist/glyf-contours.js +121 -0
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/raster.cjs +816 -0
- package/dist/raster.d.cts +83 -0
- package/dist/raster.d.ts +83 -0
- package/dist/raster.js +815 -0
- package/package.json +1 -1
package/dist/raster.cjs
ADDED
|
@@ -0,0 +1,816 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_objects = require("./objects.cjs");
|
|
3
|
+
const require_bytes_writer = require("./bytes/writer.cjs");
|
|
4
|
+
const require_matrix = require("./matrix.cjs");
|
|
5
|
+
const require_filters = require("./filters.cjs");
|
|
6
|
+
const require_sfnt = require("./sfnt.cjs");
|
|
7
|
+
const require_cmap_table = require("./cmap-table.cjs");
|
|
8
|
+
const require_font_tables = require("./font-tables.cjs");
|
|
9
|
+
const require_optional_content = require("./optional-content.cjs");
|
|
10
|
+
const require_diagnostics = require("./diagnostics.cjs");
|
|
11
|
+
const require_document = require("./document.cjs");
|
|
12
|
+
const require_util_abort = require("./util/abort.cjs");
|
|
13
|
+
const require_font_read = require("./font-read.cjs");
|
|
14
|
+
const require_images_read = require("./images-read.cjs");
|
|
15
|
+
const require_interpret = require("./interpret.cjs");
|
|
16
|
+
const require_read = require("./read.cjs");
|
|
17
|
+
const require_glyf = require("./glyf.cjs");
|
|
18
|
+
const require_glyf_contours = require("./glyf-contours.cjs");
|
|
19
|
+
//#region src/raster.ts
|
|
20
|
+
const FALLBACK_GLYPH_WIDTH_PER_1000 = 500;
|
|
21
|
+
function regionPixels(extentPt, scale) {
|
|
22
|
+
return Math.max(1, Math.ceil(extentPt * scale - 1e-9));
|
|
23
|
+
}
|
|
24
|
+
function renderPdfPage(pdfBytes, pageIndex, options, rasteriser) {
|
|
25
|
+
const sink = options.sink ?? require_diagnostics.NOOP_DIAGNOSTIC_SINK;
|
|
26
|
+
if (options.scale !== void 0 && options.dpi !== void 0) throw new Error("renderPdfPage accepts at most one of scale and dpi; they name the same factor in different units (dpi = 72 x scale)");
|
|
27
|
+
const scale = options.dpi !== void 0 ? options.dpi / 72 : options.scale;
|
|
28
|
+
if (scale !== void 0 && !(scale > 0)) throw new Error(`renderPdfPage requires a positive scale factor (received scale ${options.scale}, dpi ${options.dpi})`);
|
|
29
|
+
const pixelsPerPt = scale ?? 1;
|
|
30
|
+
if (options.clipPt !== void 0 && (!(options.clipPt.widthPt > 0) || !(options.clipPt.heightPt > 0))) throw new Error("renderPdfPage clipPt must carry positive widthPt and heightPt; a zero-extent region has no pixels to render");
|
|
31
|
+
require_util_abort.throwIfAborted(options.signal);
|
|
32
|
+
if (!hasPdfHeader(pdfBytes)) throw new require_diagnostics.PdfParseError("pdf/no-header", "no \"%PDF-\" header found within the first bytes of the file; this does not look like a PDF at all");
|
|
33
|
+
const doc = require_document.openPdfDocument(pdfBytes, sink);
|
|
34
|
+
const pages = doc.pages();
|
|
35
|
+
if (pageIndex < 0 || pageIndex >= pages.length) throw new require_diagnostics.PdfParseError("pdf/page-index-out-of-range", `page index ${pageIndex} is outside this document's page tree (it declares ${pages.length} page${pages.length === 1 ? "" : "s"})`);
|
|
36
|
+
const page = pages[pageIndex];
|
|
37
|
+
const mediaBox = readMediaBox(page);
|
|
38
|
+
let cropBox = readDeclaredPageBox(page, "CropBox") ?? mediaBox;
|
|
39
|
+
if (cropBox.urx - cropBox.llx <= 0 || cropBox.ury - cropBox.lly <= 0) {
|
|
40
|
+
sink({
|
|
41
|
+
code: "pdf/invalid-crop-box",
|
|
42
|
+
severity: "warning",
|
|
43
|
+
pageIndex,
|
|
44
|
+
message: "page /CropBox is degenerate (zero width or height); falling back to the /MediaBox as the visible region"
|
|
45
|
+
});
|
|
46
|
+
cropBox = mediaBox;
|
|
47
|
+
}
|
|
48
|
+
const rotation = require_read.normalizeRotation(require_objects.asNumber(require_objects.dictGet(page, "Rotate")));
|
|
49
|
+
const rotationResult = require_read.pageRotationTransform(rotation, mediaBox.urx - mediaBox.llx, mediaBox.ury - mediaBox.lly);
|
|
50
|
+
const visibleRect = rotatedRectBounds(cropBox, rotationResult.matrix);
|
|
51
|
+
const pageWidthPt = visibleRect.maxX - visibleRect.minX;
|
|
52
|
+
const pageHeightPt = visibleRect.maxY - visibleRect.minY;
|
|
53
|
+
const pageMatrix = require_matrix.multiplyMatrices(rotationResult.matrix, require_matrix.translationMatrix(-visibleRect.minX, -visibleRect.minY));
|
|
54
|
+
const requested = options.clipPt ?? {
|
|
55
|
+
xPt: 0,
|
|
56
|
+
yPt: 0,
|
|
57
|
+
widthPt: pageWidthPt,
|
|
58
|
+
heightPt: pageHeightPt
|
|
59
|
+
};
|
|
60
|
+
const clipLeft = Math.max(0, requested.xPt);
|
|
61
|
+
const clipBottom = Math.max(0, requested.yPt);
|
|
62
|
+
const clipRight = Math.min(pageWidthPt, requested.xPt + requested.widthPt);
|
|
63
|
+
const clipTop = Math.min(pageHeightPt, requested.yPt + requested.heightPt);
|
|
64
|
+
if (clipRight - clipLeft <= 0 || clipTop - clipBottom <= 0) throw new Error(`renderPdfPage clipPt does not intersect the page's visible region (clip x ${requested.xPt}..${requested.xPt + requested.widthPt}, y ${requested.yPt}..${requested.yPt + requested.heightPt}; page 0..${pageWidthPt} x 0..${pageHeightPt})`);
|
|
65
|
+
const regionWidthPt = clipRight - clipLeft;
|
|
66
|
+
const regionHeightPt = clipTop - clipBottom;
|
|
67
|
+
const widthPx = regionPixels(regionWidthPt, pixelsPerPt);
|
|
68
|
+
const heightPx = regionPixels(regionHeightPt, pixelsPerPt);
|
|
69
|
+
const pageToDeviceMatrix = [
|
|
70
|
+
pixelsPerPt,
|
|
71
|
+
0,
|
|
72
|
+
0,
|
|
73
|
+
-pixelsPerPt,
|
|
74
|
+
-clipLeft * pixelsPerPt,
|
|
75
|
+
(clipBottom + regionHeightPt) * pixelsPerPt
|
|
76
|
+
];
|
|
77
|
+
const interpretToDeviceMatrix = require_matrix.multiplyMatrices(pageMatrix, pageToDeviceMatrix);
|
|
78
|
+
const resources = doc.resolveDict(require_objects.dictGet(page, "Resources"));
|
|
79
|
+
rasteriser.beginPage({
|
|
80
|
+
widthPx,
|
|
81
|
+
heightPx,
|
|
82
|
+
scale: pixelsPerPt,
|
|
83
|
+
widthPt: regionWidthPt,
|
|
84
|
+
heightPt: regionHeightPt
|
|
85
|
+
});
|
|
86
|
+
if (resources === void 0) {
|
|
87
|
+
sink({
|
|
88
|
+
code: "pdf/object-missing-value",
|
|
89
|
+
severity: "warning",
|
|
90
|
+
message: "page has no /Resources dict; its content stream cannot be interpreted"
|
|
91
|
+
});
|
|
92
|
+
return rasteriser.finish();
|
|
93
|
+
}
|
|
94
|
+
const fontResolver = require_font_read.createFontResolver({
|
|
95
|
+
resolver: doc,
|
|
96
|
+
sink
|
|
97
|
+
});
|
|
98
|
+
const { layers, layerNameOf } = require_optional_content.readOptionalContent(doc.catalog, doc, sink);
|
|
99
|
+
const hiddenLayers = new Set(layers.filter((layer) => !layer.visible).map((layer) => layer.name));
|
|
100
|
+
const extracted = require_interpret.interpretContentStream(readPageContentBytes(page, doc, sink), resources, {
|
|
101
|
+
fontMetrics: fontResolver.metrics,
|
|
102
|
+
resolver: doc,
|
|
103
|
+
sink,
|
|
104
|
+
layerNameOf
|
|
105
|
+
});
|
|
106
|
+
const outlineFaces = /* @__PURE__ */ new Map();
|
|
107
|
+
for (const item of extracted) {
|
|
108
|
+
require_util_abort.throwIfAborted(options.signal);
|
|
109
|
+
if (item.layerName !== void 0 && hiddenLayers.has(item.layerName)) continue;
|
|
110
|
+
drawExtractedItem(item, doc, fontResolver, outlineFaces, interpretToDeviceMatrix, pixelsPerPt, rasteriser, sink);
|
|
111
|
+
}
|
|
112
|
+
return rasteriser.finish();
|
|
113
|
+
}
|
|
114
|
+
function drawExtractedItem(item, resolver, fontResolver, outlineFaces, interpretToDeviceMatrix, pixelsPerPt, rasteriser, sink) {
|
|
115
|
+
if (item.kind === "text") {
|
|
116
|
+
drawTextRun(item, fontResolver, resolver, outlineFaces, interpretToDeviceMatrix, rasteriser, sink);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (item.kind === "rect") {
|
|
120
|
+
drawRect(item, interpretToDeviceMatrix, pixelsPerPt, rasteriser);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (item.kind === "ellipse") {
|
|
124
|
+
drawEllipse(item, interpretToDeviceMatrix, pixelsPerPt, rasteriser);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (item.kind === "line") {
|
|
128
|
+
drawLine(item, interpretToDeviceMatrix, pixelsPerPt, rasteriser);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (item.kind === "path") {
|
|
132
|
+
drawPath(item, interpretToDeviceMatrix, pixelsPerPt, rasteriser);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (item.kind === "image") {
|
|
136
|
+
drawImageXObjectItem(item, resolver, interpretToDeviceMatrix, rasteriser, sink);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
drawInlineImageItem(item, resolver, interpretToDeviceMatrix, rasteriser, sink);
|
|
140
|
+
}
|
|
141
|
+
function drawRect(item, matrix, pixelsPerPt, rasteriser) {
|
|
142
|
+
const corners = [
|
|
143
|
+
require_matrix.applyMatrix(matrix, {
|
|
144
|
+
x: item.xPt,
|
|
145
|
+
y: item.yPt
|
|
146
|
+
}),
|
|
147
|
+
require_matrix.applyMatrix(matrix, {
|
|
148
|
+
x: item.xPt + item.widthPt,
|
|
149
|
+
y: item.yPt
|
|
150
|
+
}),
|
|
151
|
+
require_matrix.applyMatrix(matrix, {
|
|
152
|
+
x: item.xPt,
|
|
153
|
+
y: item.yPt + item.heightPt
|
|
154
|
+
}),
|
|
155
|
+
require_matrix.applyMatrix(matrix, {
|
|
156
|
+
x: item.xPt + item.widthPt,
|
|
157
|
+
y: item.yPt + item.heightPt
|
|
158
|
+
})
|
|
159
|
+
];
|
|
160
|
+
const minX = Math.min(...corners.map((corner) => corner.x));
|
|
161
|
+
const minY = Math.min(...corners.map((corner) => corner.y));
|
|
162
|
+
const maxX = Math.max(...corners.map((corner) => corner.x));
|
|
163
|
+
const maxY = Math.max(...corners.map((corner) => corner.y));
|
|
164
|
+
if (item.fill !== void 0) rasteriser.draw({
|
|
165
|
+
kind: "fillRect",
|
|
166
|
+
xPx: minX,
|
|
167
|
+
yPx: minY,
|
|
168
|
+
widthPx: maxX - minX,
|
|
169
|
+
heightPx: maxY - minY,
|
|
170
|
+
color: item.fill
|
|
171
|
+
});
|
|
172
|
+
if (item.stroke !== void 0) rasteriser.draw({
|
|
173
|
+
kind: "path",
|
|
174
|
+
subpaths: [{
|
|
175
|
+
startXPx: minX,
|
|
176
|
+
startYPx: minY,
|
|
177
|
+
segments: [
|
|
178
|
+
{
|
|
179
|
+
kind: "line",
|
|
180
|
+
xPx: maxX,
|
|
181
|
+
yPx: minY
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
kind: "line",
|
|
185
|
+
xPx: maxX,
|
|
186
|
+
yPx: maxY
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
kind: "line",
|
|
190
|
+
xPx: minX,
|
|
191
|
+
yPx: maxY
|
|
192
|
+
}
|
|
193
|
+
],
|
|
194
|
+
closed: true
|
|
195
|
+
}],
|
|
196
|
+
stroke: strokeSpec(item.stroke, void 0, pixelsPerPt)
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function drawEllipse(item, matrix, pixelsPerPt, rasteriser) {
|
|
200
|
+
const cx = item.xPt + item.widthPt / 2;
|
|
201
|
+
const cy = item.yPt + item.heightPt / 2;
|
|
202
|
+
const rx = item.widthPt / 2;
|
|
203
|
+
const ry = item.heightPt / 2;
|
|
204
|
+
const dx = rx * require_matrix.BEZIER_KAPPA;
|
|
205
|
+
const dy = ry * require_matrix.BEZIER_KAPPA;
|
|
206
|
+
const cardinalPoints = [
|
|
207
|
+
[cx + rx, cy],
|
|
208
|
+
[cx, cy + ry],
|
|
209
|
+
[cx - rx, cy],
|
|
210
|
+
[cx, cy - ry]
|
|
211
|
+
];
|
|
212
|
+
const controls = [
|
|
213
|
+
[cx + rx, cy + dy],
|
|
214
|
+
[cx + dx, cy + ry],
|
|
215
|
+
[cx - dx, cy + ry],
|
|
216
|
+
[cx - rx, cy + dy],
|
|
217
|
+
[cx - rx, cy - dy],
|
|
218
|
+
[cx - dx, cy - ry],
|
|
219
|
+
[cx + dx, cy - ry],
|
|
220
|
+
[cx + rx, cy - dy]
|
|
221
|
+
];
|
|
222
|
+
const transformed = (point) => require_matrix.applyMatrix(matrix, {
|
|
223
|
+
x: point[0],
|
|
224
|
+
y: point[1]
|
|
225
|
+
});
|
|
226
|
+
const start = transformed(cardinalPoints[0]);
|
|
227
|
+
const segments = [];
|
|
228
|
+
for (let i = 0; i < 4; i++) {
|
|
229
|
+
const end = transformed(cardinalPoints[(i + 1) % 4]);
|
|
230
|
+
const c1 = transformed(controls[i * 2]);
|
|
231
|
+
const c2 = transformed(controls[i * 2 + 1]);
|
|
232
|
+
segments.push({
|
|
233
|
+
kind: "cubic",
|
|
234
|
+
c1xPx: c1.x,
|
|
235
|
+
c1yPx: c1.y,
|
|
236
|
+
c2xPx: c2.x,
|
|
237
|
+
c2yPx: c2.y,
|
|
238
|
+
xPx: end.x,
|
|
239
|
+
yPx: end.y
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
rasteriser.draw({
|
|
243
|
+
kind: "path",
|
|
244
|
+
subpaths: [{
|
|
245
|
+
startXPx: start.x,
|
|
246
|
+
startYPx: start.y,
|
|
247
|
+
segments,
|
|
248
|
+
closed: true
|
|
249
|
+
}],
|
|
250
|
+
...item.fill !== void 0 ? { fill: {
|
|
251
|
+
color: item.fill,
|
|
252
|
+
fillRule: "nonzero"
|
|
253
|
+
} } : {},
|
|
254
|
+
...item.stroke !== void 0 ? { stroke: strokeSpec(item.stroke, void 0, pixelsPerPt) } : {}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function drawLine(item, matrix, pixelsPerPt, rasteriser) {
|
|
258
|
+
const p1 = require_matrix.applyMatrix(matrix, {
|
|
259
|
+
x: item.x1Pt,
|
|
260
|
+
y: item.y1Pt
|
|
261
|
+
});
|
|
262
|
+
const p2 = require_matrix.applyMatrix(matrix, {
|
|
263
|
+
x: item.x2Pt,
|
|
264
|
+
y: item.y2Pt
|
|
265
|
+
});
|
|
266
|
+
if (item.style === "dotted") {
|
|
267
|
+
drawDottedSegment(p1, p2, item.widthPt * pixelsPerPt, item.color, rasteriser);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
rasteriser.draw({
|
|
271
|
+
kind: "path",
|
|
272
|
+
subpaths: [{
|
|
273
|
+
startXPx: p1.x,
|
|
274
|
+
startYPx: p1.y,
|
|
275
|
+
segments: [{
|
|
276
|
+
kind: "line",
|
|
277
|
+
xPx: p2.x,
|
|
278
|
+
yPx: p2.y
|
|
279
|
+
}],
|
|
280
|
+
closed: false
|
|
281
|
+
}],
|
|
282
|
+
stroke: strokeSpec({
|
|
283
|
+
color: item.color,
|
|
284
|
+
widthPt: item.widthPt
|
|
285
|
+
}, item.style, pixelsPerPt)
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
function drawPath(item, matrix, pixelsPerPt, rasteriser) {
|
|
289
|
+
if (item.stroke !== void 0 && item.style === "dotted") {
|
|
290
|
+
const widthPx = item.stroke.widthPt * pixelsPerPt;
|
|
291
|
+
for (const subpath of item.subpaths) {
|
|
292
|
+
let prev = require_matrix.applyMatrix(matrix, {
|
|
293
|
+
x: subpath.startXPt,
|
|
294
|
+
y: subpath.startYPt
|
|
295
|
+
});
|
|
296
|
+
const emitTo = (end) => {
|
|
297
|
+
drawDottedSegment(prev, end, widthPx, item.stroke.color, rasteriser);
|
|
298
|
+
prev = end;
|
|
299
|
+
};
|
|
300
|
+
for (const segment of subpath.segments) if (segment.kind === "line") emitTo(require_matrix.applyMatrix(matrix, {
|
|
301
|
+
x: segment.xPt,
|
|
302
|
+
y: segment.yPt
|
|
303
|
+
}));
|
|
304
|
+
else {
|
|
305
|
+
const flattened = flattenCubic(prev, require_matrix.applyMatrix(matrix, {
|
|
306
|
+
x: segment.c1xPt,
|
|
307
|
+
y: segment.c1yPt
|
|
308
|
+
}), require_matrix.applyMatrix(matrix, {
|
|
309
|
+
x: segment.c2xPt,
|
|
310
|
+
y: segment.c2yPt
|
|
311
|
+
}), require_matrix.applyMatrix(matrix, {
|
|
312
|
+
x: segment.xPt,
|
|
313
|
+
y: segment.yPt
|
|
314
|
+
}));
|
|
315
|
+
for (const point of flattened) emitTo(point);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const subpaths = item.subpaths.map((subpath) => ({
|
|
321
|
+
startXPx: require_matrix.applyMatrix(matrix, {
|
|
322
|
+
x: subpath.startXPt,
|
|
323
|
+
y: subpath.startYPt
|
|
324
|
+
}).x,
|
|
325
|
+
startYPx: require_matrix.applyMatrix(matrix, {
|
|
326
|
+
x: subpath.startXPt,
|
|
327
|
+
y: subpath.startYPt
|
|
328
|
+
}).y,
|
|
329
|
+
segments: subpath.segments.map((segment) => {
|
|
330
|
+
if (segment.kind === "line") {
|
|
331
|
+
const end = require_matrix.applyMatrix(matrix, {
|
|
332
|
+
x: segment.xPt,
|
|
333
|
+
y: segment.yPt
|
|
334
|
+
});
|
|
335
|
+
return {
|
|
336
|
+
kind: "line",
|
|
337
|
+
xPx: end.x,
|
|
338
|
+
yPx: end.y
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
const c1 = require_matrix.applyMatrix(matrix, {
|
|
342
|
+
x: segment.c1xPt,
|
|
343
|
+
y: segment.c1yPt
|
|
344
|
+
});
|
|
345
|
+
const c2 = require_matrix.applyMatrix(matrix, {
|
|
346
|
+
x: segment.c2xPt,
|
|
347
|
+
y: segment.c2yPt
|
|
348
|
+
});
|
|
349
|
+
const end = require_matrix.applyMatrix(matrix, {
|
|
350
|
+
x: segment.xPt,
|
|
351
|
+
y: segment.yPt
|
|
352
|
+
});
|
|
353
|
+
return {
|
|
354
|
+
kind: "cubic",
|
|
355
|
+
c1xPx: c1.x,
|
|
356
|
+
c1yPx: c1.y,
|
|
357
|
+
c2xPx: c2.x,
|
|
358
|
+
c2yPx: c2.y,
|
|
359
|
+
xPx: end.x,
|
|
360
|
+
yPx: end.y
|
|
361
|
+
};
|
|
362
|
+
}),
|
|
363
|
+
closed: subpath.closed
|
|
364
|
+
}));
|
|
365
|
+
rasteriser.draw({
|
|
366
|
+
kind: "path",
|
|
367
|
+
subpaths,
|
|
368
|
+
...item.fill !== void 0 ? { fill: {
|
|
369
|
+
color: item.fill,
|
|
370
|
+
fillRule: item.fillRule
|
|
371
|
+
} } : {},
|
|
372
|
+
...item.stroke !== void 0 ? { stroke: strokeSpec(item.stroke, item.style, pixelsPerPt) } : {}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
function drawImageXObjectItem(item, resolver, interpretToDeviceMatrix, rasteriser, sink) {
|
|
376
|
+
const xobjects = resolver.resolveDict(require_objects.dictGet(item.resources, "XObject"));
|
|
377
|
+
const stream = xobjects !== void 0 ? resolver.resolve(require_objects.dictGet(xobjects, item.resourceName)) : void 0;
|
|
378
|
+
if (stream?.kind !== "stream") return;
|
|
379
|
+
const image = require_images_read.readImageXObject(stream.dict, stream.raw, resolver, sink);
|
|
380
|
+
if (image === void 0) return;
|
|
381
|
+
drawImage(image, item.matrix, interpretToDeviceMatrix, rasteriser);
|
|
382
|
+
}
|
|
383
|
+
function drawInlineImageItem(item, resolver, interpretToDeviceMatrix, rasteriser, sink) {
|
|
384
|
+
const image = require_images_read.readImageXObject(item.dict, item.data, resolver, sink);
|
|
385
|
+
if (image === void 0) return;
|
|
386
|
+
drawImage(image, item.matrix, interpretToDeviceMatrix, rasteriser);
|
|
387
|
+
}
|
|
388
|
+
function drawImage(image, ctm, interpretToDeviceMatrix, rasteriser) {
|
|
389
|
+
const matrix = require_matrix.multiplyMatrices([
|
|
390
|
+
1,
|
|
391
|
+
0,
|
|
392
|
+
0,
|
|
393
|
+
-1,
|
|
394
|
+
0,
|
|
395
|
+
1
|
|
396
|
+
], require_matrix.multiplyMatrices(ctm, interpretToDeviceMatrix));
|
|
397
|
+
rasteriser.draw({
|
|
398
|
+
kind: "image",
|
|
399
|
+
format: image.format,
|
|
400
|
+
bytes: image.bytes,
|
|
401
|
+
sourceWidthPx: image.widthPx,
|
|
402
|
+
sourceHeightPx: image.heightPx,
|
|
403
|
+
matrix
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
function strokeSpec(stroke, style, pixelsPerPt) {
|
|
407
|
+
const widthPx = stroke.widthPt * pixelsPerPt;
|
|
408
|
+
if (style === "dashed") return {
|
|
409
|
+
color: stroke.color,
|
|
410
|
+
widthPx,
|
|
411
|
+
dashPx: [widthPx * 3, widthPx * 3]
|
|
412
|
+
};
|
|
413
|
+
return {
|
|
414
|
+
color: stroke.color,
|
|
415
|
+
widthPx
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function drawDottedSegment(p1, p2, widthPx, color, rasteriser) {
|
|
419
|
+
const length = Math.hypot(p2.x - p1.x, p2.y - p1.y);
|
|
420
|
+
if (length === 0) return;
|
|
421
|
+
const spacing = Math.max(widthPx * 2, 1);
|
|
422
|
+
const dotHalf = widthPx / 2;
|
|
423
|
+
for (let distance = 0; distance <= length; distance += spacing) {
|
|
424
|
+
const t = distance / length;
|
|
425
|
+
rasteriser.draw({
|
|
426
|
+
kind: "fillRect",
|
|
427
|
+
xPx: p1.x + (p2.x - p1.x) * t - dotHalf,
|
|
428
|
+
yPx: p1.y + (p2.y - p1.y) * t - dotHalf,
|
|
429
|
+
widthPx,
|
|
430
|
+
heightPx: widthPx,
|
|
431
|
+
color
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
const STROKE_FLATTEN_TOLERANCE_PX = .05;
|
|
436
|
+
const MAX_FLATTEN_DEPTH = 16;
|
|
437
|
+
function flattenCubic(p0, c1, c2, p1) {
|
|
438
|
+
const points = [];
|
|
439
|
+
const flatten = (a, b, c, d, depth) => {
|
|
440
|
+
const chordX = d.x - a.x;
|
|
441
|
+
const chordY = d.y - a.y;
|
|
442
|
+
const chordLength = Math.hypot(chordX, chordY) || 1;
|
|
443
|
+
const dist1 = Math.abs((b.x - a.x) * chordY - (b.y - a.y) * chordX) / chordLength;
|
|
444
|
+
const dist2 = Math.abs((c.x - a.x) * chordY - (c.y - a.y) * chordX) / chordLength;
|
|
445
|
+
if (depth >= MAX_FLATTEN_DEPTH || Math.max(dist1, dist2) <= STROKE_FLATTEN_TOLERANCE_PX) {
|
|
446
|
+
points.push(d);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
const ab = {
|
|
450
|
+
x: (a.x + b.x) / 2,
|
|
451
|
+
y: (a.y + b.y) / 2
|
|
452
|
+
};
|
|
453
|
+
const bc = {
|
|
454
|
+
x: (b.x + c.x) / 2,
|
|
455
|
+
y: (b.y + c.y) / 2
|
|
456
|
+
};
|
|
457
|
+
const cd = {
|
|
458
|
+
x: (c.x + d.x) / 2,
|
|
459
|
+
y: (c.y + d.y) / 2
|
|
460
|
+
};
|
|
461
|
+
const abc = {
|
|
462
|
+
x: (ab.x + bc.x) / 2,
|
|
463
|
+
y: (ab.y + bc.y) / 2
|
|
464
|
+
};
|
|
465
|
+
const bcd = {
|
|
466
|
+
x: (bc.x + cd.x) / 2,
|
|
467
|
+
y: (bc.y + cd.y) / 2
|
|
468
|
+
};
|
|
469
|
+
const mid = {
|
|
470
|
+
x: (abc.x + bcd.x) / 2,
|
|
471
|
+
y: (abc.y + bcd.y) / 2
|
|
472
|
+
};
|
|
473
|
+
flatten(a, ab, abc, mid, depth + 1);
|
|
474
|
+
flatten(mid, bcd, cd, d, depth + 1);
|
|
475
|
+
};
|
|
476
|
+
flatten(p0, c1, c2, p1, 0);
|
|
477
|
+
return points;
|
|
478
|
+
}
|
|
479
|
+
function openEmbeddedProgram(descriptorOwner, resolver) {
|
|
480
|
+
const descriptor = resolver.resolveDict(require_objects.dictGet(descriptorOwner, "FontDescriptor"));
|
|
481
|
+
if (descriptor === void 0) return { kind: "absent" };
|
|
482
|
+
for (const key of ["FontFile2", "FontFile3"]) {
|
|
483
|
+
const stream = resolver.resolve(require_objects.dictGet(descriptor, key));
|
|
484
|
+
if (stream?.kind !== "stream") continue;
|
|
485
|
+
const bytes = require_filters.decodeStream(stream.raw, stream.dict, require_diagnostics.NOOP_DIAGNOSTIC_SINK).bytes;
|
|
486
|
+
if (bytes.length >= 3 && bytes[0] === 1 && bytes[1] === 0 && bytes[2] === 4) return { kind: "cff" };
|
|
487
|
+
const sfnt = require_sfnt.parseSfnt(bytes);
|
|
488
|
+
if (sfnt === void 0) continue;
|
|
489
|
+
if (sfnt.tables.has("CFF ")) return { kind: "cff" };
|
|
490
|
+
const head = require_font_tables.parseHead(sfnt);
|
|
491
|
+
const maxp = require_font_tables.parseMaxp(sfnt);
|
|
492
|
+
if (head === void 0 || maxp === void 0) continue;
|
|
493
|
+
const glyf = require_glyf.parseGlyf(sfnt, {
|
|
494
|
+
numGlyphs: maxp.numGlyphs,
|
|
495
|
+
indexToLocFormat: head.indexToLocFormat
|
|
496
|
+
});
|
|
497
|
+
if (glyf === void 0) continue;
|
|
498
|
+
return {
|
|
499
|
+
kind: "glyf",
|
|
500
|
+
sfnt,
|
|
501
|
+
face: {
|
|
502
|
+
composite: false,
|
|
503
|
+
glyf,
|
|
504
|
+
unitsPerEm: head.unitsPerEm,
|
|
505
|
+
glyphIdOf: () => void 0
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
return { kind: "absent" };
|
|
510
|
+
}
|
|
511
|
+
function resolveTextOutlineFace(fontResourceName, resources, resolver, fontResolver, outlineFaces, sink) {
|
|
512
|
+
const fontsDict = resolver.resolveDict(require_objects.dictGet(resources, "Font"));
|
|
513
|
+
const fontDict = fontsDict !== void 0 ? resolver.resolveDict(require_objects.dictGet(fontsDict, fontResourceName)) : void 0;
|
|
514
|
+
if (fontDict === void 0) return;
|
|
515
|
+
if (outlineFaces.has(fontDict)) return outlineFaces.get(fontDict);
|
|
516
|
+
const face = buildTextOutlineFace(fontDict, resolver, fontResolver, fontResourceName, resources, sink);
|
|
517
|
+
outlineFaces.set(fontDict, face);
|
|
518
|
+
return face;
|
|
519
|
+
}
|
|
520
|
+
function buildTextOutlineFace(fontDict, resolver, fontResolver, fontResourceName, resources, sink) {
|
|
521
|
+
const faceName = require_objects.asName(require_objects.dictGet(fontDict, "BaseFont")) ?? require_objects.asName(require_objects.dictGet(fontDict, "Subtype")) ?? "font";
|
|
522
|
+
const unavailable = (reason) => {
|
|
523
|
+
sink({
|
|
524
|
+
code: "raster/text-outlines-unavailable",
|
|
525
|
+
severity: "warning",
|
|
526
|
+
message: `font resource /${fontResourceName} (${faceName}) has no drawable outline (${reason}); its text is not rendered`
|
|
527
|
+
});
|
|
528
|
+
};
|
|
529
|
+
const cff = () => {
|
|
530
|
+
sink({
|
|
531
|
+
code: "raster/text-cff-outlines",
|
|
532
|
+
severity: "warning",
|
|
533
|
+
message: `font resource /${fontResourceName} (${faceName}) carries CFF outlines; this raster surface fills sfnt (TrueType/glyf) outlines only, so its text is not rendered rather than approximated`
|
|
534
|
+
});
|
|
535
|
+
};
|
|
536
|
+
const subtype = require_objects.asName(require_objects.dictGet(fontDict, "Subtype"));
|
|
537
|
+
if (subtype === "Type0") {
|
|
538
|
+
const encoding = resolver.resolve(require_objects.dictGet(fontDict, "Encoding"));
|
|
539
|
+
if (encoding?.kind !== "name" || encoding.name !== "Identity-H") {
|
|
540
|
+
unavailable("a Type0 font whose /Encoding is not Identity-H (a predefined or embedded CMap this raster walk does not decode)");
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const descendants = require_objects.asArray(require_objects.dictGet(fontDict, "DescendantFonts"));
|
|
544
|
+
const descendant = descendants !== void 0 ? resolver.resolveDict(descendants[0]) : void 0;
|
|
545
|
+
if (descendant === void 0) {
|
|
546
|
+
unavailable("a Type0 font with no readable /DescendantFonts entry");
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const descendantSubtype = require_objects.asName(require_objects.dictGet(descendant, "Subtype"));
|
|
550
|
+
if (descendantSubtype === "CIDFontType0") {
|
|
551
|
+
cff();
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
if (descendantSubtype !== "CIDFontType2") {
|
|
555
|
+
unavailable(`a descendant font of subtype ${descendantSubtype ?? "(none)"}`);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
const cidToGidMap = resolver.resolve(require_objects.dictGet(descendant, "CIDToGIDMap"));
|
|
559
|
+
const program = openEmbeddedProgram(descendant, resolver);
|
|
560
|
+
if (program.kind === "cff") {
|
|
561
|
+
cff();
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
if (program.kind === "absent") {
|
|
565
|
+
unavailable("no readable /FontFile2 (or glyf-bearing /FontFile3) stream");
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
if (cidToGidMap !== void 0 && cidToGidMap.kind !== "name" && cidToGidMap.kind !== "stream") {
|
|
569
|
+
unavailable("a /CIDToGIDMap that is neither /Identity nor a readable stream");
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (cidToGidMap?.kind === "stream") {
|
|
573
|
+
const decodedBytes = require_filters.decodeStream(cidToGidMap.raw, cidToGidMap.dict, require_diagnostics.NOOP_DIAGNOSTIC_SINK).bytes;
|
|
574
|
+
const entries = [];
|
|
575
|
+
for (let i = 0; i + 1 < decodedBytes.length; i += 2) entries.push(decodedBytes[i] << 8 | decodedBytes[i + 1]);
|
|
576
|
+
return {
|
|
577
|
+
composite: true,
|
|
578
|
+
glyf: program.face.glyf,
|
|
579
|
+
unitsPerEm: program.face.unitsPerEm,
|
|
580
|
+
glyphIdOf: (codes, offset) => {
|
|
581
|
+
const cid = codes[offset] << 8 | codes[offset + 1];
|
|
582
|
+
return cid < entries.length ? entries[cid] : void 0;
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
return {
|
|
587
|
+
composite: true,
|
|
588
|
+
glyf: program.face.glyf,
|
|
589
|
+
unitsPerEm: program.face.unitsPerEm,
|
|
590
|
+
glyphIdOf: (codes, offset) => codes[offset] << 8 | codes[offset + 1]
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
if (subtype === "TrueType") {
|
|
594
|
+
const program = openEmbeddedProgram(fontDict, resolver);
|
|
595
|
+
if (program.kind === "cff") {
|
|
596
|
+
cff();
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (program.kind === "absent") {
|
|
600
|
+
unavailable("no /FontDescriptor or no readable embedded program (a standard-14 or otherwise unembedded face states no outlines at all)");
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
const cmapLookup = require_cmap_table.buildCmapLookup(program.sfnt);
|
|
604
|
+
if (cmapLookup === void 0) {
|
|
605
|
+
unavailable("an embedded program with no usable Unicode cmap subtable");
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
return {
|
|
609
|
+
composite: false,
|
|
610
|
+
glyf: program.face.glyf,
|
|
611
|
+
unitsPerEm: program.face.unitsPerEm,
|
|
612
|
+
glyphIdOf: (codes, offset) => {
|
|
613
|
+
const codePoint = (fontResolver.resolve(fontResourceName, resources)?.decodeToUnicode(codes.subarray(offset, offset + 1)))?.codePointAt(0);
|
|
614
|
+
return codePoint === void 0 ? void 0 : cmapLookup(codePoint);
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
if (subtype === "Type1" || subtype === "MMType1") {
|
|
619
|
+
if (openEmbeddedProgram(fontDict, resolver).kind === "cff") return cff();
|
|
620
|
+
return unavailable("a Type 1 PostScript program, whose outlines are charstrings rather than sfnt glyf data");
|
|
621
|
+
}
|
|
622
|
+
return unavailable(`a font of subtype ${subtype ?? "(none)"} (a Type3 glyph-procedure font has no static outline at all)`);
|
|
623
|
+
}
|
|
624
|
+
function drawTextRun(item, fontResolver, resolver, outlineFaces, interpretToDeviceMatrix, rasteriser, sink) {
|
|
625
|
+
const face = resolveTextOutlineFace(item.fontResourceName, item.resources, resolver, fontResolver, outlineFaces, sink);
|
|
626
|
+
if (face === void 0) return;
|
|
627
|
+
const composite = face.composite;
|
|
628
|
+
const placements = [];
|
|
629
|
+
let offset = 0;
|
|
630
|
+
let cumulative = 0;
|
|
631
|
+
while (offset < item.codes.length) {
|
|
632
|
+
const advance = fontResolver.metrics.glyphAdvance(item.fontResourceName, item.resources, item.codes, offset);
|
|
633
|
+
const widthPer1000 = advance?.widthPer1000 ?? FALLBACK_GLYPH_WIDTH_PER_1000;
|
|
634
|
+
const byteLength = advance?.byteLengthConsumed ?? (composite ? 2 : 1);
|
|
635
|
+
placements.push({
|
|
636
|
+
glyphId: face.glyphIdOf(item.codes, offset),
|
|
637
|
+
advance: cumulative
|
|
638
|
+
});
|
|
639
|
+
cumulative += widthPer1000 / 1e3 * item.sizePt;
|
|
640
|
+
offset += byteLength;
|
|
641
|
+
}
|
|
642
|
+
const startDeviceMatrix = require_matrix.multiplyMatrices(item.startMatrix, interpretToDeviceMatrix);
|
|
643
|
+
const endDeviceMatrix = require_matrix.multiplyMatrices(item.endMatrix, interpretToDeviceMatrix);
|
|
644
|
+
const startPoint = require_matrix.applyMatrix(startDeviceMatrix, {
|
|
645
|
+
x: 0,
|
|
646
|
+
y: 0
|
|
647
|
+
});
|
|
648
|
+
const endPoint = require_matrix.applyMatrix(endDeviceMatrix, {
|
|
649
|
+
x: 0,
|
|
650
|
+
y: 0
|
|
651
|
+
});
|
|
652
|
+
const rawEndPoint = require_matrix.applyMatrix(startDeviceMatrix, {
|
|
653
|
+
x: cumulative,
|
|
654
|
+
y: 0
|
|
655
|
+
});
|
|
656
|
+
const rawExtent = Math.hypot(rawEndPoint.x - startPoint.x, rawEndPoint.y - startPoint.y);
|
|
657
|
+
const actualExtent = Math.hypot(endPoint.x - startPoint.x, endPoint.y - startPoint.y);
|
|
658
|
+
const correction = rawExtent > 1e-9 && cumulative > 0 ? actualExtent / rawExtent : 1;
|
|
659
|
+
const glyphScale = require_matrix.scaleMatrix(1 / face.unitsPerEm, 1 / face.unitsPerEm);
|
|
660
|
+
for (const placement of placements) {
|
|
661
|
+
if (placement.glyphId === void 0) continue;
|
|
662
|
+
const outline = require_glyf_contours.decodeGlyphOutline(face.glyf, placement.glyphId);
|
|
663
|
+
if (outline === void 0 || outline.contours.length === 0) continue;
|
|
664
|
+
const trm = require_matrix.multiplyMatrices(require_matrix.translationMatrix(placement.advance * correction, 0), item.startMatrix);
|
|
665
|
+
const subpaths = glyphOutlineSubpaths(outline, require_matrix.multiplyMatrices(glyphScale, require_matrix.multiplyMatrices(trm, interpretToDeviceMatrix)));
|
|
666
|
+
if (subpaths.length === 0) continue;
|
|
667
|
+
rasteriser.draw({
|
|
668
|
+
kind: "path",
|
|
669
|
+
subpaths,
|
|
670
|
+
fill: {
|
|
671
|
+
color: item.color,
|
|
672
|
+
fillRule: "nonzero"
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
function glyphOutlineSubpaths(outline, matrix) {
|
|
678
|
+
const subpaths = [];
|
|
679
|
+
for (const contour of outline.contours) {
|
|
680
|
+
if (contour.length < 3) continue;
|
|
681
|
+
const firstOn = contour.findIndex((point) => point.onCurve);
|
|
682
|
+
const contourPoints = contour.map((point) => ({
|
|
683
|
+
x: point.x,
|
|
684
|
+
y: point.y,
|
|
685
|
+
onCurve: point.onCurve
|
|
686
|
+
}));
|
|
687
|
+
const ordered = firstOn >= 0 ? [...contourPoints.slice(firstOn), ...contourPoints.slice(0, firstOn)] : contourPoints;
|
|
688
|
+
let current = firstOn >= 0 ? {
|
|
689
|
+
x: contourPoints[firstOn].x,
|
|
690
|
+
y: contourPoints[firstOn].y
|
|
691
|
+
} : {
|
|
692
|
+
x: (contourPoints[contourPoints.length - 1].x + contourPoints[0].x) / 2,
|
|
693
|
+
y: (contourPoints[contourPoints.length - 1].y + contourPoints[0].y) / 2
|
|
694
|
+
};
|
|
695
|
+
const start = current;
|
|
696
|
+
const segments = [];
|
|
697
|
+
let pendingOffCurve;
|
|
698
|
+
const emitQuad = (from, control, to) => {
|
|
699
|
+
const p0 = require_matrix.applyMatrix(matrix, from);
|
|
700
|
+
const q = require_matrix.applyMatrix(matrix, control);
|
|
701
|
+
const p1 = require_matrix.applyMatrix(matrix, to);
|
|
702
|
+
segments.push({
|
|
703
|
+
kind: "cubic",
|
|
704
|
+
c1xPx: p0.x + 2 / 3 * (q.x - p0.x),
|
|
705
|
+
c1yPx: p0.y + 2 / 3 * (q.y - p0.y),
|
|
706
|
+
c2xPx: p1.x + 2 / 3 * (q.x - p1.x),
|
|
707
|
+
c2yPx: p1.y + 2 / 3 * (q.y - p1.y),
|
|
708
|
+
xPx: p1.x,
|
|
709
|
+
yPx: p1.y
|
|
710
|
+
});
|
|
711
|
+
};
|
|
712
|
+
const emitLine = (to) => {
|
|
713
|
+
const p1 = require_matrix.applyMatrix(matrix, to);
|
|
714
|
+
segments.push({
|
|
715
|
+
kind: "line",
|
|
716
|
+
xPx: p1.x,
|
|
717
|
+
yPx: p1.y
|
|
718
|
+
});
|
|
719
|
+
};
|
|
720
|
+
for (const point of ordered) if (point.onCurve) {
|
|
721
|
+
if (pendingOffCurve === void 0) emitLine(point);
|
|
722
|
+
else emitQuad(current, pendingOffCurve, point);
|
|
723
|
+
current = point;
|
|
724
|
+
pendingOffCurve = void 0;
|
|
725
|
+
} else {
|
|
726
|
+
if (pendingOffCurve !== void 0) {
|
|
727
|
+
const implied = {
|
|
728
|
+
x: (pendingOffCurve.x + point.x) / 2,
|
|
729
|
+
y: (pendingOffCurve.y + point.y) / 2
|
|
730
|
+
};
|
|
731
|
+
emitQuad(current, pendingOffCurve, implied);
|
|
732
|
+
current = implied;
|
|
733
|
+
}
|
|
734
|
+
pendingOffCurve = point;
|
|
735
|
+
}
|
|
736
|
+
if (pendingOffCurve !== void 0) emitQuad(current, pendingOffCurve, start);
|
|
737
|
+
if (segments.length >= 2) {
|
|
738
|
+
const startPx = require_matrix.applyMatrix(matrix, start);
|
|
739
|
+
subpaths.push({
|
|
740
|
+
startXPx: startPx.x,
|
|
741
|
+
startYPx: startPx.y,
|
|
742
|
+
segments,
|
|
743
|
+
closed: true
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return subpaths;
|
|
748
|
+
}
|
|
749
|
+
const PDF_HEADER_BYTES = new TextEncoder().encode("%PDF-");
|
|
750
|
+
const HEADER_SEARCH_WINDOW = 1024;
|
|
751
|
+
function hasPdfHeader(bytes) {
|
|
752
|
+
const window = bytes.subarray(0, Math.min(HEADER_SEARCH_WINDOW, bytes.length));
|
|
753
|
+
outer: for (let i = 0; i <= window.length - PDF_HEADER_BYTES.length; i++) {
|
|
754
|
+
for (let j = 0; j < PDF_HEADER_BYTES.length; j++) if (window[i + j] !== PDF_HEADER_BYTES[j]) continue outer;
|
|
755
|
+
return true;
|
|
756
|
+
}
|
|
757
|
+
return false;
|
|
758
|
+
}
|
|
759
|
+
function readDeclaredPageBox(page, key) {
|
|
760
|
+
const arr = require_objects.asArray(require_objects.dictGet(page, key));
|
|
761
|
+
if (arr === void 0) return;
|
|
762
|
+
const a = require_objects.asNumber(arr[0]) ?? 0;
|
|
763
|
+
const b = require_objects.asNumber(arr[1]) ?? 0;
|
|
764
|
+
const c = require_objects.asNumber(arr[2]) ?? 0;
|
|
765
|
+
const d = require_objects.asNumber(arr[3]) ?? 0;
|
|
766
|
+
return {
|
|
767
|
+
llx: Math.min(a, c),
|
|
768
|
+
lly: Math.min(b, d),
|
|
769
|
+
urx: Math.max(a, c),
|
|
770
|
+
ury: Math.max(b, d)
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
const DEFAULT_PAGE_WIDTH_PT = 612;
|
|
774
|
+
const DEFAULT_PAGE_HEIGHT_PT = 792;
|
|
775
|
+
function readMediaBox(page) {
|
|
776
|
+
return readDeclaredPageBox(page, "MediaBox") ?? {
|
|
777
|
+
llx: 0,
|
|
778
|
+
lly: 0,
|
|
779
|
+
urx: DEFAULT_PAGE_WIDTH_PT,
|
|
780
|
+
ury: DEFAULT_PAGE_HEIGHT_PT
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
function rotatedRectBounds(rect, matrix) {
|
|
784
|
+
const transformed = [
|
|
785
|
+
[rect.llx, rect.lly],
|
|
786
|
+
[rect.urx, rect.lly],
|
|
787
|
+
[rect.llx, rect.ury],
|
|
788
|
+
[rect.urx, rect.ury]
|
|
789
|
+
].map(([x, y]) => require_matrix.applyMatrix(matrix, {
|
|
790
|
+
x,
|
|
791
|
+
y
|
|
792
|
+
}));
|
|
793
|
+
const xs = transformed.map((point) => point.x);
|
|
794
|
+
const ys = transformed.map((point) => point.y);
|
|
795
|
+
return {
|
|
796
|
+
minX: Math.min(...xs),
|
|
797
|
+
minY: Math.min(...ys),
|
|
798
|
+
maxX: Math.max(...xs),
|
|
799
|
+
maxY: Math.max(...ys)
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
function readPageContentBytes(page, resolver, sink) {
|
|
803
|
+
const contentsObj = resolver.resolve(require_objects.dictGet(page, "Contents"));
|
|
804
|
+
if (contentsObj?.kind === "stream") return require_filters.decodeStream(contentsObj.raw, contentsObj.dict, sink).bytes;
|
|
805
|
+
if (contentsObj?.kind === "array") {
|
|
806
|
+
const chunks = [];
|
|
807
|
+
for (const entry of contentsObj.items) {
|
|
808
|
+
const streamObj = resolver.resolve(entry);
|
|
809
|
+
if (streamObj?.kind === "stream") chunks.push(require_filters.decodeStream(streamObj.raw, streamObj.dict, sink).bytes, new Uint8Array([10]));
|
|
810
|
+
}
|
|
811
|
+
return require_bytes_writer.concatBytes(chunks);
|
|
812
|
+
}
|
|
813
|
+
return /* @__PURE__ */ new Uint8Array(0);
|
|
814
|
+
}
|
|
815
|
+
//#endregion
|
|
816
|
+
exports.renderPdfPage = renderPdfPage;
|