@vaadin-component-factory/vcf-pdf-viewer 0.9.0 → 0.9.4

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.
Files changed (174) hide show
  1. package/README.md +1 -1
  2. package/package.json +33 -18
  3. package/{src/display → pdfjs/dist}/display_utils.js +344 -139
  4. package/{src/display → pdfjs/dist}/fetch_stream.js +115 -97
  5. package/pdfjs/dist/get.js +1857 -0
  6. package/pdfjs/dist/index.js +767 -0
  7. package/pdfjs/dist/l10n_utils.js +140 -0
  8. package/{src/shared → pdfjs/dist}/message_handler.js +243 -259
  9. package/{src/display → pdfjs/dist}/network.js +149 -87
  10. package/{src/display/content_disposition.js → pdfjs/dist/network_utils.js} +167 -55
  11. package/{src/display → pdfjs/dist}/node_stream.js +133 -98
  12. package/pdfjs/dist/pdf.js +12778 -0
  13. package/pdfjs/dist/pdf_link_service.js +638 -0
  14. package/pdfjs/dist/pdf_rendering_queue.js +199 -0
  15. package/pdfjs/dist/pdf_thumbnail_viewer.js +819 -0
  16. package/pdfjs/dist/pdf_viewer.js +3598 -0
  17. package/pdfjs/dist/typeof.js +100 -0
  18. package/pdfjs/dist/ui_utils.js +1033 -0
  19. package/{src/shared → pdfjs/dist}/util.js +301 -287
  20. package/pdfjs/dist/worker.js +62813 -0
  21. package/src/vcf-pdf-viewer.js +77 -27
  22. package/theme/lumo/vcf-pdf-viewer-styles.js +1 -1
  23. package/theme/material/vcf-pdf-viewer.js +2 -2
  24. package/src/core/.eslintrc +0 -13
  25. package/src/core/annotation.js +0 -2948
  26. package/src/core/arithmetic_decoder.js +0 -182
  27. package/src/core/ascii_85_stream.js +0 -98
  28. package/src/core/ascii_hex_stream.js +0 -79
  29. package/src/core/base_stream.js +0 -110
  30. package/src/core/bidi.js +0 -438
  31. package/src/core/calibri_factors.js +0 -308
  32. package/src/core/catalog.js +0 -1459
  33. package/src/core/ccitt.js +0 -1062
  34. package/src/core/ccitt_stream.js +0 -60
  35. package/src/core/cff_font.js +0 -116
  36. package/src/core/cff_parser.js +0 -1949
  37. package/src/core/charsets.js +0 -119
  38. package/src/core/chunked_stream.js +0 -557
  39. package/src/core/cmap.js +0 -1039
  40. package/src/core/colorspace.js +0 -1533
  41. package/src/core/core_utils.js +0 -464
  42. package/src/core/crypto.js +0 -1900
  43. package/src/core/decode_stream.js +0 -170
  44. package/src/core/decrypt_stream.js +0 -59
  45. package/src/core/default_appearance.js +0 -99
  46. package/src/core/document.js +0 -1456
  47. package/src/core/encodings.js +0 -301
  48. package/src/core/evaluator.js +0 -4601
  49. package/src/core/file_spec.js +0 -108
  50. package/src/core/flate_stream.js +0 -402
  51. package/src/core/font_renderer.js +0 -882
  52. package/src/core/fonts.js +0 -3260
  53. package/src/core/fonts_utils.js +0 -221
  54. package/src/core/function.js +0 -1257
  55. package/src/core/glyf.js +0 -706
  56. package/src/core/glyphlist.js +0 -4558
  57. package/src/core/helvetica_factors.js +0 -353
  58. package/src/core/image.js +0 -802
  59. package/src/core/image_utils.js +0 -291
  60. package/src/core/jbig2.js +0 -2572
  61. package/src/core/jbig2_stream.js +0 -73
  62. package/src/core/jpeg_stream.js +0 -105
  63. package/src/core/jpg.js +0 -1416
  64. package/src/core/jpx.js +0 -2343
  65. package/src/core/jpx_stream.js +0 -87
  66. package/src/core/liberationsans_widths.js +0 -221
  67. package/src/core/lzw_stream.js +0 -150
  68. package/src/core/metadata_parser.js +0 -146
  69. package/src/core/metrics.js +0 -2970
  70. package/src/core/murmurhash3.js +0 -139
  71. package/src/core/myriadpro_factors.js +0 -290
  72. package/src/core/name_number_tree.js +0 -153
  73. package/src/core/object_loader.js +0 -149
  74. package/src/core/opentype_file_builder.js +0 -154
  75. package/src/core/operator_list.js +0 -734
  76. package/src/core/parser.js +0 -1416
  77. package/src/core/pattern.js +0 -985
  78. package/src/core/pdf_manager.js +0 -217
  79. package/src/core/predictor_stream.js +0 -238
  80. package/src/core/primitives.js +0 -402
  81. package/src/core/ps_parser.js +0 -272
  82. package/src/core/run_length_stream.js +0 -61
  83. package/src/core/segoeui_factors.js +0 -308
  84. package/src/core/standard_fonts.js +0 -817
  85. package/src/core/stream.js +0 -103
  86. package/src/core/struct_tree.js +0 -335
  87. package/src/core/to_unicode_map.js +0 -103
  88. package/src/core/type1_font.js +0 -421
  89. package/src/core/type1_parser.js +0 -776
  90. package/src/core/unicode.js +0 -1649
  91. package/src/core/worker.js +0 -848
  92. package/src/core/worker_stream.js +0 -135
  93. package/src/core/writer.js +0 -278
  94. package/src/core/xfa/bind.js +0 -652
  95. package/src/core/xfa/builder.js +0 -207
  96. package/src/core/xfa/config.js +0 -1926
  97. package/src/core/xfa/connection_set.js +0 -202
  98. package/src/core/xfa/data.js +0 -82
  99. package/src/core/xfa/datasets.js +0 -76
  100. package/src/core/xfa/factory.js +0 -111
  101. package/src/core/xfa/fonts.js +0 -181
  102. package/src/core/xfa/formcalc_lexer.js +0 -385
  103. package/src/core/xfa/formcalc_parser.js +0 -1340
  104. package/src/core/xfa/html_utils.js +0 -639
  105. package/src/core/xfa/layout.js +0 -383
  106. package/src/core/xfa/locale_set.js +0 -345
  107. package/src/core/xfa/namespaces.js +0 -81
  108. package/src/core/xfa/parser.js +0 -184
  109. package/src/core/xfa/setup.js +0 -38
  110. package/src/core/xfa/signature.js +0 -40
  111. package/src/core/xfa/som.js +0 -338
  112. package/src/core/xfa/stylesheet.js +0 -40
  113. package/src/core/xfa/template.js +0 -6260
  114. package/src/core/xfa/text.js +0 -290
  115. package/src/core/xfa/unknown.js +0 -29
  116. package/src/core/xfa/utils.js +0 -217
  117. package/src/core/xfa/xdp.js +0 -59
  118. package/src/core/xfa/xfa_object.js +0 -1130
  119. package/src/core/xfa/xhtml.js +0 -543
  120. package/src/core/xfa_fonts.js +0 -208
  121. package/src/core/xml_parser.js +0 -507
  122. package/src/core/xref.js +0 -899
  123. package/src/display/annotation_layer.js +0 -2107
  124. package/src/display/annotation_storage.js +0 -113
  125. package/src/display/api.js +0 -3292
  126. package/src/display/base_factory.js +0 -180
  127. package/src/display/canvas.js +0 -2828
  128. package/src/display/font_loader.js +0 -484
  129. package/src/display/metadata.js +0 -41
  130. package/src/display/network_utils.js +0 -100
  131. package/src/display/node_utils.js +0 -83
  132. package/src/display/optional_content_config.js +0 -189
  133. package/src/display/pattern_helper.js +0 -659
  134. package/src/display/svg.js +0 -1709
  135. package/src/display/text_layer.js +0 -847
  136. package/src/display/transport_stream.js +0 -303
  137. package/src/display/worker_options.js +0 -40
  138. package/src/display/xfa_layer.js +0 -204
  139. package/src/doc_helper.js +0 -25
  140. package/src/images/logo.svg +0 -41
  141. package/src/interfaces.js +0 -169
  142. package/src/license_header.js +0 -14
  143. package/src/license_header_libre.js +0 -21
  144. package/src/pdf.image_decoders.js +0 -46
  145. package/src/pdf.js +0 -146
  146. package/src/pdf.sandbox.external.js +0 -181
  147. package/src/pdf.sandbox.js +0 -151
  148. package/src/pdf.scripting.js +0 -25
  149. package/src/pdf.worker.entry.js +0 -19
  150. package/src/pdf.worker.js +0 -23
  151. package/src/scripting_api/aform.js +0 -608
  152. package/src/scripting_api/app.js +0 -621
  153. package/src/scripting_api/color.js +0 -129
  154. package/src/scripting_api/common.js +0 -58
  155. package/src/scripting_api/console.js +0 -38
  156. package/src/scripting_api/constants.js +0 -208
  157. package/src/scripting_api/doc.js +0 -1195
  158. package/src/scripting_api/error.js +0 -23
  159. package/src/scripting_api/event.js +0 -232
  160. package/src/scripting_api/field.js +0 -620
  161. package/src/scripting_api/fullscreen.js +0 -145
  162. package/src/scripting_api/initialization.js +0 -223
  163. package/src/scripting_api/pdf_object.js +0 -24
  164. package/src/scripting_api/print_params.js +0 -146
  165. package/src/scripting_api/proxy.js +0 -139
  166. package/src/scripting_api/thermometer.js +0 -69
  167. package/src/scripting_api/util.js +0 -581
  168. package/src/shared/.eslintrc +0 -13
  169. package/src/shared/cffStandardStrings.js +0 -311
  170. package/src/shared/compatibility.js +0 -114
  171. package/src/shared/fonts_utils.js +0 -429
  172. package/src/shared/is_node.js +0 -27
  173. package/src/shared/scripting_utils.js +0 -85
  174. package/src/worker_loader.js +0 -32
@@ -1,2828 +0,0 @@
1
- /* Copyright 2012 Mozilla Foundation
2
- *
3
- * Licensed under the Apache License, Version 2.0 (the "License");
4
- * you may not use this file except in compliance with the License.
5
- * You may obtain a copy of the License at
6
- *
7
- * http://www.apache.org/licenses/LICENSE-2.0
8
- *
9
- * Unless required by applicable law or agreed to in writing, software
10
- * distributed under the License is distributed on an "AS IS" BASIS,
11
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- * See the License for the specific language governing permissions and
13
- * limitations under the License.
14
- */
15
-
16
- import {
17
- FONT_IDENTITY_MATRIX,
18
- IDENTITY_MATRIX,
19
- ImageKind,
20
- info,
21
- IsLittleEndianCached,
22
- isNum,
23
- OPS,
24
- shadow,
25
- TextRenderingMode,
26
- unreachable,
27
- Util,
28
- warn,
29
- } from "../shared/util.js";
30
- import { getShadingPattern, TilingPattern } from "./pattern_helper.js";
31
-
32
- // <canvas> contexts store most of the state we need natively.
33
- // However, PDF needs a bit more state, which we store here.
34
- // Minimal font size that would be used during canvas fillText operations.
35
- const MIN_FONT_SIZE = 16;
36
- // Maximum font size that would be used during canvas fillText operations.
37
- const MAX_FONT_SIZE = 100;
38
- const MAX_GROUP_SIZE = 4096;
39
-
40
- const COMPILE_TYPE3_GLYPHS = true;
41
- const MAX_SIZE_TO_COMPILE = 1000;
42
-
43
- const FULL_CHUNK_HEIGHT = 16;
44
-
45
- // Because of https://bugs.chromium.org/p/chromium/issues/detail?id=1170396
46
- // some curves aren't rendered correctly.
47
- // Multiplying lineWidth by this factor should help to have "correct"
48
- // rendering with no artifacts.
49
- // Once the bug is fixed upstream, we can remove this constant and its use.
50
- const LINEWIDTH_SCALE_FACTOR = 1.000001;
51
-
52
- function addContextCurrentTransform(ctx) {
53
- // If the context doesn't expose a `mozCurrentTransform`, add a JS based one.
54
- if (ctx.mozCurrentTransform) {
55
- return;
56
- }
57
- ctx._originalSave = ctx.save;
58
- ctx._originalRestore = ctx.restore;
59
- ctx._originalRotate = ctx.rotate;
60
- ctx._originalScale = ctx.scale;
61
- ctx._originalTranslate = ctx.translate;
62
- ctx._originalTransform = ctx.transform;
63
- ctx._originalSetTransform = ctx.setTransform;
64
- ctx._originalResetTransform = ctx.resetTransform;
65
-
66
- ctx._transformMatrix = ctx._transformMatrix || [1, 0, 0, 1, 0, 0];
67
- ctx._transformStack = [];
68
-
69
- try {
70
- // The call to getOwnPropertyDescriptor throws an exception in Node.js:
71
- // "TypeError: Method lineWidth called on incompatible receiver
72
- // #<CanvasRenderingContext2D>".
73
- const desc = Object.getOwnPropertyDescriptor(
74
- Object.getPrototypeOf(ctx),
75
- "lineWidth"
76
- );
77
-
78
- ctx._setLineWidth = desc.set;
79
- ctx._getLineWidth = desc.get;
80
-
81
- Object.defineProperty(ctx, "lineWidth", {
82
- set: function setLineWidth(width) {
83
- this._setLineWidth(width * LINEWIDTH_SCALE_FACTOR);
84
- },
85
- get: function getLineWidth() {
86
- return this._getLineWidth();
87
- },
88
- });
89
- } catch (_) {}
90
-
91
- Object.defineProperty(ctx, "mozCurrentTransform", {
92
- get: function getCurrentTransform() {
93
- return this._transformMatrix;
94
- },
95
- });
96
-
97
- Object.defineProperty(ctx, "mozCurrentTransformInverse", {
98
- get: function getCurrentTransformInverse() {
99
- // Calculation done using WolframAlpha:
100
- // http://www.wolframalpha.com/input/?
101
- // i=Inverse+{{a%2C+c%2C+e}%2C+{b%2C+d%2C+f}%2C+{0%2C+0%2C+1}}
102
-
103
- const [a, b, c, d, e, f] = this._transformMatrix;
104
- const ad_bc = a * d - b * c;
105
- const bc_ad = b * c - a * d;
106
-
107
- return [
108
- d / ad_bc,
109
- b / bc_ad,
110
- c / bc_ad,
111
- a / ad_bc,
112
- (d * e - c * f) / bc_ad,
113
- (b * e - a * f) / ad_bc,
114
- ];
115
- },
116
- });
117
-
118
- ctx.save = function ctxSave() {
119
- const old = this._transformMatrix;
120
- this._transformStack.push(old);
121
- this._transformMatrix = old.slice(0, 6);
122
-
123
- this._originalSave();
124
- };
125
-
126
- ctx.restore = function ctxRestore() {
127
- const prev = this._transformStack.pop();
128
- if (prev) {
129
- this._transformMatrix = prev;
130
- this._originalRestore();
131
- }
132
- };
133
-
134
- ctx.translate = function ctxTranslate(x, y) {
135
- const m = this._transformMatrix;
136
- m[4] = m[0] * x + m[2] * y + m[4];
137
- m[5] = m[1] * x + m[3] * y + m[5];
138
-
139
- this._originalTranslate(x, y);
140
- };
141
-
142
- ctx.scale = function ctxScale(x, y) {
143
- const m = this._transformMatrix;
144
- m[0] *= x;
145
- m[1] *= x;
146
- m[2] *= y;
147
- m[3] *= y;
148
-
149
- this._originalScale(x, y);
150
- };
151
-
152
- ctx.transform = function ctxTransform(a, b, c, d, e, f) {
153
- const m = this._transformMatrix;
154
- this._transformMatrix = [
155
- m[0] * a + m[2] * b,
156
- m[1] * a + m[3] * b,
157
- m[0] * c + m[2] * d,
158
- m[1] * c + m[3] * d,
159
- m[0] * e + m[2] * f + m[4],
160
- m[1] * e + m[3] * f + m[5],
161
- ];
162
-
163
- ctx._originalTransform(a, b, c, d, e, f);
164
- };
165
-
166
- ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) {
167
- this._transformMatrix = [a, b, c, d, e, f];
168
-
169
- ctx._originalSetTransform(a, b, c, d, e, f);
170
- };
171
-
172
- ctx.resetTransform = function ctxResetTransform() {
173
- this._transformMatrix = [1, 0, 0, 1, 0, 0];
174
-
175
- ctx._originalResetTransform();
176
- };
177
-
178
- ctx.rotate = function ctxRotate(angle) {
179
- const cosValue = Math.cos(angle);
180
- const sinValue = Math.sin(angle);
181
-
182
- const m = this._transformMatrix;
183
- this._transformMatrix = [
184
- m[0] * cosValue + m[2] * sinValue,
185
- m[1] * cosValue + m[3] * sinValue,
186
- m[0] * -sinValue + m[2] * cosValue,
187
- m[1] * -sinValue + m[3] * cosValue,
188
- m[4],
189
- m[5],
190
- ];
191
-
192
- this._originalRotate(angle);
193
- };
194
- }
195
-
196
- class CachedCanvases {
197
- constructor(canvasFactory) {
198
- this.canvasFactory = canvasFactory;
199
- this.cache = Object.create(null);
200
- }
201
-
202
- getCanvas(id, width, height, trackTransform) {
203
- let canvasEntry;
204
- if (this.cache[id] !== undefined) {
205
- canvasEntry = this.cache[id];
206
- this.canvasFactory.reset(canvasEntry, width, height);
207
- // reset canvas transform for emulated mozCurrentTransform, if needed
208
- canvasEntry.context.setTransform(1, 0, 0, 1, 0, 0);
209
- } else {
210
- canvasEntry = this.canvasFactory.create(width, height);
211
- this.cache[id] = canvasEntry;
212
- }
213
- if (trackTransform) {
214
- addContextCurrentTransform(canvasEntry.context);
215
- }
216
- return canvasEntry;
217
- }
218
-
219
- clear() {
220
- for (const id in this.cache) {
221
- const canvasEntry = this.cache[id];
222
- this.canvasFactory.destroy(canvasEntry);
223
- delete this.cache[id];
224
- }
225
- }
226
- }
227
-
228
- function compileType3Glyph(imgData) {
229
- const POINT_TO_PROCESS_LIMIT = 1000;
230
- const POINT_TYPES = new Uint8Array([
231
- 0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0,
232
- ]);
233
-
234
- const width = imgData.width,
235
- height = imgData.height,
236
- width1 = width + 1;
237
- let i, ii, j, j0;
238
- const points = new Uint8Array(width1 * (height + 1));
239
-
240
- // decodes bit-packed mask data
241
- const lineSize = (width + 7) & ~7,
242
- data0 = imgData.data;
243
- const data = new Uint8Array(lineSize * height);
244
- let pos = 0;
245
- for (i = 0, ii = data0.length; i < ii; i++) {
246
- const elem = data0[i];
247
- let mask = 128;
248
- while (mask > 0) {
249
- data[pos++] = elem & mask ? 0 : 255;
250
- mask >>= 1;
251
- }
252
- }
253
-
254
- // finding interesting points: every point is located between mask pixels,
255
- // so there will be points of the (width + 1)x(height + 1) grid. Every point
256
- // will have flags assigned based on neighboring mask pixels:
257
- // 4 | 8
258
- // --P--
259
- // 2 | 1
260
- // We are interested only in points with the flags:
261
- // - outside corners: 1, 2, 4, 8;
262
- // - inside corners: 7, 11, 13, 14;
263
- // - and, intersections: 5, 10.
264
- let count = 0;
265
- pos = 0;
266
- if (data[pos] !== 0) {
267
- points[0] = 1;
268
- ++count;
269
- }
270
- for (j = 1; j < width; j++) {
271
- if (data[pos] !== data[pos + 1]) {
272
- points[j] = data[pos] ? 2 : 1;
273
- ++count;
274
- }
275
- pos++;
276
- }
277
- if (data[pos] !== 0) {
278
- points[j] = 2;
279
- ++count;
280
- }
281
- for (i = 1; i < height; i++) {
282
- pos = i * lineSize;
283
- j0 = i * width1;
284
- if (data[pos - lineSize] !== data[pos]) {
285
- points[j0] = data[pos] ? 1 : 8;
286
- ++count;
287
- }
288
- // 'sum' is the position of the current pixel configuration in the 'TYPES'
289
- // array (in order 8-1-2-4, so we can use '>>2' to shift the column).
290
- let sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0);
291
- for (j = 1; j < width; j++) {
292
- sum =
293
- (sum >> 2) +
294
- (data[pos + 1] ? 4 : 0) +
295
- (data[pos - lineSize + 1] ? 8 : 0);
296
- if (POINT_TYPES[sum]) {
297
- points[j0 + j] = POINT_TYPES[sum];
298
- ++count;
299
- }
300
- pos++;
301
- }
302
- if (data[pos - lineSize] !== data[pos]) {
303
- points[j0 + j] = data[pos] ? 2 : 4;
304
- ++count;
305
- }
306
-
307
- if (count > POINT_TO_PROCESS_LIMIT) {
308
- return null;
309
- }
310
- }
311
-
312
- pos = lineSize * (height - 1);
313
- j0 = i * width1;
314
- if (data[pos] !== 0) {
315
- points[j0] = 8;
316
- ++count;
317
- }
318
- for (j = 1; j < width; j++) {
319
- if (data[pos] !== data[pos + 1]) {
320
- points[j0 + j] = data[pos] ? 4 : 8;
321
- ++count;
322
- }
323
- pos++;
324
- }
325
- if (data[pos] !== 0) {
326
- points[j0 + j] = 4;
327
- ++count;
328
- }
329
- if (count > POINT_TO_PROCESS_LIMIT) {
330
- return null;
331
- }
332
-
333
- // building outlines
334
- const steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]);
335
- const outlines = [];
336
- for (i = 0; count && i <= height; i++) {
337
- let p = i * width1;
338
- const end = p + width;
339
- while (p < end && !points[p]) {
340
- p++;
341
- }
342
- if (p === end) {
343
- continue;
344
- }
345
- const coords = [p % width1, i];
346
-
347
- const p0 = p;
348
- let type = points[p];
349
- do {
350
- const step = steps[type];
351
- do {
352
- p += step;
353
- } while (!points[p]);
354
-
355
- const pp = points[p];
356
- if (pp !== 5 && pp !== 10) {
357
- // set new direction
358
- type = pp;
359
- // delete mark
360
- points[p] = 0;
361
- } else {
362
- // type is 5 or 10, ie, a crossing
363
- // set new direction
364
- type = pp & ((0x33 * type) >> 4);
365
- // set new type for "future hit"
366
- points[p] &= (type >> 2) | (type << 2);
367
- }
368
-
369
- coords.push(p % width1, (p / width1) | 0);
370
-
371
- if (!points[p]) {
372
- --count;
373
- }
374
- } while (p0 !== p);
375
- outlines.push(coords);
376
- --i;
377
- }
378
-
379
- const drawOutline = function (c) {
380
- c.save();
381
- // the path shall be painted in [0..1]x[0..1] space
382
- c.scale(1 / width, -1 / height);
383
- c.translate(0, -height);
384
- c.beginPath();
385
- for (let k = 0, kk = outlines.length; k < kk; k++) {
386
- const o = outlines[k];
387
- c.moveTo(o[0], o[1]);
388
- for (let l = 2, ll = o.length; l < ll; l += 2) {
389
- c.lineTo(o[l], o[l + 1]);
390
- }
391
- }
392
- c.fill();
393
- c.beginPath();
394
- c.restore();
395
- };
396
-
397
- return drawOutline;
398
- }
399
-
400
- class CanvasExtraState {
401
- constructor() {
402
- // Are soft masks and alpha values shapes or opacities?
403
- this.alphaIsShape = false;
404
- this.fontSize = 0;
405
- this.fontSizeScale = 1;
406
- this.textMatrix = IDENTITY_MATRIX;
407
- this.textMatrixScale = 1;
408
- this.fontMatrix = FONT_IDENTITY_MATRIX;
409
- this.leading = 0;
410
- // Current point (in user coordinates)
411
- this.x = 0;
412
- this.y = 0;
413
- // Start of text line (in text coordinates)
414
- this.lineX = 0;
415
- this.lineY = 0;
416
- // Character and word spacing
417
- this.charSpacing = 0;
418
- this.wordSpacing = 0;
419
- this.textHScale = 1;
420
- this.textRenderingMode = TextRenderingMode.FILL;
421
- this.textRise = 0;
422
- // Default fore and background colors
423
- this.fillColor = "#000000";
424
- this.strokeColor = "#000000";
425
- this.patternFill = false;
426
- // Note: fill alpha applies to all non-stroking operations
427
- this.fillAlpha = 1;
428
- this.strokeAlpha = 1;
429
- this.lineWidth = 1;
430
- this.activeSMask = null;
431
- this.resumeSMaskCtx = null; // nonclonable field (see the save method below)
432
- this.transferMaps = null;
433
- }
434
-
435
- clone() {
436
- return Object.create(this);
437
- }
438
-
439
- setCurrentPoint(x, y) {
440
- this.x = x;
441
- this.y = y;
442
- }
443
- }
444
-
445
- /**
446
- * @type {any}
447
- */
448
- const CanvasGraphics = (function CanvasGraphicsClosure() {
449
- // Defines the time the executeOperatorList is going to be executing
450
- // before it stops and shedules a continue of execution.
451
- const EXECUTION_TIME = 15;
452
- // Defines the number of steps before checking the execution time
453
- const EXECUTION_STEPS = 10;
454
-
455
- function putBinaryImageData(ctx, imgData, transferMaps = null) {
456
- if (typeof ImageData !== "undefined" && imgData instanceof ImageData) {
457
- ctx.putImageData(imgData, 0, 0);
458
- return;
459
- }
460
-
461
- // Put the image data to the canvas in chunks, rather than putting the
462
- // whole image at once. This saves JS memory, because the ImageData object
463
- // is smaller. It also possibly saves C++ memory within the implementation
464
- // of putImageData(). (E.g. in Firefox we make two short-lived copies of
465
- // the data passed to putImageData()). |n| shouldn't be too small, however,
466
- // because too many putImageData() calls will slow things down.
467
- //
468
- // Note: as written, if the last chunk is partial, the putImageData() call
469
- // will (conceptually) put pixels past the bounds of the canvas. But
470
- // that's ok; any such pixels are ignored.
471
-
472
- const height = imgData.height,
473
- width = imgData.width;
474
- const partialChunkHeight = height % FULL_CHUNK_HEIGHT;
475
- const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
476
- const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
477
-
478
- const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
479
- let srcPos = 0,
480
- destPos;
481
- const src = imgData.data;
482
- const dest = chunkImgData.data;
483
- let i, j, thisChunkHeight, elemsInThisChunk;
484
-
485
- let transferMapRed, transferMapGreen, transferMapBlue, transferMapGray;
486
- if (transferMaps) {
487
- switch (transferMaps.length) {
488
- case 1:
489
- transferMapRed = transferMaps[0];
490
- transferMapGreen = transferMaps[0];
491
- transferMapBlue = transferMaps[0];
492
- transferMapGray = transferMaps[0];
493
- break;
494
- case 4:
495
- transferMapRed = transferMaps[0];
496
- transferMapGreen = transferMaps[1];
497
- transferMapBlue = transferMaps[2];
498
- transferMapGray = transferMaps[3];
499
- break;
500
- }
501
- }
502
-
503
- // There are multiple forms in which the pixel data can be passed, and
504
- // imgData.kind tells us which one this is.
505
- if (imgData.kind === ImageKind.GRAYSCALE_1BPP) {
506
- // Grayscale, 1 bit per pixel (i.e. black-and-white).
507
- const srcLength = src.byteLength;
508
- const dest32 = new Uint32Array(dest.buffer, 0, dest.byteLength >> 2);
509
- const dest32DataLength = dest32.length;
510
- const fullSrcDiff = (width + 7) >> 3;
511
- let white = 0xffffffff;
512
- let black = IsLittleEndianCached.value ? 0xff000000 : 0x000000ff;
513
-
514
- if (transferMapGray) {
515
- if (transferMapGray[0] === 0xff && transferMapGray[0xff] === 0) {
516
- [white, black] = [black, white];
517
- }
518
- }
519
-
520
- for (i = 0; i < totalChunks; i++) {
521
- thisChunkHeight =
522
- i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight;
523
- destPos = 0;
524
- for (j = 0; j < thisChunkHeight; j++) {
525
- const srcDiff = srcLength - srcPos;
526
- let k = 0;
527
- const kEnd = srcDiff > fullSrcDiff ? width : srcDiff * 8 - 7;
528
- const kEndUnrolled = kEnd & ~7;
529
- let mask = 0;
530
- let srcByte = 0;
531
- for (; k < kEndUnrolled; k += 8) {
532
- srcByte = src[srcPos++];
533
- dest32[destPos++] = srcByte & 128 ? white : black;
534
- dest32[destPos++] = srcByte & 64 ? white : black;
535
- dest32[destPos++] = srcByte & 32 ? white : black;
536
- dest32[destPos++] = srcByte & 16 ? white : black;
537
- dest32[destPos++] = srcByte & 8 ? white : black;
538
- dest32[destPos++] = srcByte & 4 ? white : black;
539
- dest32[destPos++] = srcByte & 2 ? white : black;
540
- dest32[destPos++] = srcByte & 1 ? white : black;
541
- }
542
- for (; k < kEnd; k++) {
543
- if (mask === 0) {
544
- srcByte = src[srcPos++];
545
- mask = 128;
546
- }
547
-
548
- dest32[destPos++] = srcByte & mask ? white : black;
549
- mask >>= 1;
550
- }
551
- }
552
- // We ran out of input. Make all remaining pixels transparent.
553
- while (destPos < dest32DataLength) {
554
- dest32[destPos++] = 0;
555
- }
556
-
557
- ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
558
- }
559
- } else if (imgData.kind === ImageKind.RGBA_32BPP) {
560
- // RGBA, 32-bits per pixel.
561
- const hasTransferMaps = !!(
562
- transferMapRed ||
563
- transferMapGreen ||
564
- transferMapBlue
565
- );
566
-
567
- j = 0;
568
- elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4;
569
- for (i = 0; i < fullChunks; i++) {
570
- dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
571
- srcPos += elemsInThisChunk;
572
-
573
- if (hasTransferMaps) {
574
- for (let k = 0; k < elemsInThisChunk; k += 4) {
575
- if (transferMapRed) {
576
- dest[k + 0] = transferMapRed[dest[k + 0]];
577
- }
578
- if (transferMapGreen) {
579
- dest[k + 1] = transferMapGreen[dest[k + 1]];
580
- }
581
- if (transferMapBlue) {
582
- dest[k + 2] = transferMapBlue[dest[k + 2]];
583
- }
584
- }
585
- }
586
-
587
- ctx.putImageData(chunkImgData, 0, j);
588
- j += FULL_CHUNK_HEIGHT;
589
- }
590
- if (i < totalChunks) {
591
- elemsInThisChunk = width * partialChunkHeight * 4;
592
- dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
593
-
594
- if (hasTransferMaps) {
595
- for (let k = 0; k < elemsInThisChunk; k += 4) {
596
- if (transferMapRed) {
597
- dest[k + 0] = transferMapRed[dest[k + 0]];
598
- }
599
- if (transferMapGreen) {
600
- dest[k + 1] = transferMapGreen[dest[k + 1]];
601
- }
602
- if (transferMapBlue) {
603
- dest[k + 2] = transferMapBlue[dest[k + 2]];
604
- }
605
- }
606
- }
607
-
608
- ctx.putImageData(chunkImgData, 0, j);
609
- }
610
- } else if (imgData.kind === ImageKind.RGB_24BPP) {
611
- // RGB, 24-bits per pixel.
612
- const hasTransferMaps = !!(
613
- transferMapRed ||
614
- transferMapGreen ||
615
- transferMapBlue
616
- );
617
-
618
- thisChunkHeight = FULL_CHUNK_HEIGHT;
619
- elemsInThisChunk = width * thisChunkHeight;
620
- for (i = 0; i < totalChunks; i++) {
621
- if (i >= fullChunks) {
622
- thisChunkHeight = partialChunkHeight;
623
- elemsInThisChunk = width * thisChunkHeight;
624
- }
625
-
626
- destPos = 0;
627
- for (j = elemsInThisChunk; j--; ) {
628
- dest[destPos++] = src[srcPos++];
629
- dest[destPos++] = src[srcPos++];
630
- dest[destPos++] = src[srcPos++];
631
- dest[destPos++] = 255;
632
- }
633
-
634
- if (hasTransferMaps) {
635
- for (let k = 0; k < destPos; k += 4) {
636
- if (transferMapRed) {
637
- dest[k + 0] = transferMapRed[dest[k + 0]];
638
- }
639
- if (transferMapGreen) {
640
- dest[k + 1] = transferMapGreen[dest[k + 1]];
641
- }
642
- if (transferMapBlue) {
643
- dest[k + 2] = transferMapBlue[dest[k + 2]];
644
- }
645
- }
646
- }
647
-
648
- ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
649
- }
650
- } else {
651
- throw new Error(`bad image kind: ${imgData.kind}`);
652
- }
653
- }
654
-
655
- function putBinaryImageMask(ctx, imgData) {
656
- const height = imgData.height,
657
- width = imgData.width;
658
- const partialChunkHeight = height % FULL_CHUNK_HEIGHT;
659
- const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
660
- const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
661
-
662
- const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
663
- let srcPos = 0;
664
- const src = imgData.data;
665
- const dest = chunkImgData.data;
666
-
667
- for (let i = 0; i < totalChunks; i++) {
668
- const thisChunkHeight =
669
- i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight;
670
-
671
- // Expand the mask so it can be used by the canvas. Any required
672
- // inversion has already been handled.
673
- let destPos = 3; // alpha component offset
674
- for (let j = 0; j < thisChunkHeight; j++) {
675
- let elem,
676
- mask = 0;
677
- for (let k = 0; k < width; k++) {
678
- if (!mask) {
679
- elem = src[srcPos++];
680
- mask = 128;
681
- }
682
- dest[destPos] = elem & mask ? 0 : 255;
683
- destPos += 4;
684
- mask >>= 1;
685
- }
686
- }
687
- ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
688
- }
689
- }
690
-
691
- function copyCtxState(sourceCtx, destCtx) {
692
- const properties = [
693
- "strokeStyle",
694
- "fillStyle",
695
- "fillRule",
696
- "globalAlpha",
697
- "lineWidth",
698
- "lineCap",
699
- "lineJoin",
700
- "miterLimit",
701
- "globalCompositeOperation",
702
- "font",
703
- ];
704
- for (let i = 0, ii = properties.length; i < ii; i++) {
705
- const property = properties[i];
706
- if (sourceCtx[property] !== undefined) {
707
- destCtx[property] = sourceCtx[property];
708
- }
709
- }
710
- if (sourceCtx.setLineDash !== undefined) {
711
- destCtx.setLineDash(sourceCtx.getLineDash());
712
- destCtx.lineDashOffset = sourceCtx.lineDashOffset;
713
- }
714
- }
715
-
716
- function resetCtxToDefault(ctx) {
717
- ctx.strokeStyle = "#000000";
718
- ctx.fillStyle = "#000000";
719
- ctx.fillRule = "nonzero";
720
- ctx.globalAlpha = 1;
721
- ctx.lineWidth = 1;
722
- ctx.lineCap = "butt";
723
- ctx.lineJoin = "miter";
724
- ctx.miterLimit = 10;
725
- ctx.globalCompositeOperation = "source-over";
726
- ctx.font = "10px sans-serif";
727
- if (ctx.setLineDash !== undefined) {
728
- ctx.setLineDash([]);
729
- ctx.lineDashOffset = 0;
730
- }
731
- }
732
-
733
- function composeSMaskBackdrop(bytes, r0, g0, b0) {
734
- const length = bytes.length;
735
- for (let i = 3; i < length; i += 4) {
736
- const alpha = bytes[i];
737
- if (alpha === 0) {
738
- bytes[i - 3] = r0;
739
- bytes[i - 2] = g0;
740
- bytes[i - 1] = b0;
741
- } else if (alpha < 255) {
742
- const alpha_ = 255 - alpha;
743
- bytes[i - 3] = (bytes[i - 3] * alpha + r0 * alpha_) >> 8;
744
- bytes[i - 2] = (bytes[i - 2] * alpha + g0 * alpha_) >> 8;
745
- bytes[i - 1] = (bytes[i - 1] * alpha + b0 * alpha_) >> 8;
746
- }
747
- }
748
- }
749
-
750
- function composeSMaskAlpha(maskData, layerData, transferMap) {
751
- const length = maskData.length;
752
- const scale = 1 / 255;
753
- for (let i = 3; i < length; i += 4) {
754
- const alpha = transferMap ? transferMap[maskData[i]] : maskData[i];
755
- layerData[i] = (layerData[i] * alpha * scale) | 0;
756
- }
757
- }
758
-
759
- function composeSMaskLuminosity(maskData, layerData, transferMap) {
760
- const length = maskData.length;
761
- for (let i = 3; i < length; i += 4) {
762
- const y =
763
- maskData[i - 3] * 77 + // * 0.3 / 255 * 0x10000
764
- maskData[i - 2] * 152 + // * 0.59 ....
765
- maskData[i - 1] * 28; // * 0.11 ....
766
- layerData[i] = transferMap
767
- ? (layerData[i] * transferMap[y >> 8]) >> 8
768
- : (layerData[i] * y) >> 16;
769
- }
770
- }
771
-
772
- function genericComposeSMask(
773
- maskCtx,
774
- layerCtx,
775
- width,
776
- height,
777
- subtype,
778
- backdrop,
779
- transferMap
780
- ) {
781
- const hasBackdrop = !!backdrop;
782
- const r0 = hasBackdrop ? backdrop[0] : 0;
783
- const g0 = hasBackdrop ? backdrop[1] : 0;
784
- const b0 = hasBackdrop ? backdrop[2] : 0;
785
-
786
- let composeFn;
787
- if (subtype === "Luminosity") {
788
- composeFn = composeSMaskLuminosity;
789
- } else {
790
- composeFn = composeSMaskAlpha;
791
- }
792
-
793
- // processing image in chunks to save memory
794
- const PIXELS_TO_PROCESS = 1048576;
795
- const chunkSize = Math.min(height, Math.ceil(PIXELS_TO_PROCESS / width));
796
- for (let row = 0; row < height; row += chunkSize) {
797
- const chunkHeight = Math.min(chunkSize, height - row);
798
- const maskData = maskCtx.getImageData(0, row, width, chunkHeight);
799
- const layerData = layerCtx.getImageData(0, row, width, chunkHeight);
800
-
801
- if (hasBackdrop) {
802
- composeSMaskBackdrop(maskData.data, r0, g0, b0);
803
- }
804
- composeFn(maskData.data, layerData.data, transferMap);
805
-
806
- maskCtx.putImageData(layerData, 0, row);
807
- }
808
- }
809
-
810
- function composeSMask(ctx, smask, layerCtx) {
811
- const mask = smask.canvas;
812
- const maskCtx = smask.context;
813
-
814
- ctx.setTransform(
815
- smask.scaleX,
816
- 0,
817
- 0,
818
- smask.scaleY,
819
- smask.offsetX,
820
- smask.offsetY
821
- );
822
-
823
- genericComposeSMask(
824
- maskCtx,
825
- layerCtx,
826
- mask.width,
827
- mask.height,
828
- smask.subtype,
829
- smask.backdrop,
830
- smask.transferMap
831
- );
832
- ctx.drawImage(mask, 0, 0);
833
- }
834
-
835
- const LINE_CAP_STYLES = ["butt", "round", "square"];
836
- const LINE_JOIN_STYLES = ["miter", "round", "bevel"];
837
- const NORMAL_CLIP = {};
838
- const EO_CLIP = {};
839
-
840
- // eslint-disable-next-line no-shadow
841
- class CanvasGraphics {
842
- constructor(
843
- canvasCtx,
844
- commonObjs,
845
- objs,
846
- canvasFactory,
847
- imageLayer,
848
- optionalContentConfig
849
- ) {
850
- this.ctx = canvasCtx;
851
- this.current = new CanvasExtraState();
852
- this.stateStack = [];
853
- this.pendingClip = null;
854
- this.pendingEOFill = false;
855
- this.res = null;
856
- this.xobjs = null;
857
- this.commonObjs = commonObjs;
858
- this.objs = objs;
859
- this.canvasFactory = canvasFactory;
860
- this.imageLayer = imageLayer;
861
- this.groupStack = [];
862
- this.processingType3 = null;
863
- // Patterns are painted relative to the initial page/form transform, see
864
- // PDF spec 8.7.2 NOTE 1.
865
- this.baseTransform = null;
866
- this.baseTransformStack = [];
867
- this.groupLevel = 0;
868
- this.smaskStack = [];
869
- this.smaskCounter = 0;
870
- this.tempSMask = null;
871
- this.contentVisible = true;
872
- this.markedContentStack = [];
873
- this.optionalContentConfig = optionalContentConfig;
874
- this.cachedCanvases = new CachedCanvases(this.canvasFactory);
875
- this.cachedPatterns = new Map();
876
- if (canvasCtx) {
877
- // NOTE: if mozCurrentTransform is polyfilled, then the current state of
878
- // the transformation must already be set in canvasCtx._transformMatrix.
879
- addContextCurrentTransform(canvasCtx);
880
- }
881
- this._cachedGetSinglePixelWidth = null;
882
- }
883
-
884
- beginDrawing({
885
- transform,
886
- viewport,
887
- transparency = false,
888
- background = null,
889
- }) {
890
- // For pdfs that use blend modes we have to clear the canvas else certain
891
- // blend modes can look wrong since we'd be blending with a white
892
- // backdrop. The problem with a transparent backdrop though is we then
893
- // don't get sub pixel anti aliasing on text, creating temporary
894
- // transparent canvas when we have blend modes.
895
- const width = this.ctx.canvas.width;
896
- const height = this.ctx.canvas.height;
897
-
898
- this.ctx.save();
899
- this.ctx.fillStyle = background || "rgb(255, 255, 255)";
900
- this.ctx.fillRect(0, 0, width, height);
901
- this.ctx.restore();
902
-
903
- if (transparency) {
904
- const transparentCanvas = this.cachedCanvases.getCanvas(
905
- "transparent",
906
- width,
907
- height,
908
- true
909
- );
910
- this.compositeCtx = this.ctx;
911
- this.transparentCanvas = transparentCanvas.canvas;
912
- this.ctx = transparentCanvas.context;
913
- this.ctx.save();
914
- // The transform can be applied before rendering, transferring it to
915
- // the new canvas.
916
- this.ctx.transform.apply(
917
- this.ctx,
918
- this.compositeCtx.mozCurrentTransform
919
- );
920
- }
921
-
922
- this.ctx.save();
923
- resetCtxToDefault(this.ctx);
924
- if (transform) {
925
- this.ctx.transform.apply(this.ctx, transform);
926
- }
927
- this.ctx.transform.apply(this.ctx, viewport.transform);
928
-
929
- this.baseTransform = this.ctx.mozCurrentTransform.slice();
930
- this._combinedScaleFactor = Math.hypot(
931
- this.baseTransform[0],
932
- this.baseTransform[2]
933
- );
934
-
935
- if (this.imageLayer) {
936
- this.imageLayer.beginLayout();
937
- }
938
- }
939
-
940
- executeOperatorList(
941
- operatorList,
942
- executionStartIdx,
943
- continueCallback,
944
- stepper
945
- ) {
946
- const argsArray = operatorList.argsArray;
947
- const fnArray = operatorList.fnArray;
948
- let i = executionStartIdx || 0;
949
- const argsArrayLen = argsArray.length;
950
-
951
- // Sometimes the OperatorList to execute is empty.
952
- if (argsArrayLen === i) {
953
- return i;
954
- }
955
-
956
- const chunkOperations =
957
- argsArrayLen - i > EXECUTION_STEPS &&
958
- typeof continueCallback === "function";
959
- const endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0;
960
- let steps = 0;
961
-
962
- const commonObjs = this.commonObjs;
963
- const objs = this.objs;
964
- let fnId;
965
-
966
- while (true) {
967
- if (stepper !== undefined && i === stepper.nextBreakPoint) {
968
- stepper.breakIt(i, continueCallback);
969
- return i;
970
- }
971
-
972
- fnId = fnArray[i];
973
-
974
- if (fnId !== OPS.dependency) {
975
- this[fnId].apply(this, argsArray[i]);
976
- } else {
977
- for (const depObjId of argsArray[i]) {
978
- const objsPool = depObjId.startsWith("g_") ? commonObjs : objs;
979
-
980
- // If the promise isn't resolved yet, add the continueCallback
981
- // to the promise and bail out.
982
- if (!objsPool.has(depObjId)) {
983
- objsPool.get(depObjId, continueCallback);
984
- return i;
985
- }
986
- }
987
- }
988
-
989
- i++;
990
-
991
- // If the entire operatorList was executed, stop as were done.
992
- if (i === argsArrayLen) {
993
- return i;
994
- }
995
-
996
- // If the execution took longer then a certain amount of time and
997
- // `continueCallback` is specified, interrupt the execution.
998
- if (chunkOperations && ++steps > EXECUTION_STEPS) {
999
- if (Date.now() > endTime) {
1000
- continueCallback();
1001
- return i;
1002
- }
1003
- steps = 0;
1004
- }
1005
-
1006
- // If the operatorList isn't executed completely yet OR the execution
1007
- // time was short enough, do another execution round.
1008
- }
1009
- }
1010
-
1011
- endDrawing() {
1012
- // Finishing all opened operations such as SMask group painting.
1013
- while (this.stateStack.length || this.current.activeSMask !== null) {
1014
- this.restore();
1015
- }
1016
-
1017
- this.ctx.restore();
1018
-
1019
- if (this.transparentCanvas) {
1020
- this.ctx = this.compositeCtx;
1021
- this.ctx.save();
1022
- this.ctx.setTransform(1, 0, 0, 1, 0, 0); // Avoid apply transform twice
1023
- this.ctx.drawImage(this.transparentCanvas, 0, 0);
1024
- this.ctx.restore();
1025
- this.transparentCanvas = null;
1026
- }
1027
-
1028
- this.cachedCanvases.clear();
1029
- this.cachedPatterns.clear();
1030
-
1031
- if (this.imageLayer) {
1032
- this.imageLayer.endLayout();
1033
- }
1034
- }
1035
-
1036
- _scaleImage(img, inverseTransform) {
1037
- // Vertical or horizontal scaling shall not be more than 2 to not lose the
1038
- // pixels during drawImage operation, painting on the temporary canvas(es)
1039
- // that are twice smaller in size.
1040
- const width = img.width;
1041
- const height = img.height;
1042
- let widthScale = Math.max(
1043
- Math.hypot(inverseTransform[0], inverseTransform[1]),
1044
- 1
1045
- );
1046
- let heightScale = Math.max(
1047
- Math.hypot(inverseTransform[2], inverseTransform[3]),
1048
- 1
1049
- );
1050
-
1051
- let paintWidth = width,
1052
- paintHeight = height;
1053
- let tmpCanvasId = "prescale1";
1054
- let tmpCanvas, tmpCtx;
1055
- while (
1056
- (widthScale > 2 && paintWidth > 1) ||
1057
- (heightScale > 2 && paintHeight > 1)
1058
- ) {
1059
- let newWidth = paintWidth,
1060
- newHeight = paintHeight;
1061
- if (widthScale > 2 && paintWidth > 1) {
1062
- newWidth = Math.ceil(paintWidth / 2);
1063
- widthScale /= paintWidth / newWidth;
1064
- }
1065
- if (heightScale > 2 && paintHeight > 1) {
1066
- newHeight = Math.ceil(paintHeight / 2);
1067
- heightScale /= paintHeight / newHeight;
1068
- }
1069
- tmpCanvas = this.cachedCanvases.getCanvas(
1070
- tmpCanvasId,
1071
- newWidth,
1072
- newHeight
1073
- );
1074
- tmpCtx = tmpCanvas.context;
1075
- tmpCtx.clearRect(0, 0, newWidth, newHeight);
1076
- tmpCtx.drawImage(
1077
- img,
1078
- 0,
1079
- 0,
1080
- paintWidth,
1081
- paintHeight,
1082
- 0,
1083
- 0,
1084
- newWidth,
1085
- newHeight
1086
- );
1087
- img = tmpCanvas.canvas;
1088
- paintWidth = newWidth;
1089
- paintHeight = newHeight;
1090
- tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1";
1091
- }
1092
- return {
1093
- img,
1094
- paintWidth,
1095
- paintHeight,
1096
- };
1097
- }
1098
-
1099
- _createMaskCanvas(img) {
1100
- const ctx = this.ctx;
1101
- const width = img.width,
1102
- height = img.height;
1103
- const fillColor = this.current.fillColor;
1104
- const isPatternFill = this.current.patternFill;
1105
- const maskCanvas = this.cachedCanvases.getCanvas(
1106
- "maskCanvas",
1107
- width,
1108
- height
1109
- );
1110
- const maskCtx = maskCanvas.context;
1111
- putBinaryImageMask(maskCtx, img);
1112
-
1113
- // Create the mask canvas at the size it will be drawn at and also set
1114
- // its transform to match the current transform so if there are any
1115
- // patterns applied they will be applied relative to the correct
1116
- // transform.
1117
- const objToCanvas = ctx.mozCurrentTransform;
1118
- let maskToCanvas = Util.transform(objToCanvas, [
1119
- 1 / width,
1120
- 0,
1121
- 0,
1122
- -1 / height,
1123
- 0,
1124
- 0,
1125
- ]);
1126
- maskToCanvas = Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]);
1127
- const cord1 = Util.applyTransform([0, 0], maskToCanvas);
1128
- const cord2 = Util.applyTransform([width, height], maskToCanvas);
1129
- const rect = Util.normalizeRect([cord1[0], cord1[1], cord2[0], cord2[1]]);
1130
- const drawnWidth = Math.ceil(rect[2] - rect[0]);
1131
- const drawnHeight = Math.ceil(rect[3] - rect[1]);
1132
- const fillCanvas = this.cachedCanvases.getCanvas(
1133
- "fillCanvas",
1134
- drawnWidth,
1135
- drawnHeight,
1136
- true
1137
- );
1138
- const fillCtx = fillCanvas.context;
1139
- // The offset will be the top-left cordinate mask.
1140
- const offsetX = Math.min(cord1[0], cord2[0]);
1141
- const offsetY = Math.min(cord1[1], cord2[1]);
1142
- fillCtx.translate(-offsetX, -offsetY);
1143
- fillCtx.transform.apply(fillCtx, maskToCanvas);
1144
- // Pre-scale if needed to improve image smoothing.
1145
- const scaled = this._scaleImage(
1146
- maskCanvas.canvas,
1147
- fillCtx.mozCurrentTransformInverse
1148
- );
1149
- fillCtx.drawImage(
1150
- scaled.img,
1151
- 0,
1152
- 0,
1153
- scaled.img.width,
1154
- scaled.img.height,
1155
- 0,
1156
- 0,
1157
- width,
1158
- height
1159
- );
1160
- fillCtx.globalCompositeOperation = "source-in";
1161
-
1162
- const inverse = Util.transform(fillCtx.mozCurrentTransformInverse, [
1163
- 1,
1164
- 0,
1165
- 0,
1166
- 1,
1167
- -offsetX,
1168
- -offsetY,
1169
- ]);
1170
- fillCtx.fillStyle = isPatternFill
1171
- ? fillColor.getPattern(ctx, this, inverse, false)
1172
- : fillColor;
1173
-
1174
- fillCtx.fillRect(0, 0, width, height);
1175
-
1176
- // Round the offsets to avoid drawing fractional pixels.
1177
- return {
1178
- canvas: fillCanvas.canvas,
1179
- offsetX: Math.round(offsetX),
1180
- offsetY: Math.round(offsetY),
1181
- };
1182
- }
1183
-
1184
- // Graphics state
1185
- setLineWidth(width) {
1186
- this.current.lineWidth = width;
1187
- this.ctx.lineWidth = width;
1188
- }
1189
-
1190
- setLineCap(style) {
1191
- this.ctx.lineCap = LINE_CAP_STYLES[style];
1192
- }
1193
-
1194
- setLineJoin(style) {
1195
- this.ctx.lineJoin = LINE_JOIN_STYLES[style];
1196
- }
1197
-
1198
- setMiterLimit(limit) {
1199
- this.ctx.miterLimit = limit;
1200
- }
1201
-
1202
- setDash(dashArray, dashPhase) {
1203
- const ctx = this.ctx;
1204
- if (ctx.setLineDash !== undefined) {
1205
- ctx.setLineDash(dashArray);
1206
- ctx.lineDashOffset = dashPhase;
1207
- }
1208
- }
1209
-
1210
- setRenderingIntent(intent) {
1211
- // This operation is ignored since we haven't found a use case for it yet.
1212
- }
1213
-
1214
- setFlatness(flatness) {
1215
- // This operation is ignored since we haven't found a use case for it yet.
1216
- }
1217
-
1218
- setGState(states) {
1219
- for (let i = 0, ii = states.length; i < ii; i++) {
1220
- const state = states[i];
1221
- const key = state[0];
1222
- const value = state[1];
1223
-
1224
- switch (key) {
1225
- case "LW":
1226
- this.setLineWidth(value);
1227
- break;
1228
- case "LC":
1229
- this.setLineCap(value);
1230
- break;
1231
- case "LJ":
1232
- this.setLineJoin(value);
1233
- break;
1234
- case "ML":
1235
- this.setMiterLimit(value);
1236
- break;
1237
- case "D":
1238
- this.setDash(value[0], value[1]);
1239
- break;
1240
- case "RI":
1241
- this.setRenderingIntent(value);
1242
- break;
1243
- case "FL":
1244
- this.setFlatness(value);
1245
- break;
1246
- case "Font":
1247
- this.setFont(value[0], value[1]);
1248
- break;
1249
- case "CA":
1250
- this.current.strokeAlpha = state[1];
1251
- break;
1252
- case "ca":
1253
- this.current.fillAlpha = state[1];
1254
- this.ctx.globalAlpha = state[1];
1255
- break;
1256
- case "BM":
1257
- this.ctx.globalCompositeOperation = value;
1258
- break;
1259
- case "SMask":
1260
- if (this.current.activeSMask) {
1261
- // If SMask is currrenly used, it needs to be suspended or
1262
- // finished. Suspend only makes sense when at least one save()
1263
- // was performed and state needs to be reverted on restore().
1264
- if (
1265
- this.stateStack.length > 0 &&
1266
- this.stateStack[this.stateStack.length - 1].activeSMask ===
1267
- this.current.activeSMask
1268
- ) {
1269
- this.suspendSMaskGroup();
1270
- } else {
1271
- this.endSMaskGroup();
1272
- }
1273
- }
1274
- this.current.activeSMask = value ? this.tempSMask : null;
1275
- if (this.current.activeSMask) {
1276
- this.beginSMaskGroup();
1277
- }
1278
- this.tempSMask = null;
1279
- break;
1280
- case "TR":
1281
- this.current.transferMaps = value;
1282
- }
1283
- }
1284
- }
1285
-
1286
- beginSMaskGroup() {
1287
- const activeSMask = this.current.activeSMask;
1288
- const drawnWidth = activeSMask.canvas.width;
1289
- const drawnHeight = activeSMask.canvas.height;
1290
- const cacheId = "smaskGroupAt" + this.groupLevel;
1291
- const scratchCanvas = this.cachedCanvases.getCanvas(
1292
- cacheId,
1293
- drawnWidth,
1294
- drawnHeight,
1295
- true
1296
- );
1297
-
1298
- const currentCtx = this.ctx;
1299
- const currentTransform = currentCtx.mozCurrentTransform;
1300
- this.ctx.save();
1301
-
1302
- const groupCtx = scratchCanvas.context;
1303
- groupCtx.scale(1 / activeSMask.scaleX, 1 / activeSMask.scaleY);
1304
- groupCtx.translate(-activeSMask.offsetX, -activeSMask.offsetY);
1305
- groupCtx.transform.apply(groupCtx, currentTransform);
1306
-
1307
- activeSMask.startTransformInverse = groupCtx.mozCurrentTransformInverse;
1308
-
1309
- copyCtxState(currentCtx, groupCtx);
1310
- this.ctx = groupCtx;
1311
- this.setGState([
1312
- ["BM", "source-over"],
1313
- ["ca", 1],
1314
- ["CA", 1],
1315
- ]);
1316
- this.groupStack.push(currentCtx);
1317
- this.groupLevel++;
1318
- }
1319
-
1320
- suspendSMaskGroup() {
1321
- // Similar to endSMaskGroup, the intermediate canvas has to be composed
1322
- // and future ctx state restored.
1323
- const groupCtx = this.ctx;
1324
- this.groupLevel--;
1325
- this.ctx = this.groupStack.pop();
1326
-
1327
- composeSMask(this.ctx, this.current.activeSMask, groupCtx);
1328
- this.ctx.restore();
1329
- this.ctx.save(); // save is needed since SMask will be resumed.
1330
- copyCtxState(groupCtx, this.ctx);
1331
-
1332
- // Saving state for resuming.
1333
- this.current.resumeSMaskCtx = groupCtx;
1334
- // Transform was changed in the SMask canvas, reflecting this change on
1335
- // this.ctx.
1336
- const deltaTransform = Util.transform(
1337
- this.current.activeSMask.startTransformInverse,
1338
- groupCtx.mozCurrentTransform
1339
- );
1340
- this.ctx.transform.apply(this.ctx, deltaTransform);
1341
-
1342
- // SMask was composed, the results at the groupCtx can be cleared.
1343
- groupCtx.save();
1344
- groupCtx.setTransform(1, 0, 0, 1, 0, 0);
1345
- groupCtx.clearRect(0, 0, groupCtx.canvas.width, groupCtx.canvas.height);
1346
- groupCtx.restore();
1347
- }
1348
-
1349
- resumeSMaskGroup() {
1350
- // Resuming state saved by suspendSMaskGroup. We don't need to restore
1351
- // any groupCtx state since restore() command (the only caller) will do
1352
- // that for us. See also beginSMaskGroup.
1353
- const groupCtx = this.current.resumeSMaskCtx;
1354
- const currentCtx = this.ctx;
1355
- this.ctx = groupCtx;
1356
- this.groupStack.push(currentCtx);
1357
- this.groupLevel++;
1358
- }
1359
-
1360
- endSMaskGroup() {
1361
- const groupCtx = this.ctx;
1362
- this.groupLevel--;
1363
- this.ctx = this.groupStack.pop();
1364
-
1365
- composeSMask(this.ctx, this.current.activeSMask, groupCtx);
1366
- this.ctx.restore();
1367
- copyCtxState(groupCtx, this.ctx);
1368
- // Transform was changed in the SMask canvas, reflecting this change on
1369
- // this.ctx.
1370
- const deltaTransform = Util.transform(
1371
- this.current.activeSMask.startTransformInverse,
1372
- groupCtx.mozCurrentTransform
1373
- );
1374
- this.ctx.transform.apply(this.ctx, deltaTransform);
1375
- }
1376
-
1377
- save() {
1378
- this.ctx.save();
1379
- const old = this.current;
1380
- this.stateStack.push(old);
1381
- this.current = old.clone();
1382
- this.current.resumeSMaskCtx = null;
1383
- }
1384
-
1385
- restore() {
1386
- // SMask was suspended, we just need to resume it.
1387
- if (this.current.resumeSMaskCtx) {
1388
- this.resumeSMaskGroup();
1389
- }
1390
- // SMask has to be finished once there is no states that are using the
1391
- // same SMask.
1392
- if (
1393
- this.current.activeSMask !== null &&
1394
- (this.stateStack.length === 0 ||
1395
- this.stateStack[this.stateStack.length - 1].activeSMask !==
1396
- this.current.activeSMask)
1397
- ) {
1398
- this.endSMaskGroup();
1399
- }
1400
-
1401
- if (this.stateStack.length !== 0) {
1402
- this.current = this.stateStack.pop();
1403
- this.ctx.restore();
1404
-
1405
- // Ensure that the clipping path is reset (fixes issue6413.pdf).
1406
- this.pendingClip = null;
1407
-
1408
- this._cachedGetSinglePixelWidth = null;
1409
- } else {
1410
- // We've finished all the SMask groups, reflect that in our state.
1411
- this.current.activeSMask = null;
1412
- }
1413
- }
1414
-
1415
- transform(a, b, c, d, e, f) {
1416
- this.ctx.transform(a, b, c, d, e, f);
1417
-
1418
- this._cachedGetSinglePixelWidth = null;
1419
- }
1420
-
1421
- // Path
1422
- constructPath(ops, args) {
1423
- const ctx = this.ctx;
1424
- const current = this.current;
1425
- let x = current.x,
1426
- y = current.y;
1427
- for (let i = 0, j = 0, ii = ops.length; i < ii; i++) {
1428
- switch (ops[i] | 0) {
1429
- case OPS.rectangle:
1430
- x = args[j++];
1431
- y = args[j++];
1432
- const width = args[j++];
1433
- const height = args[j++];
1434
-
1435
- const xw = x + width;
1436
- const yh = y + height;
1437
- ctx.moveTo(x, y);
1438
- if (width === 0 || height === 0) {
1439
- ctx.lineTo(xw, yh);
1440
- } else {
1441
- ctx.lineTo(xw, y);
1442
- ctx.lineTo(xw, yh);
1443
- ctx.lineTo(x, yh);
1444
- }
1445
-
1446
- ctx.closePath();
1447
- break;
1448
- case OPS.moveTo:
1449
- x = args[j++];
1450
- y = args[j++];
1451
- ctx.moveTo(x, y);
1452
- break;
1453
- case OPS.lineTo:
1454
- x = args[j++];
1455
- y = args[j++];
1456
- ctx.lineTo(x, y);
1457
- break;
1458
- case OPS.curveTo:
1459
- x = args[j + 4];
1460
- y = args[j + 5];
1461
- ctx.bezierCurveTo(
1462
- args[j],
1463
- args[j + 1],
1464
- args[j + 2],
1465
- args[j + 3],
1466
- x,
1467
- y
1468
- );
1469
- j += 6;
1470
- break;
1471
- case OPS.curveTo2:
1472
- ctx.bezierCurveTo(
1473
- x,
1474
- y,
1475
- args[j],
1476
- args[j + 1],
1477
- args[j + 2],
1478
- args[j + 3]
1479
- );
1480
- x = args[j + 2];
1481
- y = args[j + 3];
1482
- j += 4;
1483
- break;
1484
- case OPS.curveTo3:
1485
- x = args[j + 2];
1486
- y = args[j + 3];
1487
- ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);
1488
- j += 4;
1489
- break;
1490
- case OPS.closePath:
1491
- ctx.closePath();
1492
- break;
1493
- }
1494
- }
1495
- current.setCurrentPoint(x, y);
1496
- }
1497
-
1498
- closePath() {
1499
- this.ctx.closePath();
1500
- }
1501
-
1502
- stroke(consumePath) {
1503
- consumePath = typeof consumePath !== "undefined" ? consumePath : true;
1504
- const ctx = this.ctx;
1505
- const strokeColor = this.current.strokeColor;
1506
- // For stroke we want to temporarily change the global alpha to the
1507
- // stroking alpha.
1508
- ctx.globalAlpha = this.current.strokeAlpha;
1509
- if (this.contentVisible) {
1510
- if (typeof strokeColor === "object" && strokeColor?.getPattern) {
1511
- const lineWidth = this.getSinglePixelWidth();
1512
- ctx.save();
1513
- ctx.strokeStyle = strokeColor.getPattern(
1514
- ctx,
1515
- this,
1516
- ctx.mozCurrentTransformInverse
1517
- );
1518
- // Prevent drawing too thin lines by enforcing a minimum line width.
1519
- ctx.lineWidth = Math.max(lineWidth, this.current.lineWidth);
1520
- ctx.stroke();
1521
- ctx.restore();
1522
- } else {
1523
- const lineWidth = this.getSinglePixelWidth();
1524
- if (lineWidth < 0 && -lineWidth >= this.current.lineWidth) {
1525
- // The current transform will transform a square pixel into a
1526
- // parallelogram where both heights are lower than 1 and not equal.
1527
- ctx.save();
1528
- ctx.resetTransform();
1529
- ctx.lineWidth = Math.round(this._combinedScaleFactor);
1530
- ctx.stroke();
1531
- ctx.restore();
1532
- } else {
1533
- // Prevent drawing too thin lines by enforcing a minimum line width.
1534
- ctx.lineWidth = Math.max(lineWidth, this.current.lineWidth);
1535
- ctx.stroke();
1536
- }
1537
- }
1538
- }
1539
- if (consumePath) {
1540
- this.consumePath();
1541
- }
1542
- // Restore the global alpha to the fill alpha
1543
- ctx.globalAlpha = this.current.fillAlpha;
1544
- }
1545
-
1546
- closeStroke() {
1547
- this.closePath();
1548
- this.stroke();
1549
- }
1550
-
1551
- fill(consumePath) {
1552
- consumePath = typeof consumePath !== "undefined" ? consumePath : true;
1553
- const ctx = this.ctx;
1554
- const fillColor = this.current.fillColor;
1555
- const isPatternFill = this.current.patternFill;
1556
- let needRestore = false;
1557
-
1558
- if (isPatternFill) {
1559
- ctx.save();
1560
- ctx.fillStyle = fillColor.getPattern(
1561
- ctx,
1562
- this,
1563
- ctx.mozCurrentTransformInverse
1564
- );
1565
- needRestore = true;
1566
- }
1567
-
1568
- if (this.contentVisible) {
1569
- if (this.pendingEOFill) {
1570
- ctx.fill("evenodd");
1571
- this.pendingEOFill = false;
1572
- } else {
1573
- ctx.fill();
1574
- }
1575
- }
1576
-
1577
- if (needRestore) {
1578
- ctx.restore();
1579
- }
1580
- if (consumePath) {
1581
- this.consumePath();
1582
- }
1583
- }
1584
-
1585
- eoFill() {
1586
- this.pendingEOFill = true;
1587
- this.fill();
1588
- }
1589
-
1590
- fillStroke() {
1591
- this.fill(false);
1592
- this.stroke(false);
1593
-
1594
- this.consumePath();
1595
- }
1596
-
1597
- eoFillStroke() {
1598
- this.pendingEOFill = true;
1599
- this.fillStroke();
1600
- }
1601
-
1602
- closeFillStroke() {
1603
- this.closePath();
1604
- this.fillStroke();
1605
- }
1606
-
1607
- closeEOFillStroke() {
1608
- this.pendingEOFill = true;
1609
- this.closePath();
1610
- this.fillStroke();
1611
- }
1612
-
1613
- endPath() {
1614
- this.consumePath();
1615
- }
1616
-
1617
- // Clipping
1618
- clip() {
1619
- this.pendingClip = NORMAL_CLIP;
1620
- }
1621
-
1622
- eoClip() {
1623
- this.pendingClip = EO_CLIP;
1624
- }
1625
-
1626
- // Text
1627
- beginText() {
1628
- this.current.textMatrix = IDENTITY_MATRIX;
1629
- this.current.textMatrixScale = 1;
1630
- this.current.x = this.current.lineX = 0;
1631
- this.current.y = this.current.lineY = 0;
1632
- }
1633
-
1634
- endText() {
1635
- const paths = this.pendingTextPaths;
1636
- const ctx = this.ctx;
1637
- if (paths === undefined) {
1638
- ctx.beginPath();
1639
- return;
1640
- }
1641
-
1642
- ctx.save();
1643
- ctx.beginPath();
1644
- for (let i = 0; i < paths.length; i++) {
1645
- const path = paths[i];
1646
- ctx.setTransform.apply(ctx, path.transform);
1647
- ctx.translate(path.x, path.y);
1648
- path.addToPath(ctx, path.fontSize);
1649
- }
1650
- ctx.restore();
1651
- ctx.clip();
1652
- ctx.beginPath();
1653
- delete this.pendingTextPaths;
1654
- }
1655
-
1656
- setCharSpacing(spacing) {
1657
- this.current.charSpacing = spacing;
1658
- }
1659
-
1660
- setWordSpacing(spacing) {
1661
- this.current.wordSpacing = spacing;
1662
- }
1663
-
1664
- setHScale(scale) {
1665
- this.current.textHScale = scale / 100;
1666
- }
1667
-
1668
- setLeading(leading) {
1669
- this.current.leading = -leading;
1670
- }
1671
-
1672
- setFont(fontRefName, size) {
1673
- const fontObj = this.commonObjs.get(fontRefName);
1674
- const current = this.current;
1675
-
1676
- if (!fontObj) {
1677
- throw new Error(`Can't find font for ${fontRefName}`);
1678
- }
1679
- current.fontMatrix = fontObj.fontMatrix || FONT_IDENTITY_MATRIX;
1680
-
1681
- // A valid matrix needs all main diagonal elements to be non-zero
1682
- // This also ensures we bypass FF bugzilla bug #719844.
1683
- if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) {
1684
- warn("Invalid font matrix for font " + fontRefName);
1685
- }
1686
-
1687
- // The spec for Tf (setFont) says that 'size' specifies the font 'scale',
1688
- // and in some docs this can be negative (inverted x-y axes).
1689
- if (size < 0) {
1690
- size = -size;
1691
- current.fontDirection = -1;
1692
- } else {
1693
- current.fontDirection = 1;
1694
- }
1695
-
1696
- this.current.font = fontObj;
1697
- this.current.fontSize = size;
1698
-
1699
- if (fontObj.isType3Font) {
1700
- return; // we don't need ctx.font for Type3 fonts
1701
- }
1702
-
1703
- const name = fontObj.loadedName || "sans-serif";
1704
-
1705
- let bold = "normal";
1706
- if (fontObj.black) {
1707
- bold = "900";
1708
- } else if (fontObj.bold) {
1709
- bold = "bold";
1710
- }
1711
- const italic = fontObj.italic ? "italic" : "normal";
1712
- const typeface = `"${name}", ${fontObj.fallbackName}`;
1713
-
1714
- // Some font backends cannot handle fonts below certain size.
1715
- // Keeping the font at minimal size and using the fontSizeScale to change
1716
- // the current transformation matrix before the fillText/strokeText.
1717
- // See https://bugzilla.mozilla.org/show_bug.cgi?id=726227
1718
- let browserFontSize = size;
1719
- if (size < MIN_FONT_SIZE) {
1720
- browserFontSize = MIN_FONT_SIZE;
1721
- } else if (size > MAX_FONT_SIZE) {
1722
- browserFontSize = MAX_FONT_SIZE;
1723
- }
1724
- this.current.fontSizeScale = size / browserFontSize;
1725
-
1726
- this.ctx.font = `${italic} ${bold} ${browserFontSize}px ${typeface}`;
1727
- }
1728
-
1729
- setTextRenderingMode(mode) {
1730
- this.current.textRenderingMode = mode;
1731
- }
1732
-
1733
- setTextRise(rise) {
1734
- this.current.textRise = rise;
1735
- }
1736
-
1737
- moveText(x, y) {
1738
- this.current.x = this.current.lineX += x;
1739
- this.current.y = this.current.lineY += y;
1740
- }
1741
-
1742
- setLeadingMoveText(x, y) {
1743
- this.setLeading(-y);
1744
- this.moveText(x, y);
1745
- }
1746
-
1747
- setTextMatrix(a, b, c, d, e, f) {
1748
- this.current.textMatrix = [a, b, c, d, e, f];
1749
- this.current.textMatrixScale = Math.hypot(a, b);
1750
-
1751
- this.current.x = this.current.lineX = 0;
1752
- this.current.y = this.current.lineY = 0;
1753
- }
1754
-
1755
- nextLine() {
1756
- this.moveText(0, this.current.leading);
1757
- }
1758
-
1759
- paintChar(character, x, y, patternTransform, resetLineWidthToOne) {
1760
- const ctx = this.ctx;
1761
- const current = this.current;
1762
- const font = current.font;
1763
- const textRenderingMode = current.textRenderingMode;
1764
- const fontSize = current.fontSize / current.fontSizeScale;
1765
- const fillStrokeMode =
1766
- textRenderingMode & TextRenderingMode.FILL_STROKE_MASK;
1767
- const isAddToPathSet = !!(
1768
- textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG
1769
- );
1770
- const patternFill = current.patternFill && !font.missingFile;
1771
-
1772
- let addToPath;
1773
- if (font.disableFontFace || isAddToPathSet || patternFill) {
1774
- addToPath = font.getPathGenerator(this.commonObjs, character);
1775
- }
1776
-
1777
- if (font.disableFontFace || patternFill) {
1778
- ctx.save();
1779
- ctx.translate(x, y);
1780
- ctx.beginPath();
1781
- addToPath(ctx, fontSize);
1782
- if (patternTransform) {
1783
- ctx.setTransform.apply(ctx, patternTransform);
1784
- }
1785
- if (
1786
- fillStrokeMode === TextRenderingMode.FILL ||
1787
- fillStrokeMode === TextRenderingMode.FILL_STROKE
1788
- ) {
1789
- ctx.fill();
1790
- }
1791
- if (
1792
- fillStrokeMode === TextRenderingMode.STROKE ||
1793
- fillStrokeMode === TextRenderingMode.FILL_STROKE
1794
- ) {
1795
- if (resetLineWidthToOne) {
1796
- ctx.resetTransform();
1797
- ctx.lineWidth = Math.round(this._combinedScaleFactor);
1798
- }
1799
- ctx.stroke();
1800
- }
1801
- ctx.restore();
1802
- } else {
1803
- if (
1804
- fillStrokeMode === TextRenderingMode.FILL ||
1805
- fillStrokeMode === TextRenderingMode.FILL_STROKE
1806
- ) {
1807
- ctx.fillText(character, x, y);
1808
- }
1809
- if (
1810
- fillStrokeMode === TextRenderingMode.STROKE ||
1811
- fillStrokeMode === TextRenderingMode.FILL_STROKE
1812
- ) {
1813
- if (resetLineWidthToOne) {
1814
- ctx.save();
1815
- ctx.moveTo(x, y);
1816
- ctx.resetTransform();
1817
- ctx.lineWidth = Math.round(this._combinedScaleFactor);
1818
- ctx.strokeText(character, 0, 0);
1819
- ctx.restore();
1820
- } else {
1821
- ctx.strokeText(character, x, y);
1822
- }
1823
- }
1824
- }
1825
-
1826
- if (isAddToPathSet) {
1827
- const paths = this.pendingTextPaths || (this.pendingTextPaths = []);
1828
- paths.push({
1829
- transform: ctx.mozCurrentTransform,
1830
- x,
1831
- y,
1832
- fontSize,
1833
- addToPath,
1834
- });
1835
- }
1836
- }
1837
-
1838
- get isFontSubpixelAAEnabled() {
1839
- // Checks if anti-aliasing is enabled when scaled text is painted.
1840
- // On Windows GDI scaled fonts looks bad.
1841
- const { context: ctx } = this.cachedCanvases.getCanvas(
1842
- "isFontSubpixelAAEnabled",
1843
- 10,
1844
- 10
1845
- );
1846
- ctx.scale(1.5, 1);
1847
- ctx.fillText("I", 0, 10);
1848
- const data = ctx.getImageData(0, 0, 10, 10).data;
1849
- let enabled = false;
1850
- for (let i = 3; i < data.length; i += 4) {
1851
- if (data[i] > 0 && data[i] < 255) {
1852
- enabled = true;
1853
- break;
1854
- }
1855
- }
1856
- return shadow(this, "isFontSubpixelAAEnabled", enabled);
1857
- }
1858
-
1859
- showText(glyphs) {
1860
- const current = this.current;
1861
- const font = current.font;
1862
- if (font.isType3Font) {
1863
- return this.showType3Text(glyphs);
1864
- }
1865
-
1866
- const fontSize = current.fontSize;
1867
- if (fontSize === 0) {
1868
- return undefined;
1869
- }
1870
-
1871
- const ctx = this.ctx;
1872
- const fontSizeScale = current.fontSizeScale;
1873
- const charSpacing = current.charSpacing;
1874
- const wordSpacing = current.wordSpacing;
1875
- const fontDirection = current.fontDirection;
1876
- const textHScale = current.textHScale * fontDirection;
1877
- const glyphsLength = glyphs.length;
1878
- const vertical = font.vertical;
1879
- const spacingDir = vertical ? 1 : -1;
1880
- const defaultVMetrics = font.defaultVMetrics;
1881
- const widthAdvanceScale = fontSize * current.fontMatrix[0];
1882
-
1883
- const simpleFillText =
1884
- current.textRenderingMode === TextRenderingMode.FILL &&
1885
- !font.disableFontFace &&
1886
- !current.patternFill;
1887
-
1888
- ctx.save();
1889
- let patternTransform;
1890
- if (current.patternFill) {
1891
- // TODO: Patterns are not applied correctly to text if a non-embedded
1892
- // font is used. E.g. issue 8111 and ShowText-ShadingPattern.pdf.
1893
- ctx.save();
1894
- const pattern = current.fillColor.getPattern(
1895
- ctx,
1896
- this,
1897
- ctx.mozCurrentTransformInverse
1898
- );
1899
- patternTransform = ctx.mozCurrentTransform;
1900
- ctx.restore();
1901
- ctx.fillStyle = pattern;
1902
- }
1903
- ctx.transform.apply(ctx, current.textMatrix);
1904
- ctx.translate(current.x, current.y + current.textRise);
1905
-
1906
- if (fontDirection > 0) {
1907
- ctx.scale(textHScale, -1);
1908
- } else {
1909
- ctx.scale(textHScale, 1);
1910
- }
1911
-
1912
- let lineWidth = current.lineWidth;
1913
- let resetLineWidthToOne = false;
1914
- const scale = current.textMatrixScale;
1915
- if (scale === 0 || lineWidth === 0) {
1916
- const fillStrokeMode =
1917
- current.textRenderingMode & TextRenderingMode.FILL_STROKE_MASK;
1918
- if (
1919
- fillStrokeMode === TextRenderingMode.STROKE ||
1920
- fillStrokeMode === TextRenderingMode.FILL_STROKE
1921
- ) {
1922
- this._cachedGetSinglePixelWidth = null;
1923
- lineWidth = this.getSinglePixelWidth();
1924
- resetLineWidthToOne = lineWidth < 0;
1925
- }
1926
- } else {
1927
- lineWidth /= scale;
1928
- }
1929
-
1930
- if (fontSizeScale !== 1.0) {
1931
- ctx.scale(fontSizeScale, fontSizeScale);
1932
- lineWidth /= fontSizeScale;
1933
- }
1934
-
1935
- ctx.lineWidth = lineWidth;
1936
-
1937
- let x = 0,
1938
- i;
1939
- for (i = 0; i < glyphsLength; ++i) {
1940
- const glyph = glyphs[i];
1941
- if (isNum(glyph)) {
1942
- x += (spacingDir * glyph * fontSize) / 1000;
1943
- continue;
1944
- }
1945
-
1946
- let restoreNeeded = false;
1947
- const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
1948
- const character = glyph.fontChar;
1949
- const accent = glyph.accent;
1950
- let scaledX, scaledY;
1951
- let width = glyph.width;
1952
- if (vertical) {
1953
- const vmetric = glyph.vmetric || defaultVMetrics;
1954
- const vx =
1955
- -(glyph.vmetric ? vmetric[1] : width * 0.5) * widthAdvanceScale;
1956
- const vy = vmetric[2] * widthAdvanceScale;
1957
-
1958
- width = vmetric ? -vmetric[0] : width;
1959
- scaledX = vx / fontSizeScale;
1960
- scaledY = (x + vy) / fontSizeScale;
1961
- } else {
1962
- scaledX = x / fontSizeScale;
1963
- scaledY = 0;
1964
- }
1965
-
1966
- if (font.remeasure && width > 0) {
1967
- // Some standard fonts may not have the exact width: rescale per
1968
- // character if measured width is greater than expected glyph width
1969
- // and subpixel-aa is enabled, otherwise just center the glyph.
1970
- const measuredWidth =
1971
- ((ctx.measureText(character).width * 1000) / fontSize) *
1972
- fontSizeScale;
1973
- if (width < measuredWidth && this.isFontSubpixelAAEnabled) {
1974
- const characterScaleX = width / measuredWidth;
1975
- restoreNeeded = true;
1976
- ctx.save();
1977
- ctx.scale(characterScaleX, 1);
1978
- scaledX /= characterScaleX;
1979
- } else if (width !== measuredWidth) {
1980
- scaledX +=
1981
- (((width - measuredWidth) / 2000) * fontSize) / fontSizeScale;
1982
- }
1983
- }
1984
-
1985
- // Only attempt to draw the glyph if it is actually in the embedded font
1986
- // file or if there isn't a font file so the fallback font is shown.
1987
- if (this.contentVisible && (glyph.isInFont || font.missingFile)) {
1988
- if (simpleFillText && !accent) {
1989
- // common case
1990
- ctx.fillText(character, scaledX, scaledY);
1991
- } else {
1992
- this.paintChar(
1993
- character,
1994
- scaledX,
1995
- scaledY,
1996
- patternTransform,
1997
- resetLineWidthToOne
1998
- );
1999
- if (accent) {
2000
- const scaledAccentX =
2001
- scaledX + (fontSize * accent.offset.x) / fontSizeScale;
2002
- const scaledAccentY =
2003
- scaledY - (fontSize * accent.offset.y) / fontSizeScale;
2004
- this.paintChar(
2005
- accent.fontChar,
2006
- scaledAccentX,
2007
- scaledAccentY,
2008
- patternTransform,
2009
- resetLineWidthToOne
2010
- );
2011
- }
2012
- }
2013
- }
2014
-
2015
- let charWidth;
2016
- if (vertical) {
2017
- charWidth = width * widthAdvanceScale - spacing * fontDirection;
2018
- } else {
2019
- charWidth = width * widthAdvanceScale + spacing * fontDirection;
2020
- }
2021
- x += charWidth;
2022
-
2023
- if (restoreNeeded) {
2024
- ctx.restore();
2025
- }
2026
- }
2027
- if (vertical) {
2028
- current.y -= x;
2029
- } else {
2030
- current.x += x * textHScale;
2031
- }
2032
- ctx.restore();
2033
- return undefined;
2034
- }
2035
-
2036
- showType3Text(glyphs) {
2037
- // Type3 fonts - each glyph is a "mini-PDF"
2038
- const ctx = this.ctx;
2039
- const current = this.current;
2040
- const font = current.font;
2041
- const fontSize = current.fontSize;
2042
- const fontDirection = current.fontDirection;
2043
- const spacingDir = font.vertical ? 1 : -1;
2044
- const charSpacing = current.charSpacing;
2045
- const wordSpacing = current.wordSpacing;
2046
- const textHScale = current.textHScale * fontDirection;
2047
- const fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX;
2048
- const glyphsLength = glyphs.length;
2049
- const isTextInvisible =
2050
- current.textRenderingMode === TextRenderingMode.INVISIBLE;
2051
- let i, glyph, width, spacingLength;
2052
-
2053
- if (isTextInvisible || fontSize === 0) {
2054
- return;
2055
- }
2056
- this._cachedGetSinglePixelWidth = null;
2057
-
2058
- ctx.save();
2059
- ctx.transform.apply(ctx, current.textMatrix);
2060
- ctx.translate(current.x, current.y);
2061
-
2062
- ctx.scale(textHScale, fontDirection);
2063
-
2064
- for (i = 0; i < glyphsLength; ++i) {
2065
- glyph = glyphs[i];
2066
- if (isNum(glyph)) {
2067
- spacingLength = (spacingDir * glyph * fontSize) / 1000;
2068
- this.ctx.translate(spacingLength, 0);
2069
- current.x += spacingLength * textHScale;
2070
- continue;
2071
- }
2072
-
2073
- const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
2074
- const operatorList = font.charProcOperatorList[glyph.operatorListId];
2075
- if (!operatorList) {
2076
- warn(`Type3 character "${glyph.operatorListId}" is not available.`);
2077
- continue;
2078
- }
2079
- if (this.contentVisible) {
2080
- this.processingType3 = glyph;
2081
- this.save();
2082
- ctx.scale(fontSize, fontSize);
2083
- ctx.transform.apply(ctx, fontMatrix);
2084
- this.executeOperatorList(operatorList);
2085
- this.restore();
2086
- }
2087
-
2088
- const transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
2089
- width = transformed[0] * fontSize + spacing;
2090
-
2091
- ctx.translate(width, 0);
2092
- current.x += width * textHScale;
2093
- }
2094
- ctx.restore();
2095
- this.processingType3 = null;
2096
- }
2097
-
2098
- // Type3 fonts
2099
- setCharWidth(xWidth, yWidth) {
2100
- // We can safely ignore this since the width should be the same
2101
- // as the width in the Widths array.
2102
- }
2103
-
2104
- setCharWidthAndBounds(xWidth, yWidth, llx, lly, urx, ury) {
2105
- // TODO According to the spec we're also suppose to ignore any operators
2106
- // that set color or include images while processing this type3 font.
2107
- this.ctx.rect(llx, lly, urx - llx, ury - lly);
2108
- this.clip();
2109
- this.endPath();
2110
- }
2111
-
2112
- // Color
2113
- getColorN_Pattern(IR) {
2114
- let pattern;
2115
- if (IR[0] === "TilingPattern") {
2116
- const color = IR[1];
2117
- const baseTransform =
2118
- this.baseTransform || this.ctx.mozCurrentTransform.slice();
2119
- const canvasGraphicsFactory = {
2120
- createCanvasGraphics: ctx => {
2121
- return new CanvasGraphics(
2122
- ctx,
2123
- this.commonObjs,
2124
- this.objs,
2125
- this.canvasFactory
2126
- );
2127
- },
2128
- };
2129
- pattern = new TilingPattern(
2130
- IR,
2131
- color,
2132
- this.ctx,
2133
- canvasGraphicsFactory,
2134
- baseTransform
2135
- );
2136
- } else {
2137
- pattern = this._getPattern(IR[1]);
2138
- }
2139
- return pattern;
2140
- }
2141
-
2142
- setStrokeColorN() {
2143
- this.current.strokeColor = this.getColorN_Pattern(arguments);
2144
- }
2145
-
2146
- setFillColorN() {
2147
- this.current.fillColor = this.getColorN_Pattern(arguments);
2148
- this.current.patternFill = true;
2149
- }
2150
-
2151
- setStrokeRGBColor(r, g, b) {
2152
- const color = Util.makeHexColor(r, g, b);
2153
- this.ctx.strokeStyle = color;
2154
- this.current.strokeColor = color;
2155
- }
2156
-
2157
- setFillRGBColor(r, g, b) {
2158
- const color = Util.makeHexColor(r, g, b);
2159
- this.ctx.fillStyle = color;
2160
- this.current.fillColor = color;
2161
- this.current.patternFill = false;
2162
- }
2163
-
2164
- _getPattern(objId) {
2165
- if (this.cachedPatterns.has(objId)) {
2166
- return this.cachedPatterns.get(objId);
2167
- }
2168
- const pattern = getShadingPattern(this.objs.get(objId));
2169
- this.cachedPatterns.set(objId, pattern);
2170
- return pattern;
2171
- }
2172
-
2173
- shadingFill(objId) {
2174
- if (!this.contentVisible) {
2175
- return;
2176
- }
2177
- const ctx = this.ctx;
2178
-
2179
- this.save();
2180
- const pattern = this._getPattern(objId);
2181
- ctx.fillStyle = pattern.getPattern(
2182
- ctx,
2183
- this,
2184
- ctx.mozCurrentTransformInverse,
2185
- true
2186
- );
2187
-
2188
- const inv = ctx.mozCurrentTransformInverse;
2189
- if (inv) {
2190
- const canvas = ctx.canvas;
2191
- const width = canvas.width;
2192
- const height = canvas.height;
2193
-
2194
- const bl = Util.applyTransform([0, 0], inv);
2195
- const br = Util.applyTransform([0, height], inv);
2196
- const ul = Util.applyTransform([width, 0], inv);
2197
- const ur = Util.applyTransform([width, height], inv);
2198
-
2199
- const x0 = Math.min(bl[0], br[0], ul[0], ur[0]);
2200
- const y0 = Math.min(bl[1], br[1], ul[1], ur[1]);
2201
- const x1 = Math.max(bl[0], br[0], ul[0], ur[0]);
2202
- const y1 = Math.max(bl[1], br[1], ul[1], ur[1]);
2203
-
2204
- this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
2205
- } else {
2206
- // HACK to draw the gradient onto an infinite rectangle.
2207
- // PDF gradients are drawn across the entire image while
2208
- // Canvas only allows gradients to be drawn in a rectangle
2209
- // The following bug should allow us to remove this.
2210
- // https://bugzilla.mozilla.org/show_bug.cgi?id=664884
2211
-
2212
- this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
2213
- }
2214
-
2215
- this.restore();
2216
- }
2217
-
2218
- // Images
2219
- beginInlineImage() {
2220
- unreachable("Should not call beginInlineImage");
2221
- }
2222
-
2223
- beginImageData() {
2224
- unreachable("Should not call beginImageData");
2225
- }
2226
-
2227
- paintFormXObjectBegin(matrix, bbox) {
2228
- if (!this.contentVisible) {
2229
- return;
2230
- }
2231
- this.save();
2232
- this.baseTransformStack.push(this.baseTransform);
2233
-
2234
- if (Array.isArray(matrix) && matrix.length === 6) {
2235
- this.transform.apply(this, matrix);
2236
- }
2237
-
2238
- this.baseTransform = this.ctx.mozCurrentTransform;
2239
-
2240
- if (bbox) {
2241
- const width = bbox[2] - bbox[0];
2242
- const height = bbox[3] - bbox[1];
2243
- this.ctx.rect(bbox[0], bbox[1], width, height);
2244
- this.clip();
2245
- this.endPath();
2246
- }
2247
- }
2248
-
2249
- paintFormXObjectEnd() {
2250
- if (!this.contentVisible) {
2251
- return;
2252
- }
2253
- this.restore();
2254
- this.baseTransform = this.baseTransformStack.pop();
2255
- }
2256
-
2257
- beginGroup(group) {
2258
- if (!this.contentVisible) {
2259
- return;
2260
- }
2261
-
2262
- this.save();
2263
- const currentCtx = this.ctx;
2264
- // TODO non-isolated groups - according to Rik at adobe non-isolated
2265
- // group results aren't usually that different and they even have tools
2266
- // that ignore this setting. Notes from Rik on implementing:
2267
- // - When you encounter an transparency group, create a new canvas with
2268
- // the dimensions of the bbox
2269
- // - copy the content from the previous canvas to the new canvas
2270
- // - draw as usual
2271
- // - remove the backdrop alpha:
2272
- // alphaNew = 1 - (1 - alpha)/(1 - alphaBackdrop) with 'alpha' the alpha
2273
- // value of your transparency group and 'alphaBackdrop' the alpha of the
2274
- // backdrop
2275
- // - remove background color:
2276
- // colorNew = color - alphaNew *colorBackdrop /(1 - alphaNew)
2277
- if (!group.isolated) {
2278
- info("TODO: Support non-isolated groups.");
2279
- }
2280
-
2281
- // TODO knockout - supposedly possible with the clever use of compositing
2282
- // modes.
2283
- if (group.knockout) {
2284
- warn("Knockout groups not supported.");
2285
- }
2286
-
2287
- const currentTransform = currentCtx.mozCurrentTransform;
2288
- if (group.matrix) {
2289
- currentCtx.transform.apply(currentCtx, group.matrix);
2290
- }
2291
- if (!group.bbox) {
2292
- throw new Error("Bounding box is required.");
2293
- }
2294
-
2295
- // Based on the current transform figure out how big the bounding box
2296
- // will actually be.
2297
- let bounds = Util.getAxialAlignedBoundingBox(
2298
- group.bbox,
2299
- currentCtx.mozCurrentTransform
2300
- );
2301
- // Clip the bounding box to the current canvas.
2302
- const canvasBounds = [
2303
- 0,
2304
- 0,
2305
- currentCtx.canvas.width,
2306
- currentCtx.canvas.height,
2307
- ];
2308
- bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0];
2309
- // Use ceil in case we're between sizes so we don't create canvas that is
2310
- // too small and make the canvas at least 1x1 pixels.
2311
- const offsetX = Math.floor(bounds[0]);
2312
- const offsetY = Math.floor(bounds[1]);
2313
- let drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1);
2314
- let drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1);
2315
- let scaleX = 1,
2316
- scaleY = 1;
2317
- if (drawnWidth > MAX_GROUP_SIZE) {
2318
- scaleX = drawnWidth / MAX_GROUP_SIZE;
2319
- drawnWidth = MAX_GROUP_SIZE;
2320
- }
2321
- if (drawnHeight > MAX_GROUP_SIZE) {
2322
- scaleY = drawnHeight / MAX_GROUP_SIZE;
2323
- drawnHeight = MAX_GROUP_SIZE;
2324
- }
2325
-
2326
- let cacheId = "groupAt" + this.groupLevel;
2327
- if (group.smask) {
2328
- // Using two cache entries is case if masks are used one after another.
2329
- cacheId += "_smask_" + (this.smaskCounter++ % 2);
2330
- }
2331
- const scratchCanvas = this.cachedCanvases.getCanvas(
2332
- cacheId,
2333
- drawnWidth,
2334
- drawnHeight,
2335
- true
2336
- );
2337
- const groupCtx = scratchCanvas.context;
2338
-
2339
- // Since we created a new canvas that is just the size of the bounding box
2340
- // we have to translate the group ctx.
2341
- groupCtx.scale(1 / scaleX, 1 / scaleY);
2342
- groupCtx.translate(-offsetX, -offsetY);
2343
- groupCtx.transform.apply(groupCtx, currentTransform);
2344
-
2345
- if (group.smask) {
2346
- // Saving state and cached mask to be used in setGState.
2347
- this.smaskStack.push({
2348
- canvas: scratchCanvas.canvas,
2349
- context: groupCtx,
2350
- offsetX,
2351
- offsetY,
2352
- scaleX,
2353
- scaleY,
2354
- subtype: group.smask.subtype,
2355
- backdrop: group.smask.backdrop,
2356
- transferMap: group.smask.transferMap || null,
2357
- startTransformInverse: null, // used during suspend operation
2358
- });
2359
- } else {
2360
- // Setup the current ctx so when the group is popped we draw it at the
2361
- // right location.
2362
- currentCtx.setTransform(1, 0, 0, 1, 0, 0);
2363
- currentCtx.translate(offsetX, offsetY);
2364
- currentCtx.scale(scaleX, scaleY);
2365
- }
2366
- // The transparency group inherits all off the current graphics state
2367
- // except the blend mode, soft mask, and alpha constants.
2368
- copyCtxState(currentCtx, groupCtx);
2369
- this.ctx = groupCtx;
2370
- this.setGState([
2371
- ["BM", "source-over"],
2372
- ["ca", 1],
2373
- ["CA", 1],
2374
- ]);
2375
- this.groupStack.push(currentCtx);
2376
- this.groupLevel++;
2377
-
2378
- // Resetting mask state, masks will be applied on restore of the group.
2379
- this.current.activeSMask = null;
2380
- }
2381
-
2382
- endGroup(group) {
2383
- if (!this.contentVisible) {
2384
- return;
2385
- }
2386
- this.groupLevel--;
2387
- const groupCtx = this.ctx;
2388
- this.ctx = this.groupStack.pop();
2389
- // Turn off image smoothing to avoid sub pixel interpolation which can
2390
- // look kind of blurry for some pdfs.
2391
- if (this.ctx.imageSmoothingEnabled !== undefined) {
2392
- this.ctx.imageSmoothingEnabled = false;
2393
- } else {
2394
- this.ctx.mozImageSmoothingEnabled = false;
2395
- }
2396
- if (group.smask) {
2397
- this.tempSMask = this.smaskStack.pop();
2398
- } else {
2399
- this.ctx.drawImage(groupCtx.canvas, 0, 0);
2400
- }
2401
- this.restore();
2402
- }
2403
-
2404
- beginAnnotations() {
2405
- this.save();
2406
- if (this.baseTransform) {
2407
- this.ctx.setTransform.apply(this.ctx, this.baseTransform);
2408
- }
2409
- }
2410
-
2411
- endAnnotations() {
2412
- this.restore();
2413
- }
2414
-
2415
- beginAnnotation(id, rect, transform, matrix) {
2416
- this.save();
2417
- resetCtxToDefault(this.ctx);
2418
- this.current = new CanvasExtraState();
2419
-
2420
- if (Array.isArray(rect) && rect.length === 4) {
2421
- const width = rect[2] - rect[0];
2422
- const height = rect[3] - rect[1];
2423
- this.ctx.rect(rect[0], rect[1], width, height);
2424
- this.clip();
2425
- this.endPath();
2426
- }
2427
-
2428
- this.transform.apply(this, transform);
2429
- this.transform.apply(this, matrix);
2430
- }
2431
-
2432
- endAnnotation() {
2433
- this.restore();
2434
- }
2435
-
2436
- paintImageMaskXObject(img) {
2437
- if (!this.contentVisible) {
2438
- return;
2439
- }
2440
- const ctx = this.ctx;
2441
- const width = img.width,
2442
- height = img.height;
2443
-
2444
- const glyph = this.processingType3;
2445
-
2446
- if (COMPILE_TYPE3_GLYPHS && glyph && glyph.compiled === undefined) {
2447
- if (width <= MAX_SIZE_TO_COMPILE && height <= MAX_SIZE_TO_COMPILE) {
2448
- glyph.compiled = compileType3Glyph({ data: img.data, width, height });
2449
- } else {
2450
- glyph.compiled = null;
2451
- }
2452
- }
2453
-
2454
- if (glyph?.compiled) {
2455
- glyph.compiled(ctx);
2456
- return;
2457
- }
2458
- const mask = this._createMaskCanvas(img);
2459
- const maskCanvas = mask.canvas;
2460
-
2461
- ctx.save();
2462
- // The mask is drawn with the transform applied. Reset the current
2463
- // transform to draw to the identity.
2464
- ctx.setTransform(1, 0, 0, 1, 0, 0);
2465
- ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY);
2466
- ctx.restore();
2467
- }
2468
-
2469
- paintImageMaskXObjectRepeat(
2470
- imgData,
2471
- scaleX,
2472
- skewX = 0,
2473
- skewY = 0,
2474
- scaleY,
2475
- positions
2476
- ) {
2477
- if (!this.contentVisible) {
2478
- return;
2479
- }
2480
- const ctx = this.ctx;
2481
- ctx.save();
2482
- const currentTransform = ctx.mozCurrentTransform;
2483
- ctx.transform(scaleX, skewX, skewY, scaleY, 0, 0);
2484
- const mask = this._createMaskCanvas(imgData);
2485
-
2486
- ctx.setTransform(1, 0, 0, 1, 0, 0);
2487
- for (let i = 0, ii = positions.length; i < ii; i += 2) {
2488
- const trans = Util.transform(currentTransform, [
2489
- scaleX,
2490
- skewX,
2491
- skewY,
2492
- scaleY,
2493
- positions[i],
2494
- positions[i + 1],
2495
- ]);
2496
-
2497
- const [x, y] = Util.applyTransform([0, 0], trans);
2498
- ctx.drawImage(mask.canvas, x, y);
2499
- }
2500
- ctx.restore();
2501
- }
2502
-
2503
- paintImageMaskXObjectGroup(images) {
2504
- if (!this.contentVisible) {
2505
- return;
2506
- }
2507
- const ctx = this.ctx;
2508
-
2509
- const fillColor = this.current.fillColor;
2510
- const isPatternFill = this.current.patternFill;
2511
- for (let i = 0, ii = images.length; i < ii; i++) {
2512
- const image = images[i];
2513
- const width = image.width,
2514
- height = image.height;
2515
-
2516
- const maskCanvas = this.cachedCanvases.getCanvas(
2517
- "maskCanvas",
2518
- width,
2519
- height
2520
- );
2521
- const maskCtx = maskCanvas.context;
2522
- maskCtx.save();
2523
-
2524
- putBinaryImageMask(maskCtx, image);
2525
-
2526
- maskCtx.globalCompositeOperation = "source-in";
2527
-
2528
- maskCtx.fillStyle = isPatternFill
2529
- ? fillColor.getPattern(
2530
- maskCtx,
2531
- this,
2532
- ctx.mozCurrentTransformInverse,
2533
- false
2534
- )
2535
- : fillColor;
2536
- maskCtx.fillRect(0, 0, width, height);
2537
-
2538
- maskCtx.restore();
2539
-
2540
- ctx.save();
2541
- ctx.transform.apply(ctx, image.transform);
2542
- ctx.scale(1, -1);
2543
- ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1);
2544
- ctx.restore();
2545
- }
2546
- }
2547
-
2548
- paintImageXObject(objId) {
2549
- if (!this.contentVisible) {
2550
- return;
2551
- }
2552
- const imgData = objId.startsWith("g_")
2553
- ? this.commonObjs.get(objId)
2554
- : this.objs.get(objId);
2555
- if (!imgData) {
2556
- warn("Dependent image isn't ready yet");
2557
- return;
2558
- }
2559
-
2560
- this.paintInlineImageXObject(imgData);
2561
- }
2562
-
2563
- paintImageXObjectRepeat(objId, scaleX, scaleY, positions) {
2564
- if (!this.contentVisible) {
2565
- return;
2566
- }
2567
- const imgData = objId.startsWith("g_")
2568
- ? this.commonObjs.get(objId)
2569
- : this.objs.get(objId);
2570
- if (!imgData) {
2571
- warn("Dependent image isn't ready yet");
2572
- return;
2573
- }
2574
-
2575
- const width = imgData.width;
2576
- const height = imgData.height;
2577
- const map = [];
2578
- for (let i = 0, ii = positions.length; i < ii; i += 2) {
2579
- map.push({
2580
- transform: [scaleX, 0, 0, scaleY, positions[i], positions[i + 1]],
2581
- x: 0,
2582
- y: 0,
2583
- w: width,
2584
- h: height,
2585
- });
2586
- }
2587
- this.paintInlineImageXObjectGroup(imgData, map);
2588
- }
2589
-
2590
- paintInlineImageXObject(imgData) {
2591
- if (!this.contentVisible) {
2592
- return;
2593
- }
2594
- const width = imgData.width;
2595
- const height = imgData.height;
2596
- const ctx = this.ctx;
2597
-
2598
- this.save();
2599
- // scale the image to the unit square
2600
- ctx.scale(1 / width, -1 / height);
2601
-
2602
- let imgToPaint;
2603
- // typeof check is needed due to node.js support, see issue #8489
2604
- if (
2605
- (typeof HTMLElement === "function" && imgData instanceof HTMLElement) ||
2606
- !imgData.data
2607
- ) {
2608
- imgToPaint = imgData;
2609
- } else {
2610
- const tmpCanvas = this.cachedCanvases.getCanvas(
2611
- "inlineImage",
2612
- width,
2613
- height
2614
- );
2615
- const tmpCtx = tmpCanvas.context;
2616
- putBinaryImageData(tmpCtx, imgData, this.current.transferMaps);
2617
- imgToPaint = tmpCanvas.canvas;
2618
- }
2619
-
2620
- const scaled = this._scaleImage(
2621
- imgToPaint,
2622
- ctx.mozCurrentTransformInverse
2623
- );
2624
- ctx.drawImage(
2625
- scaled.img,
2626
- 0,
2627
- 0,
2628
- scaled.paintWidth,
2629
- scaled.paintHeight,
2630
- 0,
2631
- -height,
2632
- width,
2633
- height
2634
- );
2635
-
2636
- if (this.imageLayer) {
2637
- const position = this.getCanvasPosition(0, -height);
2638
- this.imageLayer.appendImage({
2639
- imgData,
2640
- left: position[0],
2641
- top: position[1],
2642
- width: width / ctx.mozCurrentTransformInverse[0],
2643
- height: height / ctx.mozCurrentTransformInverse[3],
2644
- });
2645
- }
2646
- this.restore();
2647
- }
2648
-
2649
- paintInlineImageXObjectGroup(imgData, map) {
2650
- if (!this.contentVisible) {
2651
- return;
2652
- }
2653
- const ctx = this.ctx;
2654
- const w = imgData.width;
2655
- const h = imgData.height;
2656
-
2657
- const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", w, h);
2658
- const tmpCtx = tmpCanvas.context;
2659
- putBinaryImageData(tmpCtx, imgData, this.current.transferMaps);
2660
-
2661
- for (let i = 0, ii = map.length; i < ii; i++) {
2662
- const entry = map[i];
2663
- ctx.save();
2664
- ctx.transform.apply(ctx, entry.transform);
2665
- ctx.scale(1, -1);
2666
- ctx.drawImage(
2667
- tmpCanvas.canvas,
2668
- entry.x,
2669
- entry.y,
2670
- entry.w,
2671
- entry.h,
2672
- 0,
2673
- -1,
2674
- 1,
2675
- 1
2676
- );
2677
- if (this.imageLayer) {
2678
- const position = this.getCanvasPosition(entry.x, entry.y);
2679
- this.imageLayer.appendImage({
2680
- imgData,
2681
- left: position[0],
2682
- top: position[1],
2683
- width: w,
2684
- height: h,
2685
- });
2686
- }
2687
- ctx.restore();
2688
- }
2689
- }
2690
-
2691
- paintSolidColorImageMask() {
2692
- if (!this.contentVisible) {
2693
- return;
2694
- }
2695
- this.ctx.fillRect(0, 0, 1, 1);
2696
- }
2697
-
2698
- // Marked content
2699
-
2700
- markPoint(tag) {
2701
- // TODO Marked content.
2702
- }
2703
-
2704
- markPointProps(tag, properties) {
2705
- // TODO Marked content.
2706
- }
2707
-
2708
- beginMarkedContent(tag) {
2709
- this.markedContentStack.push({
2710
- visible: true,
2711
- });
2712
- }
2713
-
2714
- beginMarkedContentProps(tag, properties) {
2715
- if (tag === "OC") {
2716
- this.markedContentStack.push({
2717
- visible: this.optionalContentConfig.isVisible(properties),
2718
- });
2719
- } else {
2720
- this.markedContentStack.push({
2721
- visible: true,
2722
- });
2723
- }
2724
- this.contentVisible = this.isContentVisible();
2725
- }
2726
-
2727
- endMarkedContent() {
2728
- this.markedContentStack.pop();
2729
- this.contentVisible = this.isContentVisible();
2730
- }
2731
-
2732
- // Compatibility
2733
-
2734
- beginCompat() {
2735
- // TODO ignore undefined operators (should we do that anyway?)
2736
- }
2737
-
2738
- endCompat() {
2739
- // TODO stop ignoring undefined operators
2740
- }
2741
-
2742
- // Helper functions
2743
-
2744
- consumePath() {
2745
- const ctx = this.ctx;
2746
- if (this.pendingClip) {
2747
- if (this.pendingClip === EO_CLIP) {
2748
- ctx.clip("evenodd");
2749
- } else {
2750
- ctx.clip();
2751
- }
2752
- this.pendingClip = null;
2753
- }
2754
- ctx.beginPath();
2755
- }
2756
-
2757
- getSinglePixelWidth() {
2758
- if (this._cachedGetSinglePixelWidth === null) {
2759
- // If transform is [a b] then a pixel (square) is transformed
2760
- // [c d]
2761
- // into a parallelogram: its area is the abs value of the determinant.
2762
- // This parallelogram has 2 heights:
2763
- // - Area / |col_1|;
2764
- // - Area / |col_2|.
2765
- // so in order to get a height of at least 1, pixel height
2766
- // must be computed as followed:
2767
- // h = max(sqrt(a² + c²) / |det(M)|, sqrt(b² + d²) / |det(M)|).
2768
- // This is equivalent to:
2769
- // h = max(|line_1_inv(M)|, |line_2_inv(M)|)
2770
- const m = this.ctx.mozCurrentTransform;
2771
-
2772
- const absDet = Math.abs(m[0] * m[3] - m[2] * m[1]);
2773
- const sqNorm1 = m[0] ** 2 + m[2] ** 2;
2774
- const sqNorm2 = m[1] ** 2 + m[3] ** 2;
2775
- const pixelHeight = Math.sqrt(Math.max(sqNorm1, sqNorm2)) / absDet;
2776
- if (
2777
- sqNorm1 !== sqNorm2 &&
2778
- this._combinedScaleFactor * pixelHeight > 1
2779
- ) {
2780
- // The parallelogram isn't a square and at least one height
2781
- // is lower than 1 so the resulting line width must be 1
2782
- // but it cannot be achieved with one scale: when scaling a pixel
2783
- // we'll get a rectangle (see issue #12295).
2784
- // For example with matrix [0.001 0, 0, 100], a pixel is transformed
2785
- // in a rectangle 0.001x100. If we just scale by 1000 (to have a 1)
2786
- // then we'll get a rectangle 1x1e5 which is wrong.
2787
- // In this case, we must reset the transform, set linewidth to 1
2788
- // and then stroke.
2789
- this._cachedGetSinglePixelWidth = -(
2790
- this._combinedScaleFactor * pixelHeight
2791
- );
2792
- } else if (absDet > Number.EPSILON) {
2793
- this._cachedGetSinglePixelWidth = pixelHeight;
2794
- } else {
2795
- // Matrix is non-invertible.
2796
- this._cachedGetSinglePixelWidth = 1;
2797
- }
2798
- }
2799
-
2800
- return this._cachedGetSinglePixelWidth;
2801
- }
2802
-
2803
- getCanvasPosition(x, y) {
2804
- const transform = this.ctx.mozCurrentTransform;
2805
- return [
2806
- transform[0] * x + transform[2] * y + transform[4],
2807
- transform[1] * x + transform[3] * y + transform[5],
2808
- ];
2809
- }
2810
-
2811
- isContentVisible() {
2812
- for (let i = this.markedContentStack.length - 1; i >= 0; i--) {
2813
- if (!this.markedContentStack[i].visible) {
2814
- return false;
2815
- }
2816
- }
2817
- return true;
2818
- }
2819
- }
2820
-
2821
- for (const op in OPS) {
2822
- CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op];
2823
- }
2824
-
2825
- return CanvasGraphics;
2826
- })();
2827
-
2828
- export { CanvasGraphics };