UNPKG

20.8 kBJavaScriptView Raw
1/**
2 * @licstart The following is the entire license notice for the
3 * Javascript code in this page
4 *
5 * Copyright 2018 Mozilla Foundation
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * @licend The above is the entire license notice for the
20 * Javascript code in this page
21 */
22"use strict";
23
24Object.defineProperty(exports, "__esModule", {
25 value: true
26});
27exports.ChunkedStreamManager = exports.ChunkedStream = void 0;
28
29var _util = require("../shared/util");
30
31function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
32
33function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
34
35function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
36
37var ChunkedStream =
38/*#__PURE__*/
39function () {
40 function ChunkedStream(length, chunkSize, manager) {
41 _classCallCheck(this, ChunkedStream);
42
43 this.bytes = new Uint8Array(length);
44 this.start = 0;
45 this.pos = 0;
46 this.end = length;
47 this.chunkSize = chunkSize;
48 this.loadedChunks = [];
49 this.numChunksLoaded = 0;
50 this.numChunks = Math.ceil(length / chunkSize);
51 this.manager = manager;
52 this.progressiveDataLength = 0;
53 this.lastSuccessfulEnsureByteChunk = -1;
54 }
55
56 _createClass(ChunkedStream, [{
57 key: "getMissingChunks",
58 value: function getMissingChunks() {
59 var chunks = [];
60
61 for (var chunk = 0, n = this.numChunks; chunk < n; ++chunk) {
62 if (!this.loadedChunks[chunk]) {
63 chunks.push(chunk);
64 }
65 }
66
67 return chunks;
68 }
69 }, {
70 key: "getBaseStreams",
71 value: function getBaseStreams() {
72 return [this];
73 }
74 }, {
75 key: "allChunksLoaded",
76 value: function allChunksLoaded() {
77 return this.numChunksLoaded === this.numChunks;
78 }
79 }, {
80 key: "onReceiveData",
81 value: function onReceiveData(begin, chunk) {
82 var chunkSize = this.chunkSize;
83
84 if (begin % chunkSize !== 0) {
85 throw new Error("Bad begin offset: ".concat(begin));
86 }
87
88 var end = begin + chunk.byteLength;
89
90 if (end % chunkSize !== 0 && end !== this.bytes.length) {
91 throw new Error("Bad end offset: ".concat(end));
92 }
93
94 this.bytes.set(new Uint8Array(chunk), begin);
95 var beginChunk = Math.floor(begin / chunkSize);
96 var endChunk = Math.floor((end - 1) / chunkSize) + 1;
97
98 for (var curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
99 if (!this.loadedChunks[curChunk]) {
100 this.loadedChunks[curChunk] = true;
101 ++this.numChunksLoaded;
102 }
103 }
104 }
105 }, {
106 key: "onReceiveProgressiveData",
107 value: function onReceiveProgressiveData(data) {
108 var position = this.progressiveDataLength;
109 var beginChunk = Math.floor(position / this.chunkSize);
110 this.bytes.set(new Uint8Array(data), position);
111 position += data.byteLength;
112 this.progressiveDataLength = position;
113 var endChunk = position >= this.end ? this.numChunks : Math.floor(position / this.chunkSize);
114
115 for (var curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
116 if (!this.loadedChunks[curChunk]) {
117 this.loadedChunks[curChunk] = true;
118 ++this.numChunksLoaded;
119 }
120 }
121 }
122 }, {
123 key: "ensureByte",
124 value: function ensureByte(pos) {
125 var chunk = Math.floor(pos / this.chunkSize);
126
127 if (chunk === this.lastSuccessfulEnsureByteChunk) {
128 return;
129 }
130
131 if (!this.loadedChunks[chunk]) {
132 throw new _util.MissingDataException(pos, pos + 1);
133 }
134
135 this.lastSuccessfulEnsureByteChunk = chunk;
136 }
137 }, {
138 key: "ensureRange",
139 value: function ensureRange(begin, end) {
140 if (begin >= end) {
141 return;
142 }
143
144 if (end <= this.progressiveDataLength) {
145 return;
146 }
147
148 var chunkSize = this.chunkSize;
149 var beginChunk = Math.floor(begin / chunkSize);
150 var endChunk = Math.floor((end - 1) / chunkSize) + 1;
151
152 for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
153 if (!this.loadedChunks[chunk]) {
154 throw new _util.MissingDataException(begin, end);
155 }
156 }
157 }
158 }, {
159 key: "nextEmptyChunk",
160 value: function nextEmptyChunk(beginChunk) {
161 var numChunks = this.numChunks;
162
163 for (var i = 0; i < numChunks; ++i) {
164 var chunk = (beginChunk + i) % numChunks;
165
166 if (!this.loadedChunks[chunk]) {
167 return chunk;
168 }
169 }
170
171 return null;
172 }
173 }, {
174 key: "hasChunk",
175 value: function hasChunk(chunk) {
176 return !!this.loadedChunks[chunk];
177 }
178 }, {
179 key: "getByte",
180 value: function getByte() {
181 var pos = this.pos;
182
183 if (pos >= this.end) {
184 return -1;
185 }
186
187 this.ensureByte(pos);
188 return this.bytes[this.pos++];
189 }
190 }, {
191 key: "getUint16",
192 value: function getUint16() {
193 var b0 = this.getByte();
194 var b1 = this.getByte();
195
196 if (b0 === -1 || b1 === -1) {
197 return -1;
198 }
199
200 return (b0 << 8) + b1;
201 }
202 }, {
203 key: "getInt32",
204 value: function getInt32() {
205 var b0 = this.getByte();
206 var b1 = this.getByte();
207 var b2 = this.getByte();
208 var b3 = this.getByte();
209 return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
210 }
211 }, {
212 key: "getBytes",
213 value: function getBytes(length) {
214 var forceClamped = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
215 var bytes = this.bytes;
216 var pos = this.pos;
217 var strEnd = this.end;
218
219 if (!length) {
220 this.ensureRange(pos, strEnd);
221
222 var _subarray = bytes.subarray(pos, strEnd);
223
224 return forceClamped ? new Uint8ClampedArray(_subarray) : _subarray;
225 }
226
227 var end = pos + length;
228
229 if (end > strEnd) {
230 end = strEnd;
231 }
232
233 this.ensureRange(pos, end);
234 this.pos = end;
235 var subarray = bytes.subarray(pos, end);
236 return forceClamped ? new Uint8ClampedArray(subarray) : subarray;
237 }
238 }, {
239 key: "peekByte",
240 value: function peekByte() {
241 var peekedByte = this.getByte();
242 this.pos--;
243 return peekedByte;
244 }
245 }, {
246 key: "peekBytes",
247 value: function peekBytes(length) {
248 var forceClamped = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
249 var bytes = this.getBytes(length, forceClamped);
250 this.pos -= bytes.length;
251 return bytes;
252 }
253 }, {
254 key: "getByteRange",
255 value: function getByteRange(begin, end) {
256 this.ensureRange(begin, end);
257 return this.bytes.subarray(begin, end);
258 }
259 }, {
260 key: "skip",
261 value: function skip(n) {
262 if (!n) {
263 n = 1;
264 }
265
266 this.pos += n;
267 }
268 }, {
269 key: "reset",
270 value: function reset() {
271 this.pos = this.start;
272 }
273 }, {
274 key: "moveStart",
275 value: function moveStart() {
276 this.start = this.pos;
277 }
278 }, {
279 key: "makeSubStream",
280 value: function makeSubStream(start, length, dict) {
281 this.ensureRange(start, start + length);
282
283 function ChunkedStreamSubstream() {}
284
285 ChunkedStreamSubstream.prototype = Object.create(this);
286
287 ChunkedStreamSubstream.prototype.getMissingChunks = function () {
288 var chunkSize = this.chunkSize;
289 var beginChunk = Math.floor(this.start / chunkSize);
290 var endChunk = Math.floor((this.end - 1) / chunkSize) + 1;
291 var missingChunks = [];
292
293 for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
294 if (!this.loadedChunks[chunk]) {
295 missingChunks.push(chunk);
296 }
297 }
298
299 return missingChunks;
300 };
301
302 var subStream = new ChunkedStreamSubstream();
303 subStream.pos = subStream.start = start;
304 subStream.end = start + length || this.end;
305 subStream.dict = dict;
306 return subStream;
307 }
308 }, {
309 key: "length",
310 get: function get() {
311 return this.end - this.start;
312 }
313 }, {
314 key: "isEmpty",
315 get: function get() {
316 return this.length === 0;
317 }
318 }]);
319
320 return ChunkedStream;
321}();
322
323exports.ChunkedStream = ChunkedStream;
324
325var ChunkedStreamManager =
326/*#__PURE__*/
327function () {
328 function ChunkedStreamManager(pdfNetworkStream, args) {
329 _classCallCheck(this, ChunkedStreamManager);
330
331 this.length = args.length;
332 this.chunkSize = args.rangeChunkSize;
333 this.stream = new ChunkedStream(this.length, this.chunkSize, this);
334 this.pdfNetworkStream = pdfNetworkStream;
335 this.disableAutoFetch = args.disableAutoFetch;
336 this.msgHandler = args.msgHandler;
337 this.currRequestId = 0;
338 this.chunksNeededByRequest = Object.create(null);
339 this.requestsByChunk = Object.create(null);
340 this.promisesByRequest = Object.create(null);
341 this.progressiveDataLength = 0;
342 this.aborted = false;
343 this._loadedStreamCapability = (0, _util.createPromiseCapability)();
344 }
345
346 _createClass(ChunkedStreamManager, [{
347 key: "onLoadedStream",
348 value: function onLoadedStream() {
349 return this._loadedStreamCapability.promise;
350 }
351 }, {
352 key: "sendRequest",
353 value: function sendRequest(begin, end) {
354 var _this = this;
355
356 var rangeReader = this.pdfNetworkStream.getRangeReader(begin, end);
357
358 if (!rangeReader.isStreamingSupported) {
359 rangeReader.onProgress = this.onProgress.bind(this);
360 }
361
362 var chunks = [],
363 loaded = 0;
364 var promise = new Promise(function (resolve, reject) {
365 var readChunk = function readChunk(chunk) {
366 try {
367 if (!chunk.done) {
368 var data = chunk.value;
369 chunks.push(data);
370 loaded += (0, _util.arrayByteLength)(data);
371
372 if (rangeReader.isStreamingSupported) {
373 _this.onProgress({
374 loaded: loaded
375 });
376 }
377
378 rangeReader.read().then(readChunk, reject);
379 return;
380 }
381
382 var chunkData = (0, _util.arraysToBytes)(chunks);
383 chunks = null;
384 resolve(chunkData);
385 } catch (e) {
386 reject(e);
387 }
388 };
389
390 rangeReader.read().then(readChunk, reject);
391 });
392 promise.then(function (data) {
393 if (_this.aborted) {
394 return;
395 }
396
397 _this.onReceiveData({
398 chunk: data,
399 begin: begin
400 });
401 });
402 }
403 }, {
404 key: "requestAllChunks",
405 value: function requestAllChunks() {
406 var missingChunks = this.stream.getMissingChunks();
407
408 this._requestChunks(missingChunks);
409
410 return this._loadedStreamCapability.promise;
411 }
412 }, {
413 key: "_requestChunks",
414 value: function _requestChunks(chunks) {
415 var requestId = this.currRequestId++;
416 var chunksNeeded = Object.create(null);
417 this.chunksNeededByRequest[requestId] = chunksNeeded;
418 var _iteratorNormalCompletion = true;
419 var _didIteratorError = false;
420 var _iteratorError = undefined;
421
422 try {
423 for (var _iterator = chunks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
424 var _chunk = _step.value;
425
426 if (!this.stream.hasChunk(_chunk)) {
427 chunksNeeded[_chunk] = true;
428 }
429 }
430 } catch (err) {
431 _didIteratorError = true;
432 _iteratorError = err;
433 } finally {
434 try {
435 if (!_iteratorNormalCompletion && _iterator.return != null) {
436 _iterator.return();
437 }
438 } finally {
439 if (_didIteratorError) {
440 throw _iteratorError;
441 }
442 }
443 }
444
445 if ((0, _util.isEmptyObj)(chunksNeeded)) {
446 return Promise.resolve();
447 }
448
449 var capability = (0, _util.createPromiseCapability)();
450 this.promisesByRequest[requestId] = capability;
451 var chunksToRequest = [];
452
453 for (var chunk in chunksNeeded) {
454 chunk = chunk | 0;
455
456 if (!(chunk in this.requestsByChunk)) {
457 this.requestsByChunk[chunk] = [];
458 chunksToRequest.push(chunk);
459 }
460
461 this.requestsByChunk[chunk].push(requestId);
462 }
463
464 if (!chunksToRequest.length) {
465 return capability.promise;
466 }
467
468 var groupedChunksToRequest = this.groupChunks(chunksToRequest);
469 var _iteratorNormalCompletion2 = true;
470 var _didIteratorError2 = false;
471 var _iteratorError2 = undefined;
472
473 try {
474 for (var _iterator2 = groupedChunksToRequest[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
475 var groupedChunk = _step2.value;
476 var begin = groupedChunk.beginChunk * this.chunkSize;
477 var end = Math.min(groupedChunk.endChunk * this.chunkSize, this.length);
478 this.sendRequest(begin, end);
479 }
480 } catch (err) {
481 _didIteratorError2 = true;
482 _iteratorError2 = err;
483 } finally {
484 try {
485 if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
486 _iterator2.return();
487 }
488 } finally {
489 if (_didIteratorError2) {
490 throw _iteratorError2;
491 }
492 }
493 }
494
495 return capability.promise;
496 }
497 }, {
498 key: "getStream",
499 value: function getStream() {
500 return this.stream;
501 }
502 }, {
503 key: "requestRange",
504 value: function requestRange(begin, end) {
505 end = Math.min(end, this.length);
506 var beginChunk = this.getBeginChunk(begin);
507 var endChunk = this.getEndChunk(end);
508 var chunks = [];
509
510 for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
511 chunks.push(chunk);
512 }
513
514 return this._requestChunks(chunks);
515 }
516 }, {
517 key: "requestRanges",
518 value: function requestRanges() {
519 var ranges = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
520 var chunksToRequest = [];
521 var _iteratorNormalCompletion3 = true;
522 var _didIteratorError3 = false;
523 var _iteratorError3 = undefined;
524
525 try {
526 for (var _iterator3 = ranges[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
527 var range = _step3.value;
528 var beginChunk = this.getBeginChunk(range.begin);
529 var endChunk = this.getEndChunk(range.end);
530
531 for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
532 if (!chunksToRequest.includes(chunk)) {
533 chunksToRequest.push(chunk);
534 }
535 }
536 }
537 } catch (err) {
538 _didIteratorError3 = true;
539 _iteratorError3 = err;
540 } finally {
541 try {
542 if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
543 _iterator3.return();
544 }
545 } finally {
546 if (_didIteratorError3) {
547 throw _iteratorError3;
548 }
549 }
550 }
551
552 chunksToRequest.sort(function (a, b) {
553 return a - b;
554 });
555 return this._requestChunks(chunksToRequest);
556 }
557 }, {
558 key: "groupChunks",
559 value: function groupChunks(chunks) {
560 var groupedChunks = [];
561 var beginChunk = -1;
562 var prevChunk = -1;
563
564 for (var i = 0, ii = chunks.length; i < ii; ++i) {
565 var chunk = chunks[i];
566
567 if (beginChunk < 0) {
568 beginChunk = chunk;
569 }
570
571 if (prevChunk >= 0 && prevChunk + 1 !== chunk) {
572 groupedChunks.push({
573 beginChunk: beginChunk,
574 endChunk: prevChunk + 1
575 });
576 beginChunk = chunk;
577 }
578
579 if (i + 1 === chunks.length) {
580 groupedChunks.push({
581 beginChunk: beginChunk,
582 endChunk: chunk + 1
583 });
584 }
585
586 prevChunk = chunk;
587 }
588
589 return groupedChunks;
590 }
591 }, {
592 key: "onProgress",
593 value: function onProgress(args) {
594 this.msgHandler.send('DocProgress', {
595 loaded: this.stream.numChunksLoaded * this.chunkSize + args.loaded,
596 total: this.length
597 });
598 }
599 }, {
600 key: "onReceiveData",
601 value: function onReceiveData(args) {
602 var chunk = args.chunk;
603 var isProgressive = args.begin === undefined;
604 var begin = isProgressive ? this.progressiveDataLength : args.begin;
605 var end = begin + chunk.byteLength;
606 var beginChunk = Math.floor(begin / this.chunkSize);
607 var endChunk = end < this.length ? Math.floor(end / this.chunkSize) : Math.ceil(end / this.chunkSize);
608
609 if (isProgressive) {
610 this.stream.onReceiveProgressiveData(chunk);
611 this.progressiveDataLength = end;
612 } else {
613 this.stream.onReceiveData(begin, chunk);
614 }
615
616 if (this.stream.allChunksLoaded()) {
617 this._loadedStreamCapability.resolve(this.stream);
618 }
619
620 var loadedRequests = [];
621
622 for (var _chunk2 = beginChunk; _chunk2 < endChunk; ++_chunk2) {
623 var requestIds = this.requestsByChunk[_chunk2] || [];
624 delete this.requestsByChunk[_chunk2];
625 var _iteratorNormalCompletion4 = true;
626 var _didIteratorError4 = false;
627 var _iteratorError4 = undefined;
628
629 try {
630 for (var _iterator4 = requestIds[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
631 var requestId = _step4.value;
632 var chunksNeeded = this.chunksNeededByRequest[requestId];
633
634 if (_chunk2 in chunksNeeded) {
635 delete chunksNeeded[_chunk2];
636 }
637
638 if (!(0, _util.isEmptyObj)(chunksNeeded)) {
639 continue;
640 }
641
642 loadedRequests.push(requestId);
643 }
644 } catch (err) {
645 _didIteratorError4 = true;
646 _iteratorError4 = err;
647 } finally {
648 try {
649 if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
650 _iterator4.return();
651 }
652 } finally {
653 if (_didIteratorError4) {
654 throw _iteratorError4;
655 }
656 }
657 }
658 }
659
660 if (!this.disableAutoFetch && (0, _util.isEmptyObj)(this.requestsByChunk)) {
661 var nextEmptyChunk;
662
663 if (this.stream.numChunksLoaded === 1) {
664 var lastChunk = this.stream.numChunks - 1;
665
666 if (!this.stream.hasChunk(lastChunk)) {
667 nextEmptyChunk = lastChunk;
668 }
669 } else {
670 nextEmptyChunk = this.stream.nextEmptyChunk(endChunk);
671 }
672
673 if (Number.isInteger(nextEmptyChunk)) {
674 this._requestChunks([nextEmptyChunk]);
675 }
676 }
677
678 for (var _i = 0; _i < loadedRequests.length; _i++) {
679 var _requestId = loadedRequests[_i];
680 var capability = this.promisesByRequest[_requestId];
681 delete this.promisesByRequest[_requestId];
682 capability.resolve();
683 }
684
685 this.msgHandler.send('DocProgress', {
686 loaded: this.stream.numChunksLoaded * this.chunkSize,
687 total: this.length
688 });
689 }
690 }, {
691 key: "onError",
692 value: function onError(err) {
693 this._loadedStreamCapability.reject(err);
694 }
695 }, {
696 key: "getBeginChunk",
697 value: function getBeginChunk(begin) {
698 return Math.floor(begin / this.chunkSize);
699 }
700 }, {
701 key: "getEndChunk",
702 value: function getEndChunk(end) {
703 return Math.floor((end - 1) / this.chunkSize) + 1;
704 }
705 }, {
706 key: "abort",
707 value: function abort() {
708 this.aborted = true;
709
710 if (this.pdfNetworkStream) {
711 this.pdfNetworkStream.cancelAllRequests('abort');
712 }
713
714 for (var requestId in this.promisesByRequest) {
715 this.promisesByRequest[requestId].reject(new Error('Request was aborted'));
716 }
717 }
718 }]);
719
720 return ChunkedStreamManager;
721}();
722
723exports.ChunkedStreamManager = ChunkedStreamManager;
\No newline at end of file