@vaadin-component-factory/vcf-pdf-viewer 4.0.2 → 4.1.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/package.json +8 -2
- package/pdfjs/dist/event_utils.js +207 -0
- package/pdfjs/dist/genericl10n.js +2420 -0
- package/pdfjs/dist/message_handler.js +327 -192
- package/pdfjs/dist/node_stream.js +2 -494
- package/pdfjs/dist/node_stream2.js +1759 -0
- package/pdfjs/dist/pdf.js +17910 -9761
- package/pdfjs/dist/pdf.worker.js +24 -0
- package/pdfjs/dist/pdf_link_service.js +223 -378
- package/pdfjs/dist/pdf_rendering_queue.js +62 -62
- package/pdfjs/dist/pdf_thumbnail_viewer.js +216 -399
- package/pdfjs/dist/pdf_viewer.js +3450 -2305
- package/pdfjs/dist/ui_utils.js +209 -480
- package/pdfjs/dist/util.js +384 -595
- package/pdfjs/dist/worker.js +44555 -45401
- package/src/vcf-pdf-viewer.js +153 -16
- package/pdfjs/dist/display_utils.js +0 -848
- package/pdfjs/dist/fetch_stream.js +0 -306
- package/pdfjs/dist/l10n_utils.js +0 -140
- package/pdfjs/dist/network.js +0 -565
- package/pdfjs/dist/network_utils.js +0 -340
|
@@ -0,0 +1,1759 @@
|
|
|
1
|
+
import { u as unreachable, G as CMapCompressionType, q as stringToBytes, U as Util, w as warn, F as FeatureTest, s as shadow, K as BaseException, e as assert, M as MissingPDFException, x as UnexpectedResponseException, i as isNodeJS, a as AbortException } from './util.js';
|
|
2
|
+
|
|
3
|
+
/* Copyright 2015 Mozilla Foundation
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
class BaseFilterFactory {
|
|
19
|
+
constructor() {
|
|
20
|
+
if (this.constructor === BaseFilterFactory) {
|
|
21
|
+
unreachable("Cannot initialize BaseFilterFactory.");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
addFilter(maps) {
|
|
25
|
+
return "none";
|
|
26
|
+
}
|
|
27
|
+
addHCMFilter(fgColor, bgColor) {
|
|
28
|
+
return "none";
|
|
29
|
+
}
|
|
30
|
+
addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) {
|
|
31
|
+
return "none";
|
|
32
|
+
}
|
|
33
|
+
destroy(keepHCM = false) {}
|
|
34
|
+
}
|
|
35
|
+
class BaseCanvasFactory {
|
|
36
|
+
constructor() {
|
|
37
|
+
if (this.constructor === BaseCanvasFactory) {
|
|
38
|
+
unreachable("Cannot initialize BaseCanvasFactory.");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
create(width, height) {
|
|
42
|
+
if (width <= 0 || height <= 0) {
|
|
43
|
+
throw new Error("Invalid canvas size");
|
|
44
|
+
}
|
|
45
|
+
const canvas = this._createCanvas(width, height);
|
|
46
|
+
return {
|
|
47
|
+
canvas,
|
|
48
|
+
context: canvas.getContext("2d")
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
reset(canvasAndContext, width, height) {
|
|
52
|
+
if (!canvasAndContext.canvas) {
|
|
53
|
+
throw new Error("Canvas is not specified");
|
|
54
|
+
}
|
|
55
|
+
if (width <= 0 || height <= 0) {
|
|
56
|
+
throw new Error("Invalid canvas size");
|
|
57
|
+
}
|
|
58
|
+
canvasAndContext.canvas.width = width;
|
|
59
|
+
canvasAndContext.canvas.height = height;
|
|
60
|
+
}
|
|
61
|
+
destroy(canvasAndContext) {
|
|
62
|
+
if (!canvasAndContext.canvas) {
|
|
63
|
+
throw new Error("Canvas is not specified");
|
|
64
|
+
}
|
|
65
|
+
// Zeroing the width and height cause Firefox to release graphics
|
|
66
|
+
// resources immediately, which can greatly reduce memory consumption.
|
|
67
|
+
canvasAndContext.canvas.width = 0;
|
|
68
|
+
canvasAndContext.canvas.height = 0;
|
|
69
|
+
canvasAndContext.canvas = null;
|
|
70
|
+
canvasAndContext.context = null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @ignore
|
|
75
|
+
*/
|
|
76
|
+
_createCanvas(width, height) {
|
|
77
|
+
unreachable("Abstract method `_createCanvas` called.");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
class BaseCMapReaderFactory {
|
|
81
|
+
constructor({
|
|
82
|
+
baseUrl = null,
|
|
83
|
+
isCompressed = true
|
|
84
|
+
}) {
|
|
85
|
+
if (this.constructor === BaseCMapReaderFactory) {
|
|
86
|
+
unreachable("Cannot initialize BaseCMapReaderFactory.");
|
|
87
|
+
}
|
|
88
|
+
this.baseUrl = baseUrl;
|
|
89
|
+
this.isCompressed = isCompressed;
|
|
90
|
+
}
|
|
91
|
+
async fetch({
|
|
92
|
+
name
|
|
93
|
+
}) {
|
|
94
|
+
if (!this.baseUrl) {
|
|
95
|
+
throw new Error('The CMap "baseUrl" parameter must be specified, ensure that ' + 'the "cMapUrl" and "cMapPacked" API parameters are provided.');
|
|
96
|
+
}
|
|
97
|
+
if (!name) {
|
|
98
|
+
throw new Error("CMap name must be specified.");
|
|
99
|
+
}
|
|
100
|
+
const url = this.baseUrl + name + (this.isCompressed ? ".bcmap" : "");
|
|
101
|
+
const compressionType = this.isCompressed ? CMapCompressionType.BINARY : CMapCompressionType.NONE;
|
|
102
|
+
return this._fetchData(url, compressionType).catch(reason => {
|
|
103
|
+
throw new Error(`Unable to load ${this.isCompressed ? "binary " : ""}CMap at: ${url}`);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @ignore
|
|
109
|
+
*/
|
|
110
|
+
_fetchData(url, compressionType) {
|
|
111
|
+
unreachable("Abstract method `_fetchData` called.");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
class BaseStandardFontDataFactory {
|
|
115
|
+
constructor({
|
|
116
|
+
baseUrl = null
|
|
117
|
+
}) {
|
|
118
|
+
if (this.constructor === BaseStandardFontDataFactory) {
|
|
119
|
+
unreachable("Cannot initialize BaseStandardFontDataFactory.");
|
|
120
|
+
}
|
|
121
|
+
this.baseUrl = baseUrl;
|
|
122
|
+
}
|
|
123
|
+
async fetch({
|
|
124
|
+
filename
|
|
125
|
+
}) {
|
|
126
|
+
if (!this.baseUrl) {
|
|
127
|
+
throw new Error('The standard font "baseUrl" parameter must be specified, ensure that ' + 'the "standardFontDataUrl" API parameter is provided.');
|
|
128
|
+
}
|
|
129
|
+
if (!filename) {
|
|
130
|
+
throw new Error("Font filename must be specified.");
|
|
131
|
+
}
|
|
132
|
+
const url = `${this.baseUrl}${filename}`;
|
|
133
|
+
return this._fetchData(url).catch(reason => {
|
|
134
|
+
throw new Error(`Unable to load font data at: ${url}`);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @ignore
|
|
140
|
+
*/
|
|
141
|
+
_fetchData(url) {
|
|
142
|
+
unreachable("Abstract method `_fetchData` called.");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
class BaseSVGFactory {
|
|
146
|
+
constructor() {
|
|
147
|
+
if (this.constructor === BaseSVGFactory) {
|
|
148
|
+
unreachable("Cannot initialize BaseSVGFactory.");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
create(width, height, skipDimensions = false) {
|
|
152
|
+
if (width <= 0 || height <= 0) {
|
|
153
|
+
throw new Error("Invalid SVG dimensions");
|
|
154
|
+
}
|
|
155
|
+
const svg = this._createSVG("svg:svg");
|
|
156
|
+
svg.setAttribute("version", "1.1");
|
|
157
|
+
if (!skipDimensions) {
|
|
158
|
+
svg.setAttribute("width", `${width}px`);
|
|
159
|
+
svg.setAttribute("height", `${height}px`);
|
|
160
|
+
}
|
|
161
|
+
svg.setAttribute("preserveAspectRatio", "none");
|
|
162
|
+
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
163
|
+
return svg;
|
|
164
|
+
}
|
|
165
|
+
createElement(type) {
|
|
166
|
+
if (typeof type !== "string") {
|
|
167
|
+
throw new Error("Invalid SVG element type");
|
|
168
|
+
}
|
|
169
|
+
return this._createSVG(type);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @ignore
|
|
174
|
+
*/
|
|
175
|
+
_createSVG(type) {
|
|
176
|
+
unreachable("Abstract method `_createSVG` called.");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* Copyright 2015 Mozilla Foundation
|
|
181
|
+
*
|
|
182
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
183
|
+
* you may not use this file except in compliance with the License.
|
|
184
|
+
* You may obtain a copy of the License at
|
|
185
|
+
*
|
|
186
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
187
|
+
*
|
|
188
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
189
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
190
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
191
|
+
* See the License for the specific language governing permissions and
|
|
192
|
+
* limitations under the License.
|
|
193
|
+
*/
|
|
194
|
+
|
|
195
|
+
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
196
|
+
class PixelsPerInch {
|
|
197
|
+
static CSS = 96.0;
|
|
198
|
+
static PDF = 72.0;
|
|
199
|
+
static PDF_TO_CSS_UNITS = this.CSS / this.PDF;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* FilterFactory aims to create some SVG filters we can use when drawing an
|
|
204
|
+
* image (or whatever) on a canvas.
|
|
205
|
+
* Filters aren't applied with ctx.putImageData because it just overwrites the
|
|
206
|
+
* underlying pixels.
|
|
207
|
+
* With these filters, it's possible for example to apply some transfer maps on
|
|
208
|
+
* an image without the need to apply them on the pixel arrays: the renderer
|
|
209
|
+
* does the magic for us.
|
|
210
|
+
*/
|
|
211
|
+
class DOMFilterFactory extends BaseFilterFactory {
|
|
212
|
+
#_cache;
|
|
213
|
+
#_defs;
|
|
214
|
+
#docId;
|
|
215
|
+
#document;
|
|
216
|
+
#_hcmCache;
|
|
217
|
+
#id = 0;
|
|
218
|
+
constructor({
|
|
219
|
+
docId,
|
|
220
|
+
ownerDocument = globalThis.document
|
|
221
|
+
} = {}) {
|
|
222
|
+
super();
|
|
223
|
+
this.#docId = docId;
|
|
224
|
+
this.#document = ownerDocument;
|
|
225
|
+
}
|
|
226
|
+
get #cache() {
|
|
227
|
+
return this.#_cache || (this.#_cache = new Map());
|
|
228
|
+
}
|
|
229
|
+
get #hcmCache() {
|
|
230
|
+
return this.#_hcmCache || (this.#_hcmCache = new Map());
|
|
231
|
+
}
|
|
232
|
+
get #defs() {
|
|
233
|
+
if (!this.#_defs) {
|
|
234
|
+
const div = this.#document.createElement("div");
|
|
235
|
+
const {
|
|
236
|
+
style
|
|
237
|
+
} = div;
|
|
238
|
+
style.visibility = "hidden";
|
|
239
|
+
style.contain = "strict";
|
|
240
|
+
style.width = style.height = 0;
|
|
241
|
+
style.position = "absolute";
|
|
242
|
+
style.top = style.left = 0;
|
|
243
|
+
style.zIndex = -1;
|
|
244
|
+
const svg = this.#document.createElementNS(SVG_NS, "svg");
|
|
245
|
+
svg.setAttribute("width", 0);
|
|
246
|
+
svg.setAttribute("height", 0);
|
|
247
|
+
this.#_defs = this.#document.createElementNS(SVG_NS, "defs");
|
|
248
|
+
div.append(svg);
|
|
249
|
+
svg.append(this.#_defs);
|
|
250
|
+
this.#document.body.append(div);
|
|
251
|
+
}
|
|
252
|
+
return this.#_defs;
|
|
253
|
+
}
|
|
254
|
+
addFilter(maps) {
|
|
255
|
+
if (!maps) {
|
|
256
|
+
return "none";
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// When a page is zoomed the page is re-drawn but the maps are likely
|
|
260
|
+
// the same.
|
|
261
|
+
let value = this.#cache.get(maps);
|
|
262
|
+
if (value) {
|
|
263
|
+
return value;
|
|
264
|
+
}
|
|
265
|
+
let tableR, tableG, tableB, key;
|
|
266
|
+
if (maps.length === 1) {
|
|
267
|
+
const mapR = maps[0];
|
|
268
|
+
const buffer = new Array(256);
|
|
269
|
+
for (let i = 0; i < 256; i++) {
|
|
270
|
+
buffer[i] = mapR[i] / 255;
|
|
271
|
+
}
|
|
272
|
+
key = tableR = tableG = tableB = buffer.join(",");
|
|
273
|
+
} else {
|
|
274
|
+
const [mapR, mapG, mapB] = maps;
|
|
275
|
+
const bufferR = new Array(256);
|
|
276
|
+
const bufferG = new Array(256);
|
|
277
|
+
const bufferB = new Array(256);
|
|
278
|
+
for (let i = 0; i < 256; i++) {
|
|
279
|
+
bufferR[i] = mapR[i] / 255;
|
|
280
|
+
bufferG[i] = mapG[i] / 255;
|
|
281
|
+
bufferB[i] = mapB[i] / 255;
|
|
282
|
+
}
|
|
283
|
+
tableR = bufferR.join(",");
|
|
284
|
+
tableG = bufferG.join(",");
|
|
285
|
+
tableB = bufferB.join(",");
|
|
286
|
+
key = `${tableR}${tableG}${tableB}`;
|
|
287
|
+
}
|
|
288
|
+
value = this.#cache.get(key);
|
|
289
|
+
if (value) {
|
|
290
|
+
this.#cache.set(maps, value);
|
|
291
|
+
return value;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// We create a SVG filter: feComponentTransferElement
|
|
295
|
+
// https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
|
|
296
|
+
|
|
297
|
+
const id = `g_${this.#docId}_transfer_map_${this.#id++}`;
|
|
298
|
+
const url = `url(#${id})`;
|
|
299
|
+
this.#cache.set(maps, url);
|
|
300
|
+
this.#cache.set(key, url);
|
|
301
|
+
const filter = this.#createFilter(id);
|
|
302
|
+
this.#addTransferMapConversion(tableR, tableG, tableB, filter);
|
|
303
|
+
return url;
|
|
304
|
+
}
|
|
305
|
+
addHCMFilter(fgColor, bgColor) {
|
|
306
|
+
var _info;
|
|
307
|
+
const key = `${fgColor}-${bgColor}`;
|
|
308
|
+
const filterName = "base";
|
|
309
|
+
let info = this.#hcmCache.get(filterName);
|
|
310
|
+
if (((_info = info) === null || _info === void 0 ? void 0 : _info.key) === key) {
|
|
311
|
+
return info.url;
|
|
312
|
+
}
|
|
313
|
+
if (info) {
|
|
314
|
+
var _info$filter;
|
|
315
|
+
(_info$filter = info.filter) === null || _info$filter === void 0 ? void 0 : _info$filter.remove();
|
|
316
|
+
info.key = key;
|
|
317
|
+
info.url = "none";
|
|
318
|
+
info.filter = null;
|
|
319
|
+
} else {
|
|
320
|
+
info = {
|
|
321
|
+
key,
|
|
322
|
+
url: "none",
|
|
323
|
+
filter: null
|
|
324
|
+
};
|
|
325
|
+
this.#hcmCache.set(filterName, info);
|
|
326
|
+
}
|
|
327
|
+
if (!fgColor || !bgColor) {
|
|
328
|
+
return info.url;
|
|
329
|
+
}
|
|
330
|
+
const fgRGB = this.#getRGB(fgColor);
|
|
331
|
+
fgColor = Util.makeHexColor(...fgRGB);
|
|
332
|
+
const bgRGB = this.#getRGB(bgColor);
|
|
333
|
+
bgColor = Util.makeHexColor(...bgRGB);
|
|
334
|
+
this.#defs.style.color = "";
|
|
335
|
+
if (fgColor === "#000000" && bgColor === "#ffffff" || fgColor === bgColor) {
|
|
336
|
+
return info.url;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance
|
|
340
|
+
//
|
|
341
|
+
// Relative luminance:
|
|
342
|
+
// https://www.w3.org/TR/WCAG20/#relativeluminancedef
|
|
343
|
+
//
|
|
344
|
+
// We compute the rounded luminance of the default background color.
|
|
345
|
+
// Then for every color in the pdf, if its rounded luminance is the
|
|
346
|
+
// same as the background one then it's replaced by the new
|
|
347
|
+
// background color else by the foreground one.
|
|
348
|
+
const map = new Array(256);
|
|
349
|
+
for (let i = 0; i <= 255; i++) {
|
|
350
|
+
const x = i / 255;
|
|
351
|
+
map[i] = x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
|
|
352
|
+
}
|
|
353
|
+
const table = map.join(",");
|
|
354
|
+
const id = `g_${this.#docId}_hcm_filter`;
|
|
355
|
+
const filter = info.filter = this.#createFilter(id);
|
|
356
|
+
this.#addTransferMapConversion(table, table, table, filter);
|
|
357
|
+
this.#addGrayConversion(filter);
|
|
358
|
+
const getSteps = (c, n) => {
|
|
359
|
+
const start = fgRGB[c] / 255;
|
|
360
|
+
const end = bgRGB[c] / 255;
|
|
361
|
+
const arr = new Array(n + 1);
|
|
362
|
+
for (let i = 0; i <= n; i++) {
|
|
363
|
+
arr[i] = start + i / n * (end - start);
|
|
364
|
+
}
|
|
365
|
+
return arr.join(",");
|
|
366
|
+
};
|
|
367
|
+
this.#addTransferMapConversion(getSteps(0, 5), getSteps(1, 5), getSteps(2, 5), filter);
|
|
368
|
+
info.url = `url(#${id})`;
|
|
369
|
+
return info.url;
|
|
370
|
+
}
|
|
371
|
+
addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) {
|
|
372
|
+
var _info2;
|
|
373
|
+
const key = `${fgColor}-${bgColor}-${newFgColor}-${newBgColor}`;
|
|
374
|
+
let info = this.#hcmCache.get(filterName);
|
|
375
|
+
if (((_info2 = info) === null || _info2 === void 0 ? void 0 : _info2.key) === key) {
|
|
376
|
+
return info.url;
|
|
377
|
+
}
|
|
378
|
+
if (info) {
|
|
379
|
+
var _info$filter2;
|
|
380
|
+
(_info$filter2 = info.filter) === null || _info$filter2 === void 0 ? void 0 : _info$filter2.remove();
|
|
381
|
+
info.key = key;
|
|
382
|
+
info.url = "none";
|
|
383
|
+
info.filter = null;
|
|
384
|
+
} else {
|
|
385
|
+
info = {
|
|
386
|
+
key,
|
|
387
|
+
url: "none",
|
|
388
|
+
filter: null
|
|
389
|
+
};
|
|
390
|
+
this.#hcmCache.set(filterName, info);
|
|
391
|
+
}
|
|
392
|
+
if (!fgColor || !bgColor) {
|
|
393
|
+
return info.url;
|
|
394
|
+
}
|
|
395
|
+
const [fgRGB, bgRGB] = [fgColor, bgColor].map(this.#getRGB.bind(this));
|
|
396
|
+
let fgGray = Math.round(0.2126 * fgRGB[0] + 0.7152 * fgRGB[1] + 0.0722 * fgRGB[2]);
|
|
397
|
+
let bgGray = Math.round(0.2126 * bgRGB[0] + 0.7152 * bgRGB[1] + 0.0722 * bgRGB[2]);
|
|
398
|
+
let [newFgRGB, newBgRGB] = [newFgColor, newBgColor].map(this.#getRGB.bind(this));
|
|
399
|
+
if (bgGray < fgGray) {
|
|
400
|
+
[fgGray, bgGray, newFgRGB, newBgRGB] = [bgGray, fgGray, newBgRGB, newFgRGB];
|
|
401
|
+
}
|
|
402
|
+
this.#defs.style.color = "";
|
|
403
|
+
|
|
404
|
+
// Now we can create the filters to highlight some canvas parts.
|
|
405
|
+
// The colors in the pdf will almost be Canvas and CanvasText, hence we
|
|
406
|
+
// want to filter them to finally get Highlight and HighlightText.
|
|
407
|
+
// Since we're in HCM the background color and the foreground color should
|
|
408
|
+
// be really different when converted to grayscale (if they're not then it
|
|
409
|
+
// means that we've a poor contrast). Once the canvas colors are converted
|
|
410
|
+
// to grayscale we can easily map them on their new colors.
|
|
411
|
+
// The grayscale step is important because if we've something like:
|
|
412
|
+
// fgColor = #FF....
|
|
413
|
+
// bgColor = #FF....
|
|
414
|
+
// then we are enable to map the red component on the new red components
|
|
415
|
+
// which can be different.
|
|
416
|
+
|
|
417
|
+
const getSteps = (fg, bg, n) => {
|
|
418
|
+
const arr = new Array(256);
|
|
419
|
+
const step = (bgGray - fgGray) / n;
|
|
420
|
+
const newStart = fg / 255;
|
|
421
|
+
const newStep = (bg - fg) / (255 * n);
|
|
422
|
+
let prev = 0;
|
|
423
|
+
for (let i = 0; i <= n; i++) {
|
|
424
|
+
const k = Math.round(fgGray + i * step);
|
|
425
|
+
const value = newStart + i * newStep;
|
|
426
|
+
for (let j = prev; j <= k; j++) {
|
|
427
|
+
arr[j] = value;
|
|
428
|
+
}
|
|
429
|
+
prev = k + 1;
|
|
430
|
+
}
|
|
431
|
+
for (let i = prev; i < 256; i++) {
|
|
432
|
+
arr[i] = arr[prev - 1];
|
|
433
|
+
}
|
|
434
|
+
return arr.join(",");
|
|
435
|
+
};
|
|
436
|
+
const id = `g_${this.#docId}_hcm_${filterName}_filter`;
|
|
437
|
+
const filter = info.filter = this.#createFilter(id);
|
|
438
|
+
this.#addGrayConversion(filter);
|
|
439
|
+
this.#addTransferMapConversion(getSteps(newFgRGB[0], newBgRGB[0], 5), getSteps(newFgRGB[1], newBgRGB[1], 5), getSteps(newFgRGB[2], newBgRGB[2], 5), filter);
|
|
440
|
+
info.url = `url(#${id})`;
|
|
441
|
+
return info.url;
|
|
442
|
+
}
|
|
443
|
+
destroy(keepHCM = false) {
|
|
444
|
+
if (keepHCM && this.#hcmCache.size !== 0) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (this.#_defs) {
|
|
448
|
+
this.#_defs.parentNode.parentNode.remove();
|
|
449
|
+
this.#_defs = null;
|
|
450
|
+
}
|
|
451
|
+
if (this.#_cache) {
|
|
452
|
+
this.#_cache.clear();
|
|
453
|
+
this.#_cache = null;
|
|
454
|
+
}
|
|
455
|
+
this.#id = 0;
|
|
456
|
+
}
|
|
457
|
+
#addGrayConversion(filter) {
|
|
458
|
+
const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix");
|
|
459
|
+
feColorMatrix.setAttribute("type", "matrix");
|
|
460
|
+
feColorMatrix.setAttribute("values", "0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0");
|
|
461
|
+
filter.append(feColorMatrix);
|
|
462
|
+
}
|
|
463
|
+
#createFilter(id) {
|
|
464
|
+
const filter = this.#document.createElementNS(SVG_NS, "filter");
|
|
465
|
+
filter.setAttribute("color-interpolation-filters", "sRGB");
|
|
466
|
+
filter.setAttribute("id", id);
|
|
467
|
+
this.#defs.append(filter);
|
|
468
|
+
return filter;
|
|
469
|
+
}
|
|
470
|
+
#appendFeFunc(feComponentTransfer, func, table) {
|
|
471
|
+
const feFunc = this.#document.createElementNS(SVG_NS, func);
|
|
472
|
+
feFunc.setAttribute("type", "discrete");
|
|
473
|
+
feFunc.setAttribute("tableValues", table);
|
|
474
|
+
feComponentTransfer.append(feFunc);
|
|
475
|
+
}
|
|
476
|
+
#addTransferMapConversion(rTable, gTable, bTable, filter) {
|
|
477
|
+
const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer");
|
|
478
|
+
filter.append(feComponentTransfer);
|
|
479
|
+
this.#appendFeFunc(feComponentTransfer, "feFuncR", rTable);
|
|
480
|
+
this.#appendFeFunc(feComponentTransfer, "feFuncG", gTable);
|
|
481
|
+
this.#appendFeFunc(feComponentTransfer, "feFuncB", bTable);
|
|
482
|
+
}
|
|
483
|
+
#getRGB(color) {
|
|
484
|
+
this.#defs.style.color = color;
|
|
485
|
+
return getRGB(getComputedStyle(this.#defs).getPropertyValue("color"));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
class DOMCanvasFactory extends BaseCanvasFactory {
|
|
489
|
+
constructor({
|
|
490
|
+
ownerDocument = globalThis.document
|
|
491
|
+
} = {}) {
|
|
492
|
+
super();
|
|
493
|
+
this._document = ownerDocument;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* @ignore
|
|
498
|
+
*/
|
|
499
|
+
_createCanvas(width, height) {
|
|
500
|
+
const canvas = this._document.createElement("canvas");
|
|
501
|
+
canvas.width = width;
|
|
502
|
+
canvas.height = height;
|
|
503
|
+
return canvas;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
async function fetchData(url, type = "text") {
|
|
507
|
+
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL") || isValidFetchUrl(url, document.baseURI)) {
|
|
508
|
+
const response = await fetch(url);
|
|
509
|
+
if (!response.ok) {
|
|
510
|
+
throw new Error(response.statusText);
|
|
511
|
+
}
|
|
512
|
+
switch (type) {
|
|
513
|
+
case "arraybuffer":
|
|
514
|
+
return response.arrayBuffer();
|
|
515
|
+
case "blob":
|
|
516
|
+
return response.blob();
|
|
517
|
+
case "json":
|
|
518
|
+
return response.json();
|
|
519
|
+
}
|
|
520
|
+
return response.text();
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// The Fetch API is not supported.
|
|
524
|
+
return new Promise((resolve, reject) => {
|
|
525
|
+
const request = new XMLHttpRequest();
|
|
526
|
+
request.open("GET", url, /* async = */true);
|
|
527
|
+
request.responseType = type;
|
|
528
|
+
request.onreadystatechange = () => {
|
|
529
|
+
if (request.readyState !== XMLHttpRequest.DONE) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (request.status === 200 || request.status === 0) {
|
|
533
|
+
switch (type) {
|
|
534
|
+
case "arraybuffer":
|
|
535
|
+
case "blob":
|
|
536
|
+
case "json":
|
|
537
|
+
resolve(request.response);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
resolve(request.responseText);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
reject(new Error(request.statusText));
|
|
544
|
+
};
|
|
545
|
+
request.send(null);
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
class DOMCMapReaderFactory extends BaseCMapReaderFactory {
|
|
549
|
+
/**
|
|
550
|
+
* @ignore
|
|
551
|
+
*/
|
|
552
|
+
_fetchData(url, compressionType) {
|
|
553
|
+
return fetchData(url, /* type = */this.isCompressed ? "arraybuffer" : "text").then(data => ({
|
|
554
|
+
cMapData: data instanceof ArrayBuffer ? new Uint8Array(data) : stringToBytes(data),
|
|
555
|
+
compressionType
|
|
556
|
+
}));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
class DOMStandardFontDataFactory extends BaseStandardFontDataFactory {
|
|
560
|
+
/**
|
|
561
|
+
* @ignore
|
|
562
|
+
*/
|
|
563
|
+
_fetchData(url) {
|
|
564
|
+
return fetchData(url, /* type = */"arraybuffer").then(data => new Uint8Array(data));
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
class DOMSVGFactory extends BaseSVGFactory {
|
|
568
|
+
/**
|
|
569
|
+
* @ignore
|
|
570
|
+
*/
|
|
571
|
+
_createSVG(type) {
|
|
572
|
+
return document.createElementNS(SVG_NS, type);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* @typedef {Object} PageViewportParameters
|
|
578
|
+
* @property {Array<number>} viewBox - The xMin, yMin, xMax and
|
|
579
|
+
* yMax coordinates.
|
|
580
|
+
* @property {number} scale - The scale of the viewport.
|
|
581
|
+
* @property {number} rotation - The rotation, in degrees, of the viewport.
|
|
582
|
+
* @property {number} [offsetX] - The horizontal, i.e. x-axis, offset. The
|
|
583
|
+
* default value is `0`.
|
|
584
|
+
* @property {number} [offsetY] - The vertical, i.e. y-axis, offset. The
|
|
585
|
+
* default value is `0`.
|
|
586
|
+
* @property {boolean} [dontFlip] - If true, the y-axis will not be flipped.
|
|
587
|
+
* The default value is `false`.
|
|
588
|
+
*/
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* @typedef {Object} PageViewportCloneParameters
|
|
592
|
+
* @property {number} [scale] - The scale, overriding the one in the cloned
|
|
593
|
+
* viewport. The default value is `this.scale`.
|
|
594
|
+
* @property {number} [rotation] - The rotation, in degrees, overriding the one
|
|
595
|
+
* in the cloned viewport. The default value is `this.rotation`.
|
|
596
|
+
* @property {number} [offsetX] - The horizontal, i.e. x-axis, offset.
|
|
597
|
+
* The default value is `this.offsetX`.
|
|
598
|
+
* @property {number} [offsetY] - The vertical, i.e. y-axis, offset.
|
|
599
|
+
* The default value is `this.offsetY`.
|
|
600
|
+
* @property {boolean} [dontFlip] - If true, the x-axis will not be flipped.
|
|
601
|
+
* The default value is `false`.
|
|
602
|
+
*/
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* PDF page viewport created based on scale, rotation and offset.
|
|
606
|
+
*/
|
|
607
|
+
class PageViewport {
|
|
608
|
+
/**
|
|
609
|
+
* @param {PageViewportParameters}
|
|
610
|
+
*/
|
|
611
|
+
constructor({
|
|
612
|
+
viewBox,
|
|
613
|
+
scale,
|
|
614
|
+
rotation,
|
|
615
|
+
offsetX = 0,
|
|
616
|
+
offsetY = 0,
|
|
617
|
+
dontFlip = false
|
|
618
|
+
}) {
|
|
619
|
+
this.viewBox = viewBox;
|
|
620
|
+
this.scale = scale;
|
|
621
|
+
this.rotation = rotation;
|
|
622
|
+
this.offsetX = offsetX;
|
|
623
|
+
this.offsetY = offsetY;
|
|
624
|
+
|
|
625
|
+
// creating transform to convert pdf coordinate system to the normal
|
|
626
|
+
// canvas like coordinates taking in account scale and rotation
|
|
627
|
+
const centerX = (viewBox[2] + viewBox[0]) / 2;
|
|
628
|
+
const centerY = (viewBox[3] + viewBox[1]) / 2;
|
|
629
|
+
let rotateA, rotateB, rotateC, rotateD;
|
|
630
|
+
// Normalize the rotation, by clamping it to the [0, 360) range.
|
|
631
|
+
rotation %= 360;
|
|
632
|
+
if (rotation < 0) {
|
|
633
|
+
rotation += 360;
|
|
634
|
+
}
|
|
635
|
+
switch (rotation) {
|
|
636
|
+
case 180:
|
|
637
|
+
rotateA = -1;
|
|
638
|
+
rotateB = 0;
|
|
639
|
+
rotateC = 0;
|
|
640
|
+
rotateD = 1;
|
|
641
|
+
break;
|
|
642
|
+
case 90:
|
|
643
|
+
rotateA = 0;
|
|
644
|
+
rotateB = 1;
|
|
645
|
+
rotateC = 1;
|
|
646
|
+
rotateD = 0;
|
|
647
|
+
break;
|
|
648
|
+
case 270:
|
|
649
|
+
rotateA = 0;
|
|
650
|
+
rotateB = -1;
|
|
651
|
+
rotateC = -1;
|
|
652
|
+
rotateD = 0;
|
|
653
|
+
break;
|
|
654
|
+
case 0:
|
|
655
|
+
rotateA = 1;
|
|
656
|
+
rotateB = 0;
|
|
657
|
+
rotateC = 0;
|
|
658
|
+
rotateD = -1;
|
|
659
|
+
break;
|
|
660
|
+
default:
|
|
661
|
+
throw new Error("PageViewport: Invalid rotation, must be a multiple of 90 degrees.");
|
|
662
|
+
}
|
|
663
|
+
if (dontFlip) {
|
|
664
|
+
rotateC = -rotateC;
|
|
665
|
+
rotateD = -rotateD;
|
|
666
|
+
}
|
|
667
|
+
let offsetCanvasX, offsetCanvasY;
|
|
668
|
+
let width, height;
|
|
669
|
+
if (rotateA === 0) {
|
|
670
|
+
offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
|
|
671
|
+
offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
|
|
672
|
+
width = (viewBox[3] - viewBox[1]) * scale;
|
|
673
|
+
height = (viewBox[2] - viewBox[0]) * scale;
|
|
674
|
+
} else {
|
|
675
|
+
offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
|
|
676
|
+
offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
|
|
677
|
+
width = (viewBox[2] - viewBox[0]) * scale;
|
|
678
|
+
height = (viewBox[3] - viewBox[1]) * scale;
|
|
679
|
+
}
|
|
680
|
+
// creating transform for the following operations:
|
|
681
|
+
// translate(-centerX, -centerY), rotate and flip vertically,
|
|
682
|
+
// scale, and translate(offsetCanvasX, offsetCanvasY)
|
|
683
|
+
this.transform = [rotateA * scale, rotateB * scale, rotateC * scale, rotateD * scale, offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY];
|
|
684
|
+
this.width = width;
|
|
685
|
+
this.height = height;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* The original, un-scaled, viewport dimensions.
|
|
690
|
+
* @type {Object}
|
|
691
|
+
*/
|
|
692
|
+
get rawDims() {
|
|
693
|
+
const {
|
|
694
|
+
viewBox
|
|
695
|
+
} = this;
|
|
696
|
+
return shadow(this, "rawDims", {
|
|
697
|
+
pageWidth: viewBox[2] - viewBox[0],
|
|
698
|
+
pageHeight: viewBox[3] - viewBox[1],
|
|
699
|
+
pageX: viewBox[0],
|
|
700
|
+
pageY: viewBox[1]
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Clones viewport, with optional additional properties.
|
|
706
|
+
* @param {PageViewportCloneParameters} [params]
|
|
707
|
+
* @returns {PageViewport} Cloned viewport.
|
|
708
|
+
*/
|
|
709
|
+
clone({
|
|
710
|
+
scale = this.scale,
|
|
711
|
+
rotation = this.rotation,
|
|
712
|
+
offsetX = this.offsetX,
|
|
713
|
+
offsetY = this.offsetY,
|
|
714
|
+
dontFlip = false
|
|
715
|
+
} = {}) {
|
|
716
|
+
return new PageViewport({
|
|
717
|
+
viewBox: this.viewBox.slice(),
|
|
718
|
+
scale,
|
|
719
|
+
rotation,
|
|
720
|
+
offsetX,
|
|
721
|
+
offsetY,
|
|
722
|
+
dontFlip
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Converts PDF point to the viewport coordinates. For examples, useful for
|
|
728
|
+
* converting PDF location into canvas pixel coordinates.
|
|
729
|
+
* @param {number} x - The x-coordinate.
|
|
730
|
+
* @param {number} y - The y-coordinate.
|
|
731
|
+
* @returns {Array} Array containing `x`- and `y`-coordinates of the
|
|
732
|
+
* point in the viewport coordinate space.
|
|
733
|
+
* @see {@link convertToPdfPoint}
|
|
734
|
+
* @see {@link convertToViewportRectangle}
|
|
735
|
+
*/
|
|
736
|
+
convertToViewportPoint(x, y) {
|
|
737
|
+
return Util.applyTransform([x, y], this.transform);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Converts PDF rectangle to the viewport coordinates.
|
|
742
|
+
* @param {Array} rect - The xMin, yMin, xMax and yMax coordinates.
|
|
743
|
+
* @returns {Array} Array containing corresponding coordinates of the
|
|
744
|
+
* rectangle in the viewport coordinate space.
|
|
745
|
+
* @see {@link convertToViewportPoint}
|
|
746
|
+
*/
|
|
747
|
+
convertToViewportRectangle(rect) {
|
|
748
|
+
const topLeft = Util.applyTransform([rect[0], rect[1]], this.transform);
|
|
749
|
+
const bottomRight = Util.applyTransform([rect[2], rect[3]], this.transform);
|
|
750
|
+
return [topLeft[0], topLeft[1], bottomRight[0], bottomRight[1]];
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Converts viewport coordinates to the PDF location. For examples, useful
|
|
755
|
+
* for converting canvas pixel location into PDF one.
|
|
756
|
+
* @param {number} x - The x-coordinate.
|
|
757
|
+
* @param {number} y - The y-coordinate.
|
|
758
|
+
* @returns {Array} Array containing `x`- and `y`-coordinates of the
|
|
759
|
+
* point in the PDF coordinate space.
|
|
760
|
+
* @see {@link convertToViewportPoint}
|
|
761
|
+
*/
|
|
762
|
+
convertToPdfPoint(x, y) {
|
|
763
|
+
return Util.applyInverseTransform([x, y], this.transform);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
class RenderingCancelledException extends BaseException {
|
|
767
|
+
constructor(msg, extraDelay = 0) {
|
|
768
|
+
super(msg, "RenderingCancelledException");
|
|
769
|
+
this.extraDelay = extraDelay;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
function isDataScheme(url) {
|
|
773
|
+
const ii = url.length;
|
|
774
|
+
let i = 0;
|
|
775
|
+
while (i < ii && url[i].trim() === "") {
|
|
776
|
+
i++;
|
|
777
|
+
}
|
|
778
|
+
return url.substring(i, i + 5).toLowerCase() === "data:";
|
|
779
|
+
}
|
|
780
|
+
function isPdfFile(filename) {
|
|
781
|
+
return typeof filename === "string" && /\.pdf$/i.test(filename);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Gets the filename from a given URL.
|
|
786
|
+
* @param {string} url
|
|
787
|
+
* @param {boolean} [onlyStripPath]
|
|
788
|
+
* @returns {string}
|
|
789
|
+
*/
|
|
790
|
+
function getFilenameFromUrl(url, onlyStripPath = false) {
|
|
791
|
+
if (!onlyStripPath) {
|
|
792
|
+
[url] = url.split(/[#?]/, 1);
|
|
793
|
+
}
|
|
794
|
+
return url.substring(url.lastIndexOf("/") + 1);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Returns the filename or guessed filename from the url (see issue 3455).
|
|
799
|
+
* @param {string} url - The original PDF location.
|
|
800
|
+
* @param {string} defaultFilename - The value returned if the filename is
|
|
801
|
+
* unknown, or the protocol is unsupported.
|
|
802
|
+
* @returns {string} Guessed PDF filename.
|
|
803
|
+
*/
|
|
804
|
+
function getPdfFilenameFromUrl(url, defaultFilename = "document.pdf") {
|
|
805
|
+
if (typeof url !== "string") {
|
|
806
|
+
return defaultFilename;
|
|
807
|
+
}
|
|
808
|
+
if (isDataScheme(url)) {
|
|
809
|
+
warn('getPdfFilenameFromUrl: ignore "data:"-URL for performance reasons.');
|
|
810
|
+
return defaultFilename;
|
|
811
|
+
}
|
|
812
|
+
const reURI = /^(?:(?:[^:]+:)?\/\/[^/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
|
|
813
|
+
// SCHEME HOST 1.PATH 2.QUERY 3.REF
|
|
814
|
+
// Pattern to get last matching NAME.pdf
|
|
815
|
+
const reFilename = /[^/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
|
|
816
|
+
const splitURI = reURI.exec(url);
|
|
817
|
+
let suggestedFilename = reFilename.exec(splitURI[1]) || reFilename.exec(splitURI[2]) || reFilename.exec(splitURI[3]);
|
|
818
|
+
if (suggestedFilename) {
|
|
819
|
+
suggestedFilename = suggestedFilename[0];
|
|
820
|
+
if (suggestedFilename.includes("%")) {
|
|
821
|
+
// URL-encoded %2Fpath%2Fto%2Ffile.pdf should be file.pdf
|
|
822
|
+
try {
|
|
823
|
+
suggestedFilename = reFilename.exec(decodeURIComponent(suggestedFilename))[0];
|
|
824
|
+
} catch {
|
|
825
|
+
// Possible (extremely rare) errors:
|
|
826
|
+
// URIError "Malformed URI", e.g. for "%AA.pdf"
|
|
827
|
+
// TypeError "null has no properties", e.g. for "%2F.pdf"
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
return suggestedFilename || defaultFilename;
|
|
832
|
+
}
|
|
833
|
+
class StatTimer {
|
|
834
|
+
started = Object.create(null);
|
|
835
|
+
times = [];
|
|
836
|
+
time(name) {
|
|
837
|
+
if (name in this.started) {
|
|
838
|
+
warn(`Timer is already running for ${name}`);
|
|
839
|
+
}
|
|
840
|
+
this.started[name] = Date.now();
|
|
841
|
+
}
|
|
842
|
+
timeEnd(name) {
|
|
843
|
+
if (!(name in this.started)) {
|
|
844
|
+
warn(`Timer has not been started for ${name}`);
|
|
845
|
+
}
|
|
846
|
+
this.times.push({
|
|
847
|
+
name,
|
|
848
|
+
start: this.started[name],
|
|
849
|
+
end: Date.now()
|
|
850
|
+
});
|
|
851
|
+
// Remove timer from started so it can be called again.
|
|
852
|
+
delete this.started[name];
|
|
853
|
+
}
|
|
854
|
+
toString() {
|
|
855
|
+
// Find the longest name for padding purposes.
|
|
856
|
+
const outBuf = [];
|
|
857
|
+
let longest = 0;
|
|
858
|
+
for (const {
|
|
859
|
+
name
|
|
860
|
+
} of this.times) {
|
|
861
|
+
longest = Math.max(name.length, longest);
|
|
862
|
+
}
|
|
863
|
+
for (const {
|
|
864
|
+
name,
|
|
865
|
+
start,
|
|
866
|
+
end
|
|
867
|
+
} of this.times) {
|
|
868
|
+
outBuf.push(`${name.padEnd(longest)} ${end - start}ms\n`);
|
|
869
|
+
}
|
|
870
|
+
return outBuf.join("");
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
function isValidFetchUrl(url, baseUrl) {
|
|
874
|
+
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
|
875
|
+
throw new Error("Not implemented: isValidFetchUrl");
|
|
876
|
+
}
|
|
877
|
+
try {
|
|
878
|
+
const {
|
|
879
|
+
protocol
|
|
880
|
+
} = baseUrl ? new URL(url, baseUrl) : new URL(url);
|
|
881
|
+
// The Fetch API only supports the http/https protocols, and not file/ftp.
|
|
882
|
+
return protocol === "http:" || protocol === "https:";
|
|
883
|
+
} catch {
|
|
884
|
+
return false; // `new URL()` will throw on incorrect data.
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* Event handler to suppress context menu.
|
|
890
|
+
*/
|
|
891
|
+
function noContextMenu(e) {
|
|
892
|
+
e.preventDefault();
|
|
893
|
+
}
|
|
894
|
+
let pdfDateStringRegex;
|
|
895
|
+
class PDFDateString {
|
|
896
|
+
/**
|
|
897
|
+
* Convert a PDF date string to a JavaScript `Date` object.
|
|
898
|
+
*
|
|
899
|
+
* The PDF date string format is described in section 7.9.4 of the official
|
|
900
|
+
* PDF 32000-1:2008 specification. However, in the PDF 1.7 reference (sixth
|
|
901
|
+
* edition) Adobe describes the same format including a trailing apostrophe.
|
|
902
|
+
* This syntax in incorrect, but Adobe Acrobat creates PDF files that contain
|
|
903
|
+
* them. We ignore all apostrophes as they are not necessary for date parsing.
|
|
904
|
+
*
|
|
905
|
+
* Moreover, Adobe Acrobat doesn't handle changing the date to universal time
|
|
906
|
+
* and doesn't use the user's time zone (effectively ignoring the HH' and mm'
|
|
907
|
+
* parts of the date string).
|
|
908
|
+
*
|
|
909
|
+
* @param {string} input
|
|
910
|
+
* @returns {Date|null}
|
|
911
|
+
*/
|
|
912
|
+
static toDateObject(input) {
|
|
913
|
+
if (!input || typeof input !== "string") {
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// Lazily initialize the regular expression.
|
|
918
|
+
pdfDateStringRegex || (pdfDateStringRegex = new RegExp("^D:" +
|
|
919
|
+
// Prefix (required)
|
|
920
|
+
"(\\d{4})" +
|
|
921
|
+
// Year (required)
|
|
922
|
+
"(\\d{2})?" +
|
|
923
|
+
// Month (optional)
|
|
924
|
+
"(\\d{2})?" +
|
|
925
|
+
// Day (optional)
|
|
926
|
+
"(\\d{2})?" +
|
|
927
|
+
// Hour (optional)
|
|
928
|
+
"(\\d{2})?" +
|
|
929
|
+
// Minute (optional)
|
|
930
|
+
"(\\d{2})?" +
|
|
931
|
+
// Second (optional)
|
|
932
|
+
"([Z|+|-])?" +
|
|
933
|
+
// Universal time relation (optional)
|
|
934
|
+
"(\\d{2})?" +
|
|
935
|
+
// Offset hour (optional)
|
|
936
|
+
"'?" +
|
|
937
|
+
// Splitting apostrophe (optional)
|
|
938
|
+
"(\\d{2})?" +
|
|
939
|
+
// Offset minute (optional)
|
|
940
|
+
"'?" // Trailing apostrophe (optional)
|
|
941
|
+
));
|
|
942
|
+
|
|
943
|
+
// Optional fields that don't satisfy the requirements from the regular
|
|
944
|
+
// expression (such as incorrect digit counts or numbers that are out of
|
|
945
|
+
// range) will fall back the defaults from the specification.
|
|
946
|
+
const matches = pdfDateStringRegex.exec(input);
|
|
947
|
+
if (!matches) {
|
|
948
|
+
return null;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// JavaScript's `Date` object expects the month to be between 0 and 11
|
|
952
|
+
// instead of 1 and 12, so we have to correct for that.
|
|
953
|
+
const year = parseInt(matches[1], 10);
|
|
954
|
+
let month = parseInt(matches[2], 10);
|
|
955
|
+
month = month >= 1 && month <= 12 ? month - 1 : 0;
|
|
956
|
+
let day = parseInt(matches[3], 10);
|
|
957
|
+
day = day >= 1 && day <= 31 ? day : 1;
|
|
958
|
+
let hour = parseInt(matches[4], 10);
|
|
959
|
+
hour = hour >= 0 && hour <= 23 ? hour : 0;
|
|
960
|
+
let minute = parseInt(matches[5], 10);
|
|
961
|
+
minute = minute >= 0 && minute <= 59 ? minute : 0;
|
|
962
|
+
let second = parseInt(matches[6], 10);
|
|
963
|
+
second = second >= 0 && second <= 59 ? second : 0;
|
|
964
|
+
const universalTimeRelation = matches[7] || "Z";
|
|
965
|
+
let offsetHour = parseInt(matches[8], 10);
|
|
966
|
+
offsetHour = offsetHour >= 0 && offsetHour <= 23 ? offsetHour : 0;
|
|
967
|
+
let offsetMinute = parseInt(matches[9], 10) || 0;
|
|
968
|
+
offsetMinute = offsetMinute >= 0 && offsetMinute <= 59 ? offsetMinute : 0;
|
|
969
|
+
|
|
970
|
+
// Universal time relation 'Z' means that the local time is equal to the
|
|
971
|
+
// universal time, whereas the relations '+'/'-' indicate that the local
|
|
972
|
+
// time is later respectively earlier than the universal time. Every date
|
|
973
|
+
// is normalized to universal time.
|
|
974
|
+
if (universalTimeRelation === "-") {
|
|
975
|
+
hour += offsetHour;
|
|
976
|
+
minute += offsetMinute;
|
|
977
|
+
} else if (universalTimeRelation === "+") {
|
|
978
|
+
hour -= offsetHour;
|
|
979
|
+
minute -= offsetMinute;
|
|
980
|
+
}
|
|
981
|
+
return new Date(Date.UTC(year, month, day, hour, minute, second));
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/**
|
|
986
|
+
* NOTE: This is (mostly) intended to support printing of XFA forms.
|
|
987
|
+
*/
|
|
988
|
+
function getXfaPageViewport(xfaPage, {
|
|
989
|
+
scale = 1,
|
|
990
|
+
rotation = 0
|
|
991
|
+
}) {
|
|
992
|
+
const {
|
|
993
|
+
width,
|
|
994
|
+
height
|
|
995
|
+
} = xfaPage.attributes.style;
|
|
996
|
+
const viewBox = [0, 0, parseInt(width), parseInt(height)];
|
|
997
|
+
return new PageViewport({
|
|
998
|
+
viewBox,
|
|
999
|
+
scale,
|
|
1000
|
+
rotation
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
function getRGB(color) {
|
|
1004
|
+
if (color.startsWith("#")) {
|
|
1005
|
+
const colorRGB = parseInt(color.slice(1), 16);
|
|
1006
|
+
return [(colorRGB & 0xff0000) >> 16, (colorRGB & 0x00ff00) >> 8, colorRGB & 0x0000ff];
|
|
1007
|
+
}
|
|
1008
|
+
if (color.startsWith("rgb(")) {
|
|
1009
|
+
// getComputedStyle(...).color returns a `rgb(R, G, B)` color.
|
|
1010
|
+
return color.slice(/* "rgb(".length */4, -1) // Strip out "rgb(" and ")".
|
|
1011
|
+
.split(",").map(x => parseInt(x));
|
|
1012
|
+
}
|
|
1013
|
+
if (color.startsWith("rgba(")) {
|
|
1014
|
+
return color.slice(/* "rgba(".length */5, -1) // Strip out "rgba(" and ")".
|
|
1015
|
+
.split(",").map(x => parseInt(x)).slice(0, 3);
|
|
1016
|
+
}
|
|
1017
|
+
warn(`Not a valid color format: "${color}"`);
|
|
1018
|
+
return [0, 0, 0];
|
|
1019
|
+
}
|
|
1020
|
+
function getColorValues(colors) {
|
|
1021
|
+
const span = document.createElement("span");
|
|
1022
|
+
span.style.visibility = "hidden";
|
|
1023
|
+
document.body.append(span);
|
|
1024
|
+
for (const name of colors.keys()) {
|
|
1025
|
+
span.style.color = name;
|
|
1026
|
+
const computedColor = window.getComputedStyle(span).color;
|
|
1027
|
+
colors.set(name, getRGB(computedColor));
|
|
1028
|
+
}
|
|
1029
|
+
span.remove();
|
|
1030
|
+
}
|
|
1031
|
+
function getCurrentTransform(ctx) {
|
|
1032
|
+
const {
|
|
1033
|
+
a,
|
|
1034
|
+
b,
|
|
1035
|
+
c,
|
|
1036
|
+
d,
|
|
1037
|
+
e,
|
|
1038
|
+
f
|
|
1039
|
+
} = ctx.getTransform();
|
|
1040
|
+
return [a, b, c, d, e, f];
|
|
1041
|
+
}
|
|
1042
|
+
function getCurrentTransformInverse(ctx) {
|
|
1043
|
+
const {
|
|
1044
|
+
a,
|
|
1045
|
+
b,
|
|
1046
|
+
c,
|
|
1047
|
+
d,
|
|
1048
|
+
e,
|
|
1049
|
+
f
|
|
1050
|
+
} = ctx.getTransform().invertSelf();
|
|
1051
|
+
return [a, b, c, d, e, f];
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* @param {HTMLDivElement} div
|
|
1056
|
+
* @param {PageViewport} viewport
|
|
1057
|
+
* @param {boolean} mustFlip
|
|
1058
|
+
* @param {boolean} mustRotate
|
|
1059
|
+
*/
|
|
1060
|
+
function setLayerDimensions(div, viewport, mustFlip = false, mustRotate = true) {
|
|
1061
|
+
if (viewport instanceof PageViewport) {
|
|
1062
|
+
const {
|
|
1063
|
+
pageWidth,
|
|
1064
|
+
pageHeight
|
|
1065
|
+
} = viewport.rawDims;
|
|
1066
|
+
const {
|
|
1067
|
+
style
|
|
1068
|
+
} = div;
|
|
1069
|
+
const useRound = FeatureTest.isCSSRoundSupported;
|
|
1070
|
+
const w = `var(--scale-factor) * ${pageWidth}px`,
|
|
1071
|
+
h = `var(--scale-factor) * ${pageHeight}px`;
|
|
1072
|
+
const widthStr = useRound ? `round(${w}, 1px)` : `calc(${w})`,
|
|
1073
|
+
heightStr = useRound ? `round(${h}, 1px)` : `calc(${h})`;
|
|
1074
|
+
if (!mustFlip || viewport.rotation % 180 === 0) {
|
|
1075
|
+
style.width = widthStr;
|
|
1076
|
+
style.height = heightStr;
|
|
1077
|
+
} else {
|
|
1078
|
+
style.width = heightStr;
|
|
1079
|
+
style.height = widthStr;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
if (mustRotate) {
|
|
1083
|
+
div.setAttribute("data-main-rotation", viewport.rotation);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
/* Copyright 2017 Mozilla Foundation
|
|
1088
|
+
*
|
|
1089
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1090
|
+
* you may not use this file except in compliance with the License.
|
|
1091
|
+
* You may obtain a copy of the License at
|
|
1092
|
+
*
|
|
1093
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1094
|
+
*
|
|
1095
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1096
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1097
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1098
|
+
* See the License for the specific language governing permissions and
|
|
1099
|
+
* limitations under the License.
|
|
1100
|
+
*/
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
// This getFilenameFromContentDispositionHeader function is adapted from
|
|
1104
|
+
// https://github.com/Rob--W/open-in-browser/blob/7e2e35a38b8b4e981b11da7b2f01df0149049e92/extension/content-disposition.js
|
|
1105
|
+
// with the following changes:
|
|
1106
|
+
// - Modified to conform to PDF.js's coding style.
|
|
1107
|
+
// - Move return to the end of the function to prevent Babel from dropping the
|
|
1108
|
+
// function declarations.
|
|
1109
|
+
|
|
1110
|
+
/**
|
|
1111
|
+
* Extract file name from the Content-Disposition HTTP response header.
|
|
1112
|
+
*
|
|
1113
|
+
* @param {string} contentDisposition
|
|
1114
|
+
* @returns {string} Filename, if found in the Content-Disposition header.
|
|
1115
|
+
*/
|
|
1116
|
+
function getFilenameFromContentDispositionHeader(contentDisposition) {
|
|
1117
|
+
let needsEncodingFixup = true;
|
|
1118
|
+
|
|
1119
|
+
// filename*=ext-value ("ext-value" from RFC 5987, referenced by RFC 6266).
|
|
1120
|
+
let tmp = toParamRegExp("filename\\*", "i").exec(contentDisposition);
|
|
1121
|
+
if (tmp) {
|
|
1122
|
+
tmp = tmp[1];
|
|
1123
|
+
let filename = rfc2616unquote(tmp);
|
|
1124
|
+
filename = unescape(filename);
|
|
1125
|
+
filename = rfc5987decode(filename);
|
|
1126
|
+
filename = rfc2047decode(filename);
|
|
1127
|
+
return fixupEncoding(filename);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
// Continuations (RFC 2231 section 3, referenced by RFC 5987 section 3.1).
|
|
1131
|
+
// filename*n*=part
|
|
1132
|
+
// filename*n=part
|
|
1133
|
+
tmp = rfc2231getparam(contentDisposition);
|
|
1134
|
+
if (tmp) {
|
|
1135
|
+
// RFC 2047, section
|
|
1136
|
+
const filename = rfc2047decode(tmp);
|
|
1137
|
+
return fixupEncoding(filename);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// filename=value (RFC 5987, section 4.1).
|
|
1141
|
+
tmp = toParamRegExp("filename", "i").exec(contentDisposition);
|
|
1142
|
+
if (tmp) {
|
|
1143
|
+
tmp = tmp[1];
|
|
1144
|
+
let filename = rfc2616unquote(tmp);
|
|
1145
|
+
filename = rfc2047decode(filename);
|
|
1146
|
+
return fixupEncoding(filename);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// After this line there are only function declarations. We cannot put
|
|
1150
|
+
// "return" here for readability because babel would then drop the function
|
|
1151
|
+
// declarations...
|
|
1152
|
+
function toParamRegExp(attributePattern, flags) {
|
|
1153
|
+
return new RegExp("(?:^|;)\\s*" + attributePattern + "\\s*=\\s*" +
|
|
1154
|
+
// Captures: value = token | quoted-string
|
|
1155
|
+
// (RFC 2616, section 3.6 and referenced by RFC 6266 4.1)
|
|
1156
|
+
"(" + '[^";\\s][^;\\s]*' + "|" + '"(?:[^"\\\\]|\\\\"?)+"?' + ")", flags);
|
|
1157
|
+
}
|
|
1158
|
+
function textdecode(encoding, value) {
|
|
1159
|
+
if (encoding) {
|
|
1160
|
+
if (!/^[\x00-\xFF]+$/.test(value)) {
|
|
1161
|
+
return value;
|
|
1162
|
+
}
|
|
1163
|
+
try {
|
|
1164
|
+
const decoder = new TextDecoder(encoding, {
|
|
1165
|
+
fatal: true
|
|
1166
|
+
});
|
|
1167
|
+
const buffer = stringToBytes(value);
|
|
1168
|
+
value = decoder.decode(buffer);
|
|
1169
|
+
needsEncodingFixup = false;
|
|
1170
|
+
} catch {
|
|
1171
|
+
// TextDecoder constructor threw - unrecognized encoding.
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
return value;
|
|
1175
|
+
}
|
|
1176
|
+
function fixupEncoding(value) {
|
|
1177
|
+
if (needsEncodingFixup && /[\x80-\xff]/.test(value)) {
|
|
1178
|
+
// Maybe multi-byte UTF-8.
|
|
1179
|
+
value = textdecode("utf-8", value);
|
|
1180
|
+
if (needsEncodingFixup) {
|
|
1181
|
+
// Try iso-8859-1 encoding.
|
|
1182
|
+
value = textdecode("iso-8859-1", value);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
return value;
|
|
1186
|
+
}
|
|
1187
|
+
function rfc2231getparam(contentDispositionStr) {
|
|
1188
|
+
const matches = [];
|
|
1189
|
+
let match;
|
|
1190
|
+
// Iterate over all filename*n= and filename*n*= with n being an integer
|
|
1191
|
+
// of at least zero. Any non-zero number must not start with '0'.
|
|
1192
|
+
const iter = toParamRegExp("filename\\*((?!0\\d)\\d+)(\\*?)", "ig");
|
|
1193
|
+
while ((match = iter.exec(contentDispositionStr)) !== null) {
|
|
1194
|
+
let [, n, quot, part] = match; // eslint-disable-line prefer-const
|
|
1195
|
+
n = parseInt(n, 10);
|
|
1196
|
+
if (n in matches) {
|
|
1197
|
+
// Ignore anything after the invalid second filename*0.
|
|
1198
|
+
if (n === 0) {
|
|
1199
|
+
break;
|
|
1200
|
+
}
|
|
1201
|
+
continue;
|
|
1202
|
+
}
|
|
1203
|
+
matches[n] = [quot, part];
|
|
1204
|
+
}
|
|
1205
|
+
const parts = [];
|
|
1206
|
+
for (let n = 0; n < matches.length; ++n) {
|
|
1207
|
+
if (!(n in matches)) {
|
|
1208
|
+
// Numbers must be consecutive. Truncate when there is a hole.
|
|
1209
|
+
break;
|
|
1210
|
+
}
|
|
1211
|
+
let [quot, part] = matches[n]; // eslint-disable-line prefer-const
|
|
1212
|
+
part = rfc2616unquote(part);
|
|
1213
|
+
if (quot) {
|
|
1214
|
+
part = unescape(part);
|
|
1215
|
+
if (n === 0) {
|
|
1216
|
+
part = rfc5987decode(part);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
parts.push(part);
|
|
1220
|
+
}
|
|
1221
|
+
return parts.join("");
|
|
1222
|
+
}
|
|
1223
|
+
function rfc2616unquote(value) {
|
|
1224
|
+
if (value.startsWith('"')) {
|
|
1225
|
+
const parts = value.slice(1).split('\\"');
|
|
1226
|
+
// Find the first unescaped " and terminate there.
|
|
1227
|
+
for (let i = 0; i < parts.length; ++i) {
|
|
1228
|
+
const quotindex = parts[i].indexOf('"');
|
|
1229
|
+
if (quotindex !== -1) {
|
|
1230
|
+
parts[i] = parts[i].slice(0, quotindex);
|
|
1231
|
+
parts.length = i + 1; // Truncates and stop the iteration.
|
|
1232
|
+
}
|
|
1233
|
+
parts[i] = parts[i].replaceAll(/\\(.)/g, "$1");
|
|
1234
|
+
}
|
|
1235
|
+
value = parts.join('"');
|
|
1236
|
+
}
|
|
1237
|
+
return value;
|
|
1238
|
+
}
|
|
1239
|
+
function rfc5987decode(extvalue) {
|
|
1240
|
+
// Decodes "ext-value" from RFC 5987.
|
|
1241
|
+
const encodingend = extvalue.indexOf("'");
|
|
1242
|
+
if (encodingend === -1) {
|
|
1243
|
+
// Some servers send "filename*=" without encoding 'language' prefix,
|
|
1244
|
+
// e.g. in https://github.com/Rob--W/open-in-browser/issues/26
|
|
1245
|
+
// Let's accept the value like Firefox (57) (Chrome 62 rejects it).
|
|
1246
|
+
return extvalue;
|
|
1247
|
+
}
|
|
1248
|
+
const encoding = extvalue.slice(0, encodingend);
|
|
1249
|
+
const langvalue = extvalue.slice(encodingend + 1);
|
|
1250
|
+
// Ignore language (RFC 5987 section 3.2.1, and RFC 6266 section 4.1 ).
|
|
1251
|
+
const value = langvalue.replace(/^[^']*'/, "");
|
|
1252
|
+
return textdecode(encoding, value);
|
|
1253
|
+
}
|
|
1254
|
+
function rfc2047decode(value) {
|
|
1255
|
+
// RFC 2047-decode the result. Firefox tried to drop support for it, but
|
|
1256
|
+
// backed out because some servers use it - https://bugzil.la/875615
|
|
1257
|
+
// Firefox's condition for decoding is here: https://searchfox.org/mozilla-central/rev/4a590a5a15e35d88a3b23dd6ac3c471cf85b04a8/netwerk/mime/nsMIMEHeaderParamImpl.cpp#742-748
|
|
1258
|
+
|
|
1259
|
+
// We are more strict and only recognize RFC 2047-encoding if the value
|
|
1260
|
+
// starts with "=?", since then it is likely that the full value is
|
|
1261
|
+
// RFC 2047-encoded.
|
|
1262
|
+
|
|
1263
|
+
// Firefox also decodes words even where RFC 2047 section 5 states:
|
|
1264
|
+
// "An 'encoded-word' MUST NOT appear within a 'quoted-string'."
|
|
1265
|
+
if (!value.startsWith("=?") || /[\x00-\x19\x80-\xff]/.test(value)) {
|
|
1266
|
+
return value;
|
|
1267
|
+
}
|
|
1268
|
+
// RFC 2047, section 2.4
|
|
1269
|
+
// encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
|
|
1270
|
+
// charset = token (but let's restrict to characters that denote a
|
|
1271
|
+
// possibly valid encoding).
|
|
1272
|
+
// encoding = q or b
|
|
1273
|
+
// encoded-text = any printable ASCII character other than ? or space.
|
|
1274
|
+
// ... but Firefox permits ? and space.
|
|
1275
|
+
return value.replaceAll(/=\?([\w-]*)\?([QqBb])\?((?:[^?]|\?(?!=))*)\?=/g, function (matches, charset, encoding, text) {
|
|
1276
|
+
if (encoding === "q" || encoding === "Q") {
|
|
1277
|
+
// RFC 2047 section 4.2.
|
|
1278
|
+
text = text.replaceAll("_", " ");
|
|
1279
|
+
text = text.replaceAll(/=([0-9a-fA-F]{2})/g, function (match, hex) {
|
|
1280
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
1281
|
+
});
|
|
1282
|
+
return textdecode(charset, text);
|
|
1283
|
+
} // else encoding is b or B - base64 (RFC 2047 section 4.1)
|
|
1284
|
+
try {
|
|
1285
|
+
text = atob(text);
|
|
1286
|
+
} catch {}
|
|
1287
|
+
return textdecode(charset, text);
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
return "";
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
/* Copyright 2012 Mozilla Foundation
|
|
1294
|
+
*
|
|
1295
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1296
|
+
* you may not use this file except in compliance with the License.
|
|
1297
|
+
* You may obtain a copy of the License at
|
|
1298
|
+
*
|
|
1299
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1300
|
+
*
|
|
1301
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1302
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1303
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1304
|
+
* See the License for the specific language governing permissions and
|
|
1305
|
+
* limitations under the License.
|
|
1306
|
+
*/
|
|
1307
|
+
|
|
1308
|
+
function validateRangeRequestCapabilities({
|
|
1309
|
+
getResponseHeader,
|
|
1310
|
+
isHttp,
|
|
1311
|
+
rangeChunkSize,
|
|
1312
|
+
disableRange
|
|
1313
|
+
}) {
|
|
1314
|
+
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
|
1315
|
+
assert(Number.isInteger(rangeChunkSize) && rangeChunkSize > 0, "rangeChunkSize must be an integer larger than zero.");
|
|
1316
|
+
}
|
|
1317
|
+
const returnValues = {
|
|
1318
|
+
allowRangeRequests: false,
|
|
1319
|
+
suggestedLength: undefined
|
|
1320
|
+
};
|
|
1321
|
+
const length = parseInt(getResponseHeader("Content-Length"), 10);
|
|
1322
|
+
if (!Number.isInteger(length)) {
|
|
1323
|
+
return returnValues;
|
|
1324
|
+
}
|
|
1325
|
+
returnValues.suggestedLength = length;
|
|
1326
|
+
if (length <= 2 * rangeChunkSize) {
|
|
1327
|
+
// The file size is smaller than the size of two chunks, so it does not
|
|
1328
|
+
// make any sense to abort the request and retry with a range request.
|
|
1329
|
+
return returnValues;
|
|
1330
|
+
}
|
|
1331
|
+
if (disableRange || !isHttp) {
|
|
1332
|
+
return returnValues;
|
|
1333
|
+
}
|
|
1334
|
+
if (getResponseHeader("Accept-Ranges") !== "bytes") {
|
|
1335
|
+
return returnValues;
|
|
1336
|
+
}
|
|
1337
|
+
const contentEncoding = getResponseHeader("Content-Encoding") || "identity";
|
|
1338
|
+
if (contentEncoding !== "identity") {
|
|
1339
|
+
return returnValues;
|
|
1340
|
+
}
|
|
1341
|
+
returnValues.allowRangeRequests = true;
|
|
1342
|
+
return returnValues;
|
|
1343
|
+
}
|
|
1344
|
+
function extractFilenameFromHeader(getResponseHeader) {
|
|
1345
|
+
const contentDisposition = getResponseHeader("Content-Disposition");
|
|
1346
|
+
if (contentDisposition) {
|
|
1347
|
+
let filename = getFilenameFromContentDispositionHeader(contentDisposition);
|
|
1348
|
+
if (filename.includes("%")) {
|
|
1349
|
+
try {
|
|
1350
|
+
filename = decodeURIComponent(filename);
|
|
1351
|
+
} catch {}
|
|
1352
|
+
}
|
|
1353
|
+
if (isPdfFile(filename)) {
|
|
1354
|
+
return filename;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
return null;
|
|
1358
|
+
}
|
|
1359
|
+
function createResponseStatusError(status, url) {
|
|
1360
|
+
if (status === 404 || status === 0 && url.startsWith("file:")) {
|
|
1361
|
+
return new MissingPDFException('Missing PDF "' + url + '".');
|
|
1362
|
+
}
|
|
1363
|
+
return new UnexpectedResponseException(`Unexpected server response (${status}) while retrieving PDF "${url}".`, status);
|
|
1364
|
+
}
|
|
1365
|
+
function validateResponseStatus(status) {
|
|
1366
|
+
return status === 200 || status === 206;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
/* Copyright 2012 Mozilla Foundation
|
|
1370
|
+
*
|
|
1371
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1372
|
+
* you may not use this file except in compliance with the License.
|
|
1373
|
+
* You may obtain a copy of the License at
|
|
1374
|
+
*
|
|
1375
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1376
|
+
*
|
|
1377
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1378
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1379
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1380
|
+
* See the License for the specific language governing permissions and
|
|
1381
|
+
* limitations under the License.
|
|
1382
|
+
*/
|
|
1383
|
+
|
|
1384
|
+
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
|
1385
|
+
throw new Error('Module "./node_stream.js" shall not be used with MOZCENTRAL builds.');
|
|
1386
|
+
}
|
|
1387
|
+
let fs, http, https, url;
|
|
1388
|
+
if (isNodeJS) {
|
|
1389
|
+
// Native packages.
|
|
1390
|
+
fs = await __non_webpack_import__("fs");
|
|
1391
|
+
http = await __non_webpack_import__("http");
|
|
1392
|
+
https = await __non_webpack_import__("https");
|
|
1393
|
+
url = await __non_webpack_import__("url");
|
|
1394
|
+
}
|
|
1395
|
+
const fileUriRegex = /^file:\/\/\/[a-zA-Z]:\//;
|
|
1396
|
+
function parseUrl(sourceUrl) {
|
|
1397
|
+
const parsedUrl = url.parse(sourceUrl);
|
|
1398
|
+
if (parsedUrl.protocol === "file:" || parsedUrl.host) {
|
|
1399
|
+
return parsedUrl;
|
|
1400
|
+
}
|
|
1401
|
+
// Prepending 'file:///' to Windows absolute path.
|
|
1402
|
+
if (/^[a-z]:[/\\]/i.test(sourceUrl)) {
|
|
1403
|
+
return url.parse(`file:///${sourceUrl}`);
|
|
1404
|
+
}
|
|
1405
|
+
// Changes protocol to 'file:' if url refers to filesystem.
|
|
1406
|
+
if (!parsedUrl.host) {
|
|
1407
|
+
parsedUrl.protocol = "file:";
|
|
1408
|
+
}
|
|
1409
|
+
return parsedUrl;
|
|
1410
|
+
}
|
|
1411
|
+
class PDFNodeStream {
|
|
1412
|
+
constructor(source) {
|
|
1413
|
+
this.source = source;
|
|
1414
|
+
this.url = parseUrl(source.url);
|
|
1415
|
+
this.isHttp = this.url.protocol === "http:" || this.url.protocol === "https:";
|
|
1416
|
+
// Check if url refers to filesystem.
|
|
1417
|
+
this.isFsUrl = this.url.protocol === "file:";
|
|
1418
|
+
this.httpHeaders = this.isHttp && source.httpHeaders || {};
|
|
1419
|
+
this._fullRequestReader = null;
|
|
1420
|
+
this._rangeRequestReaders = [];
|
|
1421
|
+
}
|
|
1422
|
+
get _progressiveDataLength() {
|
|
1423
|
+
var _this$_fullRequestRea, _this$_fullRequestRea2;
|
|
1424
|
+
return (_this$_fullRequestRea = (_this$_fullRequestRea2 = this._fullRequestReader) === null || _this$_fullRequestRea2 === void 0 ? void 0 : _this$_fullRequestRea2._loaded) !== null && _this$_fullRequestRea !== void 0 ? _this$_fullRequestRea : 0;
|
|
1425
|
+
}
|
|
1426
|
+
getFullReader() {
|
|
1427
|
+
assert(!this._fullRequestReader, "PDFNodeStream.getFullReader can only be called once.");
|
|
1428
|
+
this._fullRequestReader = this.isFsUrl ? new PDFNodeStreamFsFullReader(this) : new PDFNodeStreamFullReader(this);
|
|
1429
|
+
return this._fullRequestReader;
|
|
1430
|
+
}
|
|
1431
|
+
getRangeReader(start, end) {
|
|
1432
|
+
if (end <= this._progressiveDataLength) {
|
|
1433
|
+
return null;
|
|
1434
|
+
}
|
|
1435
|
+
const rangeReader = this.isFsUrl ? new PDFNodeStreamFsRangeReader(this, start, end) : new PDFNodeStreamRangeReader(this, start, end);
|
|
1436
|
+
this._rangeRequestReaders.push(rangeReader);
|
|
1437
|
+
return rangeReader;
|
|
1438
|
+
}
|
|
1439
|
+
cancelAllRequests(reason) {
|
|
1440
|
+
var _this$_fullRequestRea3;
|
|
1441
|
+
(_this$_fullRequestRea3 = this._fullRequestReader) === null || _this$_fullRequestRea3 === void 0 ? void 0 : _this$_fullRequestRea3.cancel(reason);
|
|
1442
|
+
for (const reader of this._rangeRequestReaders.slice(0)) {
|
|
1443
|
+
reader.cancel(reason);
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
class BaseFullReader {
|
|
1448
|
+
constructor(stream) {
|
|
1449
|
+
this._url = stream.url;
|
|
1450
|
+
this._done = false;
|
|
1451
|
+
this._storedError = null;
|
|
1452
|
+
this.onProgress = null;
|
|
1453
|
+
const source = stream.source;
|
|
1454
|
+
this._contentLength = source.length; // optional
|
|
1455
|
+
this._loaded = 0;
|
|
1456
|
+
this._filename = null;
|
|
1457
|
+
this._disableRange = source.disableRange || false;
|
|
1458
|
+
this._rangeChunkSize = source.rangeChunkSize;
|
|
1459
|
+
if (!this._rangeChunkSize && !this._disableRange) {
|
|
1460
|
+
this._disableRange = true;
|
|
1461
|
+
}
|
|
1462
|
+
this._isStreamingSupported = !source.disableStream;
|
|
1463
|
+
this._isRangeSupported = !source.disableRange;
|
|
1464
|
+
this._readableStream = null;
|
|
1465
|
+
this._readCapability = Promise.withResolvers();
|
|
1466
|
+
this._headersCapability = Promise.withResolvers();
|
|
1467
|
+
}
|
|
1468
|
+
get headersReady() {
|
|
1469
|
+
return this._headersCapability.promise;
|
|
1470
|
+
}
|
|
1471
|
+
get filename() {
|
|
1472
|
+
return this._filename;
|
|
1473
|
+
}
|
|
1474
|
+
get contentLength() {
|
|
1475
|
+
return this._contentLength;
|
|
1476
|
+
}
|
|
1477
|
+
get isRangeSupported() {
|
|
1478
|
+
return this._isRangeSupported;
|
|
1479
|
+
}
|
|
1480
|
+
get isStreamingSupported() {
|
|
1481
|
+
return this._isStreamingSupported;
|
|
1482
|
+
}
|
|
1483
|
+
async read() {
|
|
1484
|
+
var _this$onProgress;
|
|
1485
|
+
await this._readCapability.promise;
|
|
1486
|
+
if (this._done) {
|
|
1487
|
+
return {
|
|
1488
|
+
value: undefined,
|
|
1489
|
+
done: true
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
if (this._storedError) {
|
|
1493
|
+
throw this._storedError;
|
|
1494
|
+
}
|
|
1495
|
+
const chunk = this._readableStream.read();
|
|
1496
|
+
if (chunk === null) {
|
|
1497
|
+
this._readCapability = Promise.withResolvers();
|
|
1498
|
+
return this.read();
|
|
1499
|
+
}
|
|
1500
|
+
this._loaded += chunk.length;
|
|
1501
|
+
(_this$onProgress = this.onProgress) === null || _this$onProgress === void 0 ? void 0 : _this$onProgress.call(this, {
|
|
1502
|
+
loaded: this._loaded,
|
|
1503
|
+
total: this._contentLength
|
|
1504
|
+
});
|
|
1505
|
+
|
|
1506
|
+
// Ensure that `read()` method returns ArrayBuffer.
|
|
1507
|
+
const buffer = new Uint8Array(chunk).buffer;
|
|
1508
|
+
return {
|
|
1509
|
+
value: buffer,
|
|
1510
|
+
done: false
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
cancel(reason) {
|
|
1514
|
+
// Call `this._error()` method when cancel is called
|
|
1515
|
+
// before _readableStream is set.
|
|
1516
|
+
if (!this._readableStream) {
|
|
1517
|
+
this._error(reason);
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
this._readableStream.destroy(reason);
|
|
1521
|
+
}
|
|
1522
|
+
_error(reason) {
|
|
1523
|
+
this._storedError = reason;
|
|
1524
|
+
this._readCapability.resolve();
|
|
1525
|
+
}
|
|
1526
|
+
_setReadableStream(readableStream) {
|
|
1527
|
+
this._readableStream = readableStream;
|
|
1528
|
+
readableStream.on("readable", () => {
|
|
1529
|
+
this._readCapability.resolve();
|
|
1530
|
+
});
|
|
1531
|
+
readableStream.on("end", () => {
|
|
1532
|
+
// Destroy readable to minimize resource usage.
|
|
1533
|
+
readableStream.destroy();
|
|
1534
|
+
this._done = true;
|
|
1535
|
+
this._readCapability.resolve();
|
|
1536
|
+
});
|
|
1537
|
+
readableStream.on("error", reason => {
|
|
1538
|
+
this._error(reason);
|
|
1539
|
+
});
|
|
1540
|
+
|
|
1541
|
+
// We need to stop reading when range is supported and streaming is
|
|
1542
|
+
// disabled.
|
|
1543
|
+
if (!this._isStreamingSupported && this._isRangeSupported) {
|
|
1544
|
+
this._error(new AbortException("streaming is disabled"));
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
// Destroy ReadableStream if already in errored state.
|
|
1548
|
+
if (this._storedError) {
|
|
1549
|
+
this._readableStream.destroy(this._storedError);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
class BaseRangeReader {
|
|
1554
|
+
constructor(stream) {
|
|
1555
|
+
this._url = stream.url;
|
|
1556
|
+
this._done = false;
|
|
1557
|
+
this._storedError = null;
|
|
1558
|
+
this.onProgress = null;
|
|
1559
|
+
this._loaded = 0;
|
|
1560
|
+
this._readableStream = null;
|
|
1561
|
+
this._readCapability = Promise.withResolvers();
|
|
1562
|
+
const source = stream.source;
|
|
1563
|
+
this._isStreamingSupported = !source.disableStream;
|
|
1564
|
+
}
|
|
1565
|
+
get isStreamingSupported() {
|
|
1566
|
+
return this._isStreamingSupported;
|
|
1567
|
+
}
|
|
1568
|
+
async read() {
|
|
1569
|
+
var _this$onProgress2;
|
|
1570
|
+
await this._readCapability.promise;
|
|
1571
|
+
if (this._done) {
|
|
1572
|
+
return {
|
|
1573
|
+
value: undefined,
|
|
1574
|
+
done: true
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
if (this._storedError) {
|
|
1578
|
+
throw this._storedError;
|
|
1579
|
+
}
|
|
1580
|
+
const chunk = this._readableStream.read();
|
|
1581
|
+
if (chunk === null) {
|
|
1582
|
+
this._readCapability = Promise.withResolvers();
|
|
1583
|
+
return this.read();
|
|
1584
|
+
}
|
|
1585
|
+
this._loaded += chunk.length;
|
|
1586
|
+
(_this$onProgress2 = this.onProgress) === null || _this$onProgress2 === void 0 ? void 0 : _this$onProgress2.call(this, {
|
|
1587
|
+
loaded: this._loaded
|
|
1588
|
+
});
|
|
1589
|
+
|
|
1590
|
+
// Ensure that `read()` method returns ArrayBuffer.
|
|
1591
|
+
const buffer = new Uint8Array(chunk).buffer;
|
|
1592
|
+
return {
|
|
1593
|
+
value: buffer,
|
|
1594
|
+
done: false
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
cancel(reason) {
|
|
1598
|
+
// Call `this._error()` method when cancel is called
|
|
1599
|
+
// before _readableStream is set.
|
|
1600
|
+
if (!this._readableStream) {
|
|
1601
|
+
this._error(reason);
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
this._readableStream.destroy(reason);
|
|
1605
|
+
}
|
|
1606
|
+
_error(reason) {
|
|
1607
|
+
this._storedError = reason;
|
|
1608
|
+
this._readCapability.resolve();
|
|
1609
|
+
}
|
|
1610
|
+
_setReadableStream(readableStream) {
|
|
1611
|
+
this._readableStream = readableStream;
|
|
1612
|
+
readableStream.on("readable", () => {
|
|
1613
|
+
this._readCapability.resolve();
|
|
1614
|
+
});
|
|
1615
|
+
readableStream.on("end", () => {
|
|
1616
|
+
// Destroy readableStream to minimize resource usage.
|
|
1617
|
+
readableStream.destroy();
|
|
1618
|
+
this._done = true;
|
|
1619
|
+
this._readCapability.resolve();
|
|
1620
|
+
});
|
|
1621
|
+
readableStream.on("error", reason => {
|
|
1622
|
+
this._error(reason);
|
|
1623
|
+
});
|
|
1624
|
+
|
|
1625
|
+
// Destroy readableStream if already in errored state.
|
|
1626
|
+
if (this._storedError) {
|
|
1627
|
+
this._readableStream.destroy(this._storedError);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
function createRequestOptions(parsedUrl, headers) {
|
|
1632
|
+
return {
|
|
1633
|
+
protocol: parsedUrl.protocol,
|
|
1634
|
+
auth: parsedUrl.auth,
|
|
1635
|
+
host: parsedUrl.hostname,
|
|
1636
|
+
port: parsedUrl.port,
|
|
1637
|
+
path: parsedUrl.path,
|
|
1638
|
+
method: "GET",
|
|
1639
|
+
headers
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
class PDFNodeStreamFullReader extends BaseFullReader {
|
|
1643
|
+
constructor(stream) {
|
|
1644
|
+
super(stream);
|
|
1645
|
+
const handleResponse = response => {
|
|
1646
|
+
if (response.statusCode === 404) {
|
|
1647
|
+
const error = new MissingPDFException(`Missing PDF "${this._url}".`);
|
|
1648
|
+
this._storedError = error;
|
|
1649
|
+
this._headersCapability.reject(error);
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
this._headersCapability.resolve();
|
|
1653
|
+
this._setReadableStream(response);
|
|
1654
|
+
|
|
1655
|
+
// Make sure that headers name are in lower case, as mentioned
|
|
1656
|
+
// here: https://nodejs.org/api/http.html#http_message_headers.
|
|
1657
|
+
const getResponseHeader = name => this._readableStream.headers[name.toLowerCase()];
|
|
1658
|
+
const {
|
|
1659
|
+
allowRangeRequests,
|
|
1660
|
+
suggestedLength
|
|
1661
|
+
} = validateRangeRequestCapabilities({
|
|
1662
|
+
getResponseHeader,
|
|
1663
|
+
isHttp: stream.isHttp,
|
|
1664
|
+
rangeChunkSize: this._rangeChunkSize,
|
|
1665
|
+
disableRange: this._disableRange
|
|
1666
|
+
});
|
|
1667
|
+
this._isRangeSupported = allowRangeRequests;
|
|
1668
|
+
// Setting right content length.
|
|
1669
|
+
this._contentLength = suggestedLength || this._contentLength;
|
|
1670
|
+
this._filename = extractFilenameFromHeader(getResponseHeader);
|
|
1671
|
+
};
|
|
1672
|
+
this._request = null;
|
|
1673
|
+
if (this._url.protocol === "http:") {
|
|
1674
|
+
this._request = http.request(createRequestOptions(this._url, stream.httpHeaders), handleResponse);
|
|
1675
|
+
} else {
|
|
1676
|
+
this._request = https.request(createRequestOptions(this._url, stream.httpHeaders), handleResponse);
|
|
1677
|
+
}
|
|
1678
|
+
this._request.on("error", reason => {
|
|
1679
|
+
this._storedError = reason;
|
|
1680
|
+
this._headersCapability.reject(reason);
|
|
1681
|
+
});
|
|
1682
|
+
// Note: `request.end(data)` is used to write `data` to request body
|
|
1683
|
+
// and notify end of request. But one should always call `request.end()`
|
|
1684
|
+
// even if there is no data to write -- (to notify the end of request).
|
|
1685
|
+
this._request.end();
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
class PDFNodeStreamRangeReader extends BaseRangeReader {
|
|
1689
|
+
constructor(stream, start, end) {
|
|
1690
|
+
super(stream);
|
|
1691
|
+
this._httpHeaders = {};
|
|
1692
|
+
for (const property in stream.httpHeaders) {
|
|
1693
|
+
const value = stream.httpHeaders[property];
|
|
1694
|
+
if (value === undefined) {
|
|
1695
|
+
continue;
|
|
1696
|
+
}
|
|
1697
|
+
this._httpHeaders[property] = value;
|
|
1698
|
+
}
|
|
1699
|
+
this._httpHeaders.Range = `bytes=${start}-${end - 1}`;
|
|
1700
|
+
const handleResponse = response => {
|
|
1701
|
+
if (response.statusCode === 404) {
|
|
1702
|
+
const error = new MissingPDFException(`Missing PDF "${this._url}".`);
|
|
1703
|
+
this._storedError = error;
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
this._setReadableStream(response);
|
|
1707
|
+
};
|
|
1708
|
+
this._request = null;
|
|
1709
|
+
if (this._url.protocol === "http:") {
|
|
1710
|
+
this._request = http.request(createRequestOptions(this._url, this._httpHeaders), handleResponse);
|
|
1711
|
+
} else {
|
|
1712
|
+
this._request = https.request(createRequestOptions(this._url, this._httpHeaders), handleResponse);
|
|
1713
|
+
}
|
|
1714
|
+
this._request.on("error", reason => {
|
|
1715
|
+
this._storedError = reason;
|
|
1716
|
+
});
|
|
1717
|
+
this._request.end();
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
class PDFNodeStreamFsFullReader extends BaseFullReader {
|
|
1721
|
+
constructor(stream) {
|
|
1722
|
+
super(stream);
|
|
1723
|
+
let path = decodeURIComponent(this._url.path);
|
|
1724
|
+
|
|
1725
|
+
// Remove the extra slash to get right path from url like `file:///C:/`
|
|
1726
|
+
if (fileUriRegex.test(this._url.href)) {
|
|
1727
|
+
path = path.replace(/^\//, "");
|
|
1728
|
+
}
|
|
1729
|
+
fs.promises.lstat(path).then(stat => {
|
|
1730
|
+
// Setting right content length.
|
|
1731
|
+
this._contentLength = stat.size;
|
|
1732
|
+
this._setReadableStream(fs.createReadStream(path));
|
|
1733
|
+
this._headersCapability.resolve();
|
|
1734
|
+
}, error => {
|
|
1735
|
+
if (error.code === "ENOENT") {
|
|
1736
|
+
error = new MissingPDFException(`Missing PDF "${path}".`);
|
|
1737
|
+
}
|
|
1738
|
+
this._storedError = error;
|
|
1739
|
+
this._headersCapability.reject(error);
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
class PDFNodeStreamFsRangeReader extends BaseRangeReader {
|
|
1744
|
+
constructor(stream, start, end) {
|
|
1745
|
+
super(stream);
|
|
1746
|
+
let path = decodeURIComponent(this._url.path);
|
|
1747
|
+
|
|
1748
|
+
// Remove the extra slash to get right path from url like `file:///C:/`
|
|
1749
|
+
if (fileUriRegex.test(this._url.href)) {
|
|
1750
|
+
path = path.replace(/^\//, "");
|
|
1751
|
+
}
|
|
1752
|
+
this._setReadableStream(fs.createReadStream(path, {
|
|
1753
|
+
start,
|
|
1754
|
+
end: end - 1
|
|
1755
|
+
}));
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
export { getXfaPageViewport as A, BaseCanvasFactory as B, DOMCanvasFactory as D, PixelsPerInch as P, RenderingCancelledException as R, StatTimer as S, getRGB as a, BaseCMapReaderFactory as b, BaseFilterFactory as c, BaseStandardFontDataFactory as d, getCurrentTransform as e, fetchData as f, getColorValues as g, getCurrentTransformInverse as h, isPdfFile as i, createResponseStatusError as j, validateRangeRequestCapabilities as k, extractFilenameFromHeader as l, DOMCMapReaderFactory as m, noContextMenu as n, DOMFilterFactory as o, DOMStandardFontDataFactory as p, isDataScheme as q, isValidFetchUrl as r, setLayerDimensions as s, PDFNodeStream as t, PageViewport as u, validateResponseStatus as v, PDFDateString as w, DOMSVGFactory as x, getFilenameFromUrl as y, getPdfFilenameFromUrl as z };
|