2
0

operator_list.js 18 KB

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