UNPKG

18.5 kBJavaScriptView Raw
1/**
2 * @licstart The following is the entire license notice for the
3 * JavaScript code in this page
4 *
5 * Copyright 2022 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.OperatorList = void 0;
28
29var _util = require("../shared/util.js");
30
31function addState(parentState, pattern, checkFn, iterateFn, processFn) {
32 let state = parentState;
33
34 for (let i = 0, ii = pattern.length - 1; i < ii; i++) {
35 const item = pattern[i];
36 state = state[item] || (state[item] = []);
37 }
38
39 state[pattern.at(-1)] = {
40 checkFn,
41 iterateFn,
42 processFn
43 };
44}
45
46const InitialState = [];
47addState(InitialState, [_util.OPS.save, _util.OPS.transform, _util.OPS.paintInlineImageXObject, _util.OPS.restore], null, function iterateInlineImageGroup(context, i) {
48 const fnArray = context.fnArray;
49 const iFirstSave = context.iCurr - 3;
50 const pos = (i - iFirstSave) % 4;
51
52 switch (pos) {
53 case 0:
54 return fnArray[i] === _util.OPS.save;
55
56 case 1:
57 return fnArray[i] === _util.OPS.transform;
58
59 case 2:
60 return fnArray[i] === _util.OPS.paintInlineImageXObject;
61
62 case 3:
63 return fnArray[i] === _util.OPS.restore;
64 }
65
66 throw new Error(`iterateInlineImageGroup - invalid pos: ${pos}`);
67}, function foundInlineImageGroup(context, i) {
68 const MIN_IMAGES_IN_INLINE_IMAGES_BLOCK = 10;
69 const MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200;
70 const MAX_WIDTH = 1000;
71 const IMAGE_PADDING = 1;
72 const fnArray = context.fnArray,
73 argsArray = context.argsArray;
74 const curr = context.iCurr;
75 const iFirstSave = curr - 3;
76 const iFirstTransform = curr - 2;
77 const iFirstPIIXO = curr - 1;
78 const count = Math.min(Math.floor((i - iFirstSave) / 4), MAX_IMAGES_IN_INLINE_IMAGES_BLOCK);
79
80 if (count < MIN_IMAGES_IN_INLINE_IMAGES_BLOCK) {
81 return i - (i - iFirstSave) % 4;
82 }
83
84 let maxX = 0;
85 const map = [];
86 let maxLineHeight = 0;
87 let currentX = IMAGE_PADDING,
88 currentY = IMAGE_PADDING;
89
90 for (let q = 0; q < count; q++) {
91 const transform = argsArray[iFirstTransform + (q << 2)];
92 const img = argsArray[iFirstPIIXO + (q << 2)][0];
93
94 if (currentX + img.width > MAX_WIDTH) {
95 maxX = Math.max(maxX, currentX);
96 currentY += maxLineHeight + 2 * IMAGE_PADDING;
97 currentX = 0;
98 maxLineHeight = 0;
99 }
100
101 map.push({
102 transform,
103 x: currentX,
104 y: currentY,
105 w: img.width,
106 h: img.height
107 });
108 currentX += img.width + 2 * IMAGE_PADDING;
109 maxLineHeight = Math.max(maxLineHeight, img.height);
110 }
111
112 const imgWidth = Math.max(maxX, currentX) + IMAGE_PADDING;
113 const imgHeight = currentY + maxLineHeight + IMAGE_PADDING;
114 const imgData = new Uint8Array(imgWidth * imgHeight * 4);
115 const imgRowSize = imgWidth << 2;
116
117 for (let q = 0; q < count; q++) {
118 const data = argsArray[iFirstPIIXO + (q << 2)][0].data;
119 const rowSize = map[q].w << 2;
120 let dataOffset = 0;
121 let offset = map[q].x + map[q].y * imgWidth << 2;
122 imgData.set(data.subarray(0, rowSize), offset - imgRowSize);
123
124 for (let k = 0, kk = map[q].h; k < kk; k++) {
125 imgData.set(data.subarray(dataOffset, dataOffset + rowSize), offset);
126 dataOffset += rowSize;
127 offset += imgRowSize;
128 }
129
130 imgData.set(data.subarray(dataOffset - rowSize, dataOffset), offset);
131
132 while (offset >= 0) {
133 data[offset - 4] = data[offset];
134 data[offset - 3] = data[offset + 1];
135 data[offset - 2] = data[offset + 2];
136 data[offset - 1] = data[offset + 3];
137 data[offset + rowSize] = data[offset + rowSize - 4];
138 data[offset + rowSize + 1] = data[offset + rowSize - 3];
139 data[offset + rowSize + 2] = data[offset + rowSize - 2];
140 data[offset + rowSize + 3] = data[offset + rowSize - 1];
141 offset -= imgRowSize;
142 }
143 }
144
145 fnArray.splice(iFirstSave, count * 4, _util.OPS.paintInlineImageXObjectGroup);
146 argsArray.splice(iFirstSave, count * 4, [{
147 width: imgWidth,
148 height: imgHeight,
149 kind: _util.ImageKind.RGBA_32BPP,
150 data: imgData
151 }, map]);
152 return iFirstSave + 1;
153});
154addState(InitialState, [_util.OPS.save, _util.OPS.transform, _util.OPS.paintImageMaskXObject, _util.OPS.restore], null, function iterateImageMaskGroup(context, i) {
155 const fnArray = context.fnArray;
156 const iFirstSave = context.iCurr - 3;
157 const pos = (i - iFirstSave) % 4;
158
159 switch (pos) {
160 case 0:
161 return fnArray[i] === _util.OPS.save;
162
163 case 1:
164 return fnArray[i] === _util.OPS.transform;
165
166 case 2:
167 return fnArray[i] === _util.OPS.paintImageMaskXObject;
168
169 case 3:
170 return fnArray[i] === _util.OPS.restore;
171 }
172
173 throw new Error(`iterateImageMaskGroup - invalid pos: ${pos}`);
174}, function foundImageMaskGroup(context, i) {
175 const MIN_IMAGES_IN_MASKS_BLOCK = 10;
176 const MAX_IMAGES_IN_MASKS_BLOCK = 100;
177 const MAX_SAME_IMAGES_IN_MASKS_BLOCK = 1000;
178 const fnArray = context.fnArray,
179 argsArray = context.argsArray;
180 const curr = context.iCurr;
181 const iFirstSave = curr - 3;
182 const iFirstTransform = curr - 2;
183 const iFirstPIMXO = curr - 1;
184 let count = Math.floor((i - iFirstSave) / 4);
185
186 if (count < MIN_IMAGES_IN_MASKS_BLOCK) {
187 return i - (i - iFirstSave) % 4;
188 }
189
190 let isSameImage = false;
191 let iTransform, transformArgs;
192 const firstPIMXOArg0 = argsArray[iFirstPIMXO][0];
193 const firstTransformArg0 = argsArray[iFirstTransform][0],
194 firstTransformArg1 = argsArray[iFirstTransform][1],
195 firstTransformArg2 = argsArray[iFirstTransform][2],
196 firstTransformArg3 = argsArray[iFirstTransform][3];
197
198 if (firstTransformArg1 === firstTransformArg2) {
199 isSameImage = true;
200 iTransform = iFirstTransform + 4;
201 let iPIMXO = iFirstPIMXO + 4;
202
203 for (let q = 1; q < count; q++, iTransform += 4, iPIMXO += 4) {
204 transformArgs = argsArray[iTransform];
205
206 if (argsArray[iPIMXO][0] !== firstPIMXOArg0 || transformArgs[0] !== firstTransformArg0 || transformArgs[1] !== firstTransformArg1 || transformArgs[2] !== firstTransformArg2 || transformArgs[3] !== firstTransformArg3) {
207 if (q < MIN_IMAGES_IN_MASKS_BLOCK) {
208 isSameImage = false;
209 } else {
210 count = q;
211 }
212
213 break;
214 }
215 }
216 }
217
218 if (isSameImage) {
219 count = Math.min(count, MAX_SAME_IMAGES_IN_MASKS_BLOCK);
220 const positions = new Float32Array(count * 2);
221 iTransform = iFirstTransform;
222
223 for (let q = 0; q < count; q++, iTransform += 4) {
224 transformArgs = argsArray[iTransform];
225 positions[q << 1] = transformArgs[4];
226 positions[(q << 1) + 1] = transformArgs[5];
227 }
228
229 fnArray.splice(iFirstSave, count * 4, _util.OPS.paintImageMaskXObjectRepeat);
230 argsArray.splice(iFirstSave, count * 4, [firstPIMXOArg0, firstTransformArg0, firstTransformArg1, firstTransformArg2, firstTransformArg3, positions]);
231 } else {
232 count = Math.min(count, MAX_IMAGES_IN_MASKS_BLOCK);
233 const images = [];
234
235 for (let q = 0; q < count; q++) {
236 transformArgs = argsArray[iFirstTransform + (q << 2)];
237 const maskParams = argsArray[iFirstPIMXO + (q << 2)][0];
238 images.push({
239 data: maskParams.data,
240 width: maskParams.width,
241 height: maskParams.height,
242 interpolate: maskParams.interpolate,
243 count: maskParams.count,
244 transform: transformArgs
245 });
246 }
247
248 fnArray.splice(iFirstSave, count * 4, _util.OPS.paintImageMaskXObjectGroup);
249 argsArray.splice(iFirstSave, count * 4, [images]);
250 }
251
252 return iFirstSave + 1;
253});
254addState(InitialState, [_util.OPS.save, _util.OPS.transform, _util.OPS.paintImageXObject, _util.OPS.restore], function (context) {
255 const argsArray = context.argsArray;
256 const iFirstTransform = context.iCurr - 2;
257 return argsArray[iFirstTransform][1] === 0 && argsArray[iFirstTransform][2] === 0;
258}, function iterateImageGroup(context, i) {
259 const fnArray = context.fnArray,
260 argsArray = context.argsArray;
261 const iFirstSave = context.iCurr - 3;
262 const pos = (i - iFirstSave) % 4;
263
264 switch (pos) {
265 case 0:
266 return fnArray[i] === _util.OPS.save;
267
268 case 1:
269 if (fnArray[i] !== _util.OPS.transform) {
270 return false;
271 }
272
273 const iFirstTransform = context.iCurr - 2;
274 const firstTransformArg0 = argsArray[iFirstTransform][0];
275 const firstTransformArg3 = argsArray[iFirstTransform][3];
276
277 if (argsArray[i][0] !== firstTransformArg0 || argsArray[i][1] !== 0 || argsArray[i][2] !== 0 || argsArray[i][3] !== firstTransformArg3) {
278 return false;
279 }
280
281 return true;
282
283 case 2:
284 if (fnArray[i] !== _util.OPS.paintImageXObject) {
285 return false;
286 }
287
288 const iFirstPIXO = context.iCurr - 1;
289 const firstPIXOArg0 = argsArray[iFirstPIXO][0];
290
291 if (argsArray[i][0] !== firstPIXOArg0) {
292 return false;
293 }
294
295 return true;
296
297 case 3:
298 return fnArray[i] === _util.OPS.restore;
299 }
300
301 throw new Error(`iterateImageGroup - invalid pos: ${pos}`);
302}, function (context, i) {
303 const MIN_IMAGES_IN_BLOCK = 3;
304 const MAX_IMAGES_IN_BLOCK = 1000;
305 const fnArray = context.fnArray,
306 argsArray = context.argsArray;
307 const curr = context.iCurr;
308 const iFirstSave = curr - 3;
309 const iFirstTransform = curr - 2;
310 const iFirstPIXO = curr - 1;
311 const firstPIXOArg0 = argsArray[iFirstPIXO][0];
312 const firstTransformArg0 = argsArray[iFirstTransform][0];
313 const firstTransformArg3 = argsArray[iFirstTransform][3];
314 const count = Math.min(Math.floor((i - iFirstSave) / 4), MAX_IMAGES_IN_BLOCK);
315
316 if (count < MIN_IMAGES_IN_BLOCK) {
317 return i - (i - iFirstSave) % 4;
318 }
319
320 const positions = new Float32Array(count * 2);
321 let iTransform = iFirstTransform;
322
323 for (let q = 0; q < count; q++, iTransform += 4) {
324 const transformArgs = argsArray[iTransform];
325 positions[q << 1] = transformArgs[4];
326 positions[(q << 1) + 1] = transformArgs[5];
327 }
328
329 const args = [firstPIXOArg0, firstTransformArg0, firstTransformArg3, positions];
330 fnArray.splice(iFirstSave, count * 4, _util.OPS.paintImageXObjectRepeat);
331 argsArray.splice(iFirstSave, count * 4, args);
332 return iFirstSave + 1;
333});
334addState(InitialState, [_util.OPS.beginText, _util.OPS.setFont, _util.OPS.setTextMatrix, _util.OPS.showText, _util.OPS.endText], null, function iterateShowTextGroup(context, i) {
335 const fnArray = context.fnArray,
336 argsArray = context.argsArray;
337 const iFirstSave = context.iCurr - 4;
338 const pos = (i - iFirstSave) % 5;
339
340 switch (pos) {
341 case 0:
342 return fnArray[i] === _util.OPS.beginText;
343
344 case 1:
345 return fnArray[i] === _util.OPS.setFont;
346
347 case 2:
348 return fnArray[i] === _util.OPS.setTextMatrix;
349
350 case 3:
351 if (fnArray[i] !== _util.OPS.showText) {
352 return false;
353 }
354
355 const iFirstSetFont = context.iCurr - 3;
356 const firstSetFontArg0 = argsArray[iFirstSetFont][0];
357 const firstSetFontArg1 = argsArray[iFirstSetFont][1];
358
359 if (argsArray[i][0] !== firstSetFontArg0 || argsArray[i][1] !== firstSetFontArg1) {
360 return false;
361 }
362
363 return true;
364
365 case 4:
366 return fnArray[i] === _util.OPS.endText;
367 }
368
369 throw new Error(`iterateShowTextGroup - invalid pos: ${pos}`);
370}, function (context, i) {
371 const MIN_CHARS_IN_BLOCK = 3;
372 const MAX_CHARS_IN_BLOCK = 1000;
373 const fnArray = context.fnArray,
374 argsArray = context.argsArray;
375 const curr = context.iCurr;
376 const iFirstBeginText = curr - 4;
377 const iFirstSetFont = curr - 3;
378 const iFirstSetTextMatrix = curr - 2;
379 const iFirstShowText = curr - 1;
380 const iFirstEndText = curr;
381 const firstSetFontArg0 = argsArray[iFirstSetFont][0];
382 const firstSetFontArg1 = argsArray[iFirstSetFont][1];
383 let count = Math.min(Math.floor((i - iFirstBeginText) / 5), MAX_CHARS_IN_BLOCK);
384
385 if (count < MIN_CHARS_IN_BLOCK) {
386 return i - (i - iFirstBeginText) % 5;
387 }
388
389 let iFirst = iFirstBeginText;
390
391 if (iFirstBeginText >= 4 && fnArray[iFirstBeginText - 4] === fnArray[iFirstSetFont] && fnArray[iFirstBeginText - 3] === fnArray[iFirstSetTextMatrix] && fnArray[iFirstBeginText - 2] === fnArray[iFirstShowText] && fnArray[iFirstBeginText - 1] === fnArray[iFirstEndText] && argsArray[iFirstBeginText - 4][0] === firstSetFontArg0 && argsArray[iFirstBeginText - 4][1] === firstSetFontArg1) {
392 count++;
393 iFirst -= 5;
394 }
395
396 let iEndText = iFirst + 4;
397
398 for (let q = 1; q < count; q++) {
399 fnArray.splice(iEndText, 3);
400 argsArray.splice(iEndText, 3);
401 iEndText += 2;
402 }
403
404 return iEndText + 1;
405});
406
407class NullOptimizer {
408 constructor(queue) {
409 this.queue = queue;
410 }
411
412 _optimize() {}
413
414 push(fn, args) {
415 this.queue.fnArray.push(fn);
416 this.queue.argsArray.push(args);
417
418 this._optimize();
419 }
420
421 flush() {}
422
423 reset() {}
424
425}
426
427class QueueOptimizer extends NullOptimizer {
428 constructor(queue) {
429 super(queue);
430 this.state = null;
431 this.context = {
432 iCurr: 0,
433 fnArray: queue.fnArray,
434 argsArray: queue.argsArray
435 };
436 this.match = null;
437 this.lastProcessed = 0;
438 }
439
440 _optimize() {
441 const fnArray = this.queue.fnArray;
442 let i = this.lastProcessed,
443 ii = fnArray.length;
444 let state = this.state;
445 let match = this.match;
446
447 if (!state && !match && i + 1 === ii && !InitialState[fnArray[i]]) {
448 this.lastProcessed = ii;
449 return;
450 }
451
452 const context = this.context;
453
454 while (i < ii) {
455 if (match) {
456 const iterate = (0, match.iterateFn)(context, i);
457
458 if (iterate) {
459 i++;
460 continue;
461 }
462
463 i = (0, match.processFn)(context, i + 1);
464 ii = fnArray.length;
465 match = null;
466 state = null;
467
468 if (i >= ii) {
469 break;
470 }
471 }
472
473 state = (state || InitialState)[fnArray[i]];
474
475 if (!state || Array.isArray(state)) {
476 i++;
477 continue;
478 }
479
480 context.iCurr = i;
481 i++;
482
483 if (state.checkFn && !(0, state.checkFn)(context)) {
484 state = null;
485 continue;
486 }
487
488 match = state;
489 state = null;
490 }
491
492 this.state = state;
493 this.match = match;
494 this.lastProcessed = i;
495 }
496
497 flush() {
498 while (this.match) {
499 const length = this.queue.fnArray.length;
500 this.lastProcessed = (0, this.match.processFn)(this.context, length);
501 this.match = null;
502 this.state = null;
503
504 this._optimize();
505 }
506 }
507
508 reset() {
509 this.state = null;
510 this.match = null;
511 this.lastProcessed = 0;
512 }
513
514}
515
516class OperatorList {
517 static get CHUNK_SIZE() {
518 return (0, _util.shadow)(this, "CHUNK_SIZE", 1000);
519 }
520
521 static get CHUNK_SIZE_ABOUT() {
522 return (0, _util.shadow)(this, "CHUNK_SIZE_ABOUT", this.CHUNK_SIZE - 5);
523 }
524
525 constructor(intent = 0, streamSink) {
526 this._streamSink = streamSink;
527 this.fnArray = [];
528 this.argsArray = [];
529
530 if (streamSink && !(intent & _util.RenderingIntentFlag.OPLIST)) {
531 this.optimizer = new QueueOptimizer(this);
532 } else {
533 this.optimizer = new NullOptimizer(this);
534 }
535
536 this.dependencies = new Set();
537 this._totalLength = 0;
538 this.weight = 0;
539 this._resolved = streamSink ? null : Promise.resolve();
540 }
541
542 get length() {
543 return this.argsArray.length;
544 }
545
546 get ready() {
547 return this._resolved || this._streamSink.ready;
548 }
549
550 get totalLength() {
551 return this._totalLength + this.length;
552 }
553
554 addOp(fn, args) {
555 this.optimizer.push(fn, args);
556 this.weight++;
557
558 if (this._streamSink) {
559 if (this.weight >= OperatorList.CHUNK_SIZE) {
560 this.flush();
561 } else if (this.weight >= OperatorList.CHUNK_SIZE_ABOUT && (fn === _util.OPS.restore || fn === _util.OPS.endText)) {
562 this.flush();
563 }
564 }
565 }
566
567 addImageOps(fn, args, optionalContent) {
568 if (optionalContent !== undefined) {
569 this.addOp(_util.OPS.beginMarkedContentProps, ["OC", optionalContent]);
570 }
571
572 this.addOp(fn, args);
573
574 if (optionalContent !== undefined) {
575 this.addOp(_util.OPS.endMarkedContent, []);
576 }
577 }
578
579 addDependency(dependency) {
580 if (this.dependencies.has(dependency)) {
581 return;
582 }
583
584 this.dependencies.add(dependency);
585 this.addOp(_util.OPS.dependency, [dependency]);
586 }
587
588 addDependencies(dependencies) {
589 for (const dependency of dependencies) {
590 this.addDependency(dependency);
591 }
592 }
593
594 addOpList(opList) {
595 if (!(opList instanceof OperatorList)) {
596 (0, _util.warn)('addOpList - ignoring invalid "opList" parameter.');
597 return;
598 }
599
600 for (const dependency of opList.dependencies) {
601 this.dependencies.add(dependency);
602 }
603
604 for (let i = 0, ii = opList.length; i < ii; i++) {
605 this.addOp(opList.fnArray[i], opList.argsArray[i]);
606 }
607 }
608
609 getIR() {
610 return {
611 fnArray: this.fnArray,
612 argsArray: this.argsArray,
613 length: this.length
614 };
615 }
616
617 get _transfers() {
618 const transfers = [];
619 const {
620 fnArray,
621 argsArray,
622 length
623 } = this;
624
625 for (let i = 0; i < length; i++) {
626 switch (fnArray[i]) {
627 case _util.OPS.paintInlineImageXObject:
628 case _util.OPS.paintInlineImageXObjectGroup:
629 case _util.OPS.paintImageMaskXObject:
630 const arg = argsArray[i][0];
631
632 if (!arg.cached && arg.data && arg.data.buffer instanceof ArrayBuffer) {
633 transfers.push(arg.data.buffer);
634 }
635
636 break;
637 }
638 }
639
640 return transfers;
641 }
642
643 flush(lastChunk = false, separateAnnots = null) {
644 this.optimizer.flush();
645 const length = this.length;
646 this._totalLength += length;
647
648 this._streamSink.enqueue({
649 fnArray: this.fnArray,
650 argsArray: this.argsArray,
651 lastChunk,
652 separateAnnots,
653 length
654 }, 1, this._transfers);
655
656 this.dependencies.clear();
657 this.fnArray.length = 0;
658 this.argsArray.length = 0;
659 this.weight = 0;
660 this.optimizer.reset();
661 }
662
663}
664
665exports.OperatorList = OperatorList;
\No newline at end of file