@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,2948 +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
- AnnotationActionEventType,
18
- AnnotationBorderStyleType,
19
- AnnotationFieldFlag,
20
- AnnotationFlag,
21
- AnnotationReplyType,
22
- AnnotationType,
23
- assert,
24
- escapeString,
25
- getModificationDate,
26
- isAscii,
27
- isString,
28
- OPS,
29
- shadow,
30
- stringToPDFString,
31
- stringToUTF16BEString,
32
- unreachable,
33
- Util,
34
- warn,
35
- } from "../shared/util.js";
36
- import { collectActions, getInheritableProperty } from "./core_utils.js";
37
- import {
38
- createDefaultAppearance,
39
- parseDefaultAppearance,
40
- } from "./default_appearance.js";
41
- import {
42
- Dict,
43
- isDict,
44
- isName,
45
- isRef,
46
- isStream,
47
- Name,
48
- RefSet,
49
- } from "./primitives.js";
50
- import { Catalog } from "./catalog.js";
51
- import { ColorSpace } from "./colorspace.js";
52
- import { FileSpec } from "./file_spec.js";
53
- import { ObjectLoader } from "./object_loader.js";
54
- import { OperatorList } from "./operator_list.js";
55
- import { StringStream } from "./stream.js";
56
- import { writeDict } from "./writer.js";
57
-
58
- class AnnotationFactory {
59
- /**
60
- * Create an `Annotation` object of the correct type for the given reference
61
- * to an annotation dictionary. This yields a promise that is resolved when
62
- * the `Annotation` object is constructed.
63
- *
64
- * @param {XRef} xref
65
- * @param {Object} ref
66
- * @param {PDFManager} pdfManager
67
- * @param {Object} idFactory
68
- * @param {boolean} collectFields
69
- * @returns {Promise} A promise that is resolved with an {Annotation}
70
- * instance.
71
- */
72
- static create(xref, ref, pdfManager, idFactory, collectFields) {
73
- return Promise.all([
74
- pdfManager.ensureCatalog("acroForm"),
75
- collectFields ? this._getPageIndex(xref, ref, pdfManager) : -1,
76
- ]).then(([acroForm, pageIndex]) =>
77
- pdfManager.ensure(this, "_create", [
78
- xref,
79
- ref,
80
- pdfManager,
81
- idFactory,
82
- acroForm,
83
- collectFields,
84
- pageIndex,
85
- ])
86
- );
87
- }
88
-
89
- /**
90
- * @private
91
- */
92
- static _create(
93
- xref,
94
- ref,
95
- pdfManager,
96
- idFactory,
97
- acroForm,
98
- collectFields,
99
- pageIndex = -1
100
- ) {
101
- const dict = xref.fetchIfRef(ref);
102
- if (!isDict(dict)) {
103
- return undefined;
104
- }
105
-
106
- const id = isRef(ref) ? ref.toString() : `annot_${idFactory.createObjId()}`;
107
-
108
- // Determine the annotation's subtype.
109
- let subtype = dict.get("Subtype");
110
- subtype = isName(subtype) ? subtype.name : null;
111
-
112
- // Return the right annotation object based on the subtype and field type.
113
- const parameters = {
114
- xref,
115
- ref,
116
- dict,
117
- subtype,
118
- id,
119
- pdfManager,
120
- acroForm: acroForm instanceof Dict ? acroForm : Dict.empty,
121
- collectFields,
122
- pageIndex,
123
- };
124
-
125
- switch (subtype) {
126
- case "Link":
127
- return new LinkAnnotation(parameters);
128
-
129
- case "Text":
130
- return new TextAnnotation(parameters);
131
-
132
- case "Widget":
133
- let fieldType = getInheritableProperty({ dict, key: "FT" });
134
- fieldType = isName(fieldType) ? fieldType.name : null;
135
-
136
- switch (fieldType) {
137
- case "Tx":
138
- return new TextWidgetAnnotation(parameters);
139
- case "Btn":
140
- return new ButtonWidgetAnnotation(parameters);
141
- case "Ch":
142
- return new ChoiceWidgetAnnotation(parameters);
143
- case "Sig":
144
- return new SignatureWidgetAnnotation(parameters);
145
- }
146
- warn(
147
- `Unimplemented widget field type "${fieldType}", ` +
148
- "falling back to base field type."
149
- );
150
- return new WidgetAnnotation(parameters);
151
-
152
- case "Popup":
153
- return new PopupAnnotation(parameters);
154
-
155
- case "FreeText":
156
- return new FreeTextAnnotation(parameters);
157
-
158
- case "Line":
159
- return new LineAnnotation(parameters);
160
-
161
- case "Square":
162
- return new SquareAnnotation(parameters);
163
-
164
- case "Circle":
165
- return new CircleAnnotation(parameters);
166
-
167
- case "PolyLine":
168
- return new PolylineAnnotation(parameters);
169
-
170
- case "Polygon":
171
- return new PolygonAnnotation(parameters);
172
-
173
- case "Caret":
174
- return new CaretAnnotation(parameters);
175
-
176
- case "Ink":
177
- return new InkAnnotation(parameters);
178
-
179
- case "Highlight":
180
- return new HighlightAnnotation(parameters);
181
-
182
- case "Underline":
183
- return new UnderlineAnnotation(parameters);
184
-
185
- case "Squiggly":
186
- return new SquigglyAnnotation(parameters);
187
-
188
- case "StrikeOut":
189
- return new StrikeOutAnnotation(parameters);
190
-
191
- case "Stamp":
192
- return new StampAnnotation(parameters);
193
-
194
- case "FileAttachment":
195
- return new FileAttachmentAnnotation(parameters);
196
-
197
- default:
198
- if (!collectFields) {
199
- if (!subtype) {
200
- warn("Annotation is missing the required /Subtype.");
201
- } else {
202
- warn(
203
- `Unimplemented annotation type "${subtype}", ` +
204
- "falling back to base annotation."
205
- );
206
- }
207
- }
208
- return new Annotation(parameters);
209
- }
210
- }
211
-
212
- static async _getPageIndex(xref, ref, pdfManager) {
213
- try {
214
- const annotDict = await xref.fetchIfRefAsync(ref);
215
- if (!isDict(annotDict)) {
216
- return -1;
217
- }
218
- const pageRef = annotDict.getRaw("P");
219
- if (!isRef(pageRef)) {
220
- return -1;
221
- }
222
- const pageIndex = await pdfManager.ensureCatalog("getPageIndex", [
223
- pageRef,
224
- ]);
225
- return pageIndex;
226
- } catch (ex) {
227
- warn(`_getPageIndex: "${ex}".`);
228
- return -1;
229
- }
230
- }
231
- }
232
-
233
- function getRgbColor(color) {
234
- const rgbColor = new Uint8ClampedArray(3);
235
- if (!Array.isArray(color)) {
236
- return rgbColor;
237
- }
238
-
239
- switch (color.length) {
240
- case 0: // Transparent, which we indicate with a null value
241
- return null;
242
-
243
- case 1: // Convert grayscale to RGB
244
- ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0);
245
- return rgbColor;
246
-
247
- case 3: // Convert RGB percentages to RGB
248
- ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0);
249
- return rgbColor;
250
-
251
- case 4: // Convert CMYK to RGB
252
- ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0);
253
- return rgbColor;
254
-
255
- default:
256
- return rgbColor;
257
- }
258
- }
259
-
260
- function getQuadPoints(dict, rect) {
261
- if (!dict.has("QuadPoints")) {
262
- return null;
263
- }
264
-
265
- // The region is described as a number of quadrilaterals.
266
- // Each quadrilateral must consist of eight coordinates.
267
- const quadPoints = dict.getArray("QuadPoints");
268
- if (
269
- !Array.isArray(quadPoints) ||
270
- quadPoints.length === 0 ||
271
- quadPoints.length % 8 > 0
272
- ) {
273
- return null;
274
- }
275
-
276
- const quadPointsLists = [];
277
- for (let i = 0, ii = quadPoints.length / 8; i < ii; i++) {
278
- // Each series of eight numbers represents the coordinates for one
279
- // quadrilateral in the order [x1, y1, x2, y2, x3, y3, x4, y4].
280
- // Convert this to an array of objects with x and y coordinates.
281
- quadPointsLists.push([]);
282
- for (let j = i * 8, jj = i * 8 + 8; j < jj; j += 2) {
283
- const x = quadPoints[j];
284
- const y = quadPoints[j + 1];
285
-
286
- // The quadpoints should be ignored if any coordinate in the array
287
- // lies outside the region specified by the rectangle. The rectangle
288
- // can be `null` for markup annotations since their rectangle may be
289
- // incorrect (fixes bug 1538111).
290
- if (
291
- rect !== null &&
292
- (x < rect[0] || x > rect[2] || y < rect[1] || y > rect[3])
293
- ) {
294
- return null;
295
- }
296
- quadPointsLists[i].push({ x, y });
297
- }
298
- }
299
-
300
- // The PDF specification states in section 12.5.6.10 (figure 64) that the
301
- // order of the quadpoints should be bottom left, bottom right, top right
302
- // and top left. However, in practice PDF files use a different order,
303
- // namely bottom left, bottom right, top left and top right (this is also
304
- // mentioned on https://github.com/highkite/pdfAnnotate#QuadPoints), so
305
- // this is the actual order we should work with. However, the situation is
306
- // even worse since Adobe's own applications and other applications violate
307
- // the specification and create annotations with other orders, namely top
308
- // left, top right, bottom left and bottom right or even top left, top right,
309
- // bottom right and bottom left. To avoid inconsistency and broken rendering,
310
- // we normalize all lists to put the quadpoints in the same standard order
311
- // (see https://stackoverflow.com/a/10729881).
312
- return quadPointsLists.map(quadPointsList => {
313
- const [minX, maxX, minY, maxY] = quadPointsList.reduce(
314
- ([mX, MX, mY, MY], quadPoint) => [
315
- Math.min(mX, quadPoint.x),
316
- Math.max(MX, quadPoint.x),
317
- Math.min(mY, quadPoint.y),
318
- Math.max(MY, quadPoint.y),
319
- ],
320
- [Number.MAX_VALUE, Number.MIN_VALUE, Number.MAX_VALUE, Number.MIN_VALUE]
321
- );
322
- return [
323
- { x: minX, y: maxY },
324
- { x: maxX, y: maxY },
325
- { x: minX, y: minY },
326
- { x: maxX, y: minY },
327
- ];
328
- });
329
- }
330
-
331
- function getTransformMatrix(rect, bbox, matrix) {
332
- // 12.5.5: Algorithm: Appearance streams
333
- const [minX, minY, maxX, maxY] = Util.getAxialAlignedBoundingBox(
334
- bbox,
335
- matrix
336
- );
337
- if (minX === maxX || minY === maxY) {
338
- // From real-life file, bbox was [0, 0, 0, 0]. In this case,
339
- // just apply the transform for rect
340
- return [1, 0, 0, 1, rect[0], rect[1]];
341
- }
342
-
343
- const xRatio = (rect[2] - rect[0]) / (maxX - minX);
344
- const yRatio = (rect[3] - rect[1]) / (maxY - minY);
345
- return [
346
- xRatio,
347
- 0,
348
- 0,
349
- yRatio,
350
- rect[0] - minX * xRatio,
351
- rect[1] - minY * yRatio,
352
- ];
353
- }
354
-
355
- class Annotation {
356
- constructor(params) {
357
- const dict = params.dict;
358
-
359
- this.setContents(dict.get("Contents"));
360
- this.setModificationDate(dict.get("M"));
361
- this.setFlags(dict.get("F"));
362
- this.setRectangle(dict.getArray("Rect"));
363
- this.setColor(dict.getArray("C"));
364
- this.setBorderStyle(dict);
365
- this.setAppearance(dict);
366
-
367
- this._streams = [];
368
- if (this.appearance) {
369
- this._streams.push(this.appearance);
370
- }
371
-
372
- // Expose public properties using a data object.
373
- this.data = {
374
- annotationFlags: this.flags,
375
- borderStyle: this.borderStyle,
376
- color: this.color,
377
- contents: this.contents,
378
- hasAppearance: !!this.appearance,
379
- id: params.id,
380
- modificationDate: this.modificationDate,
381
- rect: this.rectangle,
382
- subtype: params.subtype,
383
- };
384
-
385
- if (params.collectFields) {
386
- // Fields can act as container for other fields and have
387
- // some actions even if no Annotation inherit from them.
388
- // Those fields can be referenced by CO (calculation order).
389
- const kids = dict.get("Kids");
390
- if (Array.isArray(kids)) {
391
- const kidIds = [];
392
- for (const kid of kids) {
393
- if (isRef(kid)) {
394
- kidIds.push(kid.toString());
395
- }
396
- }
397
- if (kidIds.length !== 0) {
398
- this.data.kidIds = kidIds;
399
- }
400
- }
401
-
402
- this.data.actions = collectActions(
403
- params.xref,
404
- dict,
405
- AnnotationActionEventType
406
- );
407
- this.data.fieldName = this._constructFieldName(dict);
408
- this.data.pageIndex = params.pageIndex;
409
- }
410
-
411
- this._fallbackFontDict = null;
412
- }
413
-
414
- /**
415
- * @private
416
- */
417
- _hasFlag(flags, flag) {
418
- return !!(flags & flag);
419
- }
420
-
421
- /**
422
- * @private
423
- */
424
- _isViewable(flags) {
425
- return (
426
- !this._hasFlag(flags, AnnotationFlag.INVISIBLE) &&
427
- !this._hasFlag(flags, AnnotationFlag.NOVIEW)
428
- );
429
- }
430
-
431
- /**
432
- * @private
433
- */
434
- _isPrintable(flags) {
435
- return (
436
- this._hasFlag(flags, AnnotationFlag.PRINT) &&
437
- !this._hasFlag(flags, AnnotationFlag.INVISIBLE)
438
- );
439
- }
440
-
441
- /**
442
- * Check if the annotation must be displayed by taking into account
443
- * the value found in the annotationStorage which may have been set
444
- * through JS.
445
- *
446
- * @public
447
- * @memberof Annotation
448
- * @param {AnnotationStorage} [annotationStorage] - Storage for annotation
449
- */
450
- mustBeViewed(annotationStorage) {
451
- const storageEntry =
452
- annotationStorage && annotationStorage.get(this.data.id);
453
- if (storageEntry && storageEntry.hidden !== undefined) {
454
- return !storageEntry.hidden;
455
- }
456
- return this.viewable && !this._hasFlag(this.flags, AnnotationFlag.HIDDEN);
457
- }
458
-
459
- /**
460
- * Check if the annotation must be printed by taking into account
461
- * the value found in the annotationStorage which may have been set
462
- * through JS.
463
- *
464
- * @public
465
- * @memberof Annotation
466
- * @param {AnnotationStorage} [annotationStorage] - Storage for annotation
467
- */
468
- mustBePrinted(annotationStorage) {
469
- const storageEntry =
470
- annotationStorage && annotationStorage.get(this.data.id);
471
- if (storageEntry && storageEntry.print !== undefined) {
472
- return storageEntry.print;
473
- }
474
- return this.printable;
475
- }
476
-
477
- /**
478
- * @type {boolean}
479
- */
480
- get viewable() {
481
- if (this.data.quadPoints === null) {
482
- return false;
483
- }
484
- if (this.flags === 0) {
485
- return true;
486
- }
487
- return this._isViewable(this.flags);
488
- }
489
-
490
- /**
491
- * @type {boolean}
492
- */
493
- get printable() {
494
- if (this.data.quadPoints === null) {
495
- return false;
496
- }
497
- if (this.flags === 0) {
498
- return false;
499
- }
500
- return this._isPrintable(this.flags);
501
- }
502
-
503
- /**
504
- * Set the contents.
505
- *
506
- * @public
507
- * @memberof Annotation
508
- * @param {string} contents - Text to display for the annotation or, if the
509
- * type of annotation does not display text, a
510
- * description of the annotation's contents
511
- */
512
- setContents(contents) {
513
- this.contents = stringToPDFString(contents || "");
514
- }
515
-
516
- /**
517
- * Set the modification date.
518
- *
519
- * @public
520
- * @memberof Annotation
521
- * @param {string} modificationDate - PDF date string that indicates when the
522
- * annotation was last modified
523
- */
524
- setModificationDate(modificationDate) {
525
- this.modificationDate = isString(modificationDate)
526
- ? modificationDate
527
- : null;
528
- }
529
-
530
- /**
531
- * Set the flags.
532
- *
533
- * @public
534
- * @memberof Annotation
535
- * @param {number} flags - Unsigned 32-bit integer specifying annotation
536
- * characteristics
537
- * @see {@link shared/util.js}
538
- */
539
- setFlags(flags) {
540
- this.flags = Number.isInteger(flags) && flags > 0 ? flags : 0;
541
- }
542
-
543
- /**
544
- * Check if a provided flag is set.
545
- *
546
- * @public
547
- * @memberof Annotation
548
- * @param {number} flag - Hexadecimal representation for an annotation
549
- * characteristic
550
- * @returns {boolean}
551
- * @see {@link shared/util.js}
552
- */
553
- hasFlag(flag) {
554
- return this._hasFlag(this.flags, flag);
555
- }
556
-
557
- /**
558
- * Set the rectangle.
559
- *
560
- * @public
561
- * @memberof Annotation
562
- * @param {Array} rectangle - The rectangle array with exactly four entries
563
- */
564
- setRectangle(rectangle) {
565
- if (Array.isArray(rectangle) && rectangle.length === 4) {
566
- this.rectangle = Util.normalizeRect(rectangle);
567
- } else {
568
- this.rectangle = [0, 0, 0, 0];
569
- }
570
- }
571
-
572
- /**
573
- * Set the color and take care of color space conversion.
574
- * The default value is black, in RGB color space.
575
- *
576
- * @public
577
- * @memberof Annotation
578
- * @param {Array} color - The color array containing either 0
579
- * (transparent), 1 (grayscale), 3 (RGB) or
580
- * 4 (CMYK) elements
581
- */
582
- setColor(color) {
583
- this.color = getRgbColor(color);
584
- }
585
-
586
- /**
587
- * Set the border style (as AnnotationBorderStyle object).
588
- *
589
- * @public
590
- * @memberof Annotation
591
- * @param {Dict} borderStyle - The border style dictionary
592
- */
593
- setBorderStyle(borderStyle) {
594
- if (
595
- typeof PDFJSDev === "undefined" ||
596
- PDFJSDev.test("!PRODUCTION || TESTING")
597
- ) {
598
- assert(this.rectangle, "setRectangle must have been called previously.");
599
- }
600
-
601
- this.borderStyle = new AnnotationBorderStyle();
602
- if (!isDict(borderStyle)) {
603
- return;
604
- }
605
- if (borderStyle.has("BS")) {
606
- const dict = borderStyle.get("BS");
607
- const dictType = dict.get("Type");
608
-
609
- if (!dictType || isName(dictType, "Border")) {
610
- this.borderStyle.setWidth(dict.get("W"), this.rectangle);
611
- this.borderStyle.setStyle(dict.get("S"));
612
- this.borderStyle.setDashArray(dict.getArray("D"));
613
- }
614
- } else if (borderStyle.has("Border")) {
615
- const array = borderStyle.getArray("Border");
616
- if (Array.isArray(array) && array.length >= 3) {
617
- this.borderStyle.setHorizontalCornerRadius(array[0]);
618
- this.borderStyle.setVerticalCornerRadius(array[1]);
619
- this.borderStyle.setWidth(array[2], this.rectangle);
620
-
621
- if (array.length === 4) {
622
- // Dash array available
623
- this.borderStyle.setDashArray(array[3]);
624
- }
625
- }
626
- } else {
627
- // There are no border entries in the dictionary. According to the
628
- // specification, we should draw a solid border of width 1 in that
629
- // case, but Adobe Reader did not implement that part of the
630
- // specification and instead draws no border at all, so we do the same.
631
- // See also https://github.com/mozilla/pdf.js/issues/6179.
632
- this.borderStyle.setWidth(0);
633
- }
634
- }
635
-
636
- /**
637
- * Set the (normal) appearance.
638
- *
639
- * @public
640
- * @memberof Annotation
641
- * @param {Dict} dict - The annotation's data dictionary
642
- */
643
- setAppearance(dict) {
644
- this.appearance = null;
645
-
646
- const appearanceStates = dict.get("AP");
647
- if (!isDict(appearanceStates)) {
648
- return;
649
- }
650
-
651
- // In case the normal appearance is a stream, then it is used directly.
652
- const normalAppearanceState = appearanceStates.get("N");
653
- if (isStream(normalAppearanceState)) {
654
- this.appearance = normalAppearanceState;
655
- return;
656
- }
657
- if (!isDict(normalAppearanceState)) {
658
- return;
659
- }
660
-
661
- // In case the normal appearance is a dictionary, the `AS` entry provides
662
- // the key of the stream in this dictionary.
663
- const as = dict.get("AS");
664
- if (!isName(as) || !normalAppearanceState.has(as.name)) {
665
- return;
666
- }
667
- this.appearance = normalAppearanceState.get(as.name);
668
- }
669
-
670
- loadResources(keys) {
671
- return this.appearance.dict.getAsync("Resources").then(resources => {
672
- if (!resources) {
673
- return undefined;
674
- }
675
-
676
- const objectLoader = new ObjectLoader(resources, keys, resources.xref);
677
- return objectLoader.load().then(function () {
678
- return resources;
679
- });
680
- });
681
- }
682
-
683
- getOperatorList(evaluator, task, renderForms, annotationStorage) {
684
- if (!this.appearance) {
685
- return Promise.resolve(new OperatorList());
686
- }
687
-
688
- const appearance = this.appearance;
689
- const data = this.data;
690
- const appearanceDict = appearance.dict;
691
- const resourcesPromise = this.loadResources([
692
- "ExtGState",
693
- "ColorSpace",
694
- "Pattern",
695
- "Shading",
696
- "XObject",
697
- "Font",
698
- ]);
699
- const bbox = appearanceDict.getArray("BBox") || [0, 0, 1, 1];
700
- const matrix = appearanceDict.getArray("Matrix") || [1, 0, 0, 1, 0, 0];
701
- const transform = getTransformMatrix(data.rect, bbox, matrix);
702
-
703
- return resourcesPromise.then(resources => {
704
- const opList = new OperatorList();
705
- opList.addOp(OPS.beginAnnotation, [
706
- data.id,
707
- data.rect,
708
- transform,
709
- matrix,
710
- ]);
711
-
712
- return evaluator
713
- .getOperatorList({
714
- stream: appearance,
715
- task,
716
- resources,
717
- operatorList: opList,
718
- fallbackFontDict: this._fallbackFontDict,
719
- })
720
- .then(() => {
721
- opList.addOp(OPS.endAnnotation, []);
722
- this.reset();
723
- return opList;
724
- });
725
- });
726
- }
727
-
728
- async save(evaluator, task, annotationStorage) {
729
- return null;
730
- }
731
-
732
- /**
733
- * Get field data for usage in JS sandbox.
734
- *
735
- * Field object is defined here:
736
- * https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/js_api_reference.pdf#page=16
737
- *
738
- * @public
739
- * @memberof Annotation
740
- * @returns {Object | null}
741
- */
742
- getFieldObject() {
743
- if (this.data.kidIds) {
744
- return {
745
- id: this.data.id,
746
- actions: this.data.actions,
747
- name: this.data.fieldName,
748
- type: "",
749
- kidIds: this.data.kidIds,
750
- page: this.data.pageIndex,
751
- };
752
- }
753
- return null;
754
- }
755
-
756
- /**
757
- * Reset the annotation.
758
- *
759
- * This involves resetting the various streams that are either cached on the
760
- * annotation instance or created during its construction.
761
- *
762
- * @public
763
- * @memberof Annotation
764
- */
765
- reset() {
766
- if (
767
- (typeof PDFJSDev === "undefined" ||
768
- PDFJSDev.test("!PRODUCTION || TESTING")) &&
769
- this.appearance &&
770
- !this._streams.includes(this.appearance)
771
- ) {
772
- unreachable("The appearance stream should always be reset.");
773
- }
774
-
775
- for (const stream of this._streams) {
776
- stream.reset();
777
- }
778
- }
779
-
780
- /**
781
- * Construct the (fully qualified) field name from the (partial) field
782
- * names of the field and its ancestors.
783
- *
784
- * @private
785
- * @memberof Annotation
786
- * @param {Dict} dict - Complete widget annotation dictionary
787
- * @returns {string}
788
- */
789
- _constructFieldName(dict) {
790
- // Both the `Parent` and `T` fields are optional. While at least one of
791
- // them should be provided, bad PDF generators may fail to do so.
792
- if (!dict.has("T") && !dict.has("Parent")) {
793
- warn("Unknown field name, falling back to empty field name.");
794
- return "";
795
- }
796
-
797
- // If no parent exists, the partial and fully qualified names are equal.
798
- if (!dict.has("Parent")) {
799
- return stringToPDFString(dict.get("T"));
800
- }
801
-
802
- // Form the fully qualified field name by appending the partial name to
803
- // the parent's fully qualified name, separated by a period.
804
- const fieldName = [];
805
- if (dict.has("T")) {
806
- fieldName.unshift(stringToPDFString(dict.get("T")));
807
- }
808
-
809
- let loopDict = dict;
810
- const visited = new RefSet();
811
- if (dict.objId) {
812
- visited.put(dict.objId);
813
- }
814
- while (loopDict.has("Parent")) {
815
- loopDict = loopDict.get("Parent");
816
- if (
817
- !(loopDict instanceof Dict) ||
818
- (loopDict.objId && visited.has(loopDict.objId))
819
- ) {
820
- // Even though it is not allowed according to the PDF specification,
821
- // bad PDF generators may provide a `Parent` entry that is not a
822
- // dictionary, but `null` for example (issue 8143).
823
- //
824
- // If parent has been already visited, it means that we're
825
- // in an infinite loop.
826
- break;
827
- }
828
- if (loopDict.objId) {
829
- visited.put(loopDict.objId);
830
- }
831
-
832
- if (loopDict.has("T")) {
833
- fieldName.unshift(stringToPDFString(loopDict.get("T")));
834
- }
835
- }
836
- return fieldName.join(".");
837
- }
838
- }
839
-
840
- /**
841
- * Contains all data regarding an annotation's border style.
842
- */
843
- class AnnotationBorderStyle {
844
- constructor() {
845
- this.width = 1;
846
- this.style = AnnotationBorderStyleType.SOLID;
847
- this.dashArray = [3];
848
- this.horizontalCornerRadius = 0;
849
- this.verticalCornerRadius = 0;
850
- }
851
-
852
- /**
853
- * Set the width.
854
- *
855
- * @public
856
- * @memberof AnnotationBorderStyle
857
- * @param {number} width - The width.
858
- * @param {Array} rect - The annotation `Rect` entry.
859
- */
860
- setWidth(width, rect = [0, 0, 0, 0]) {
861
- if (
862
- typeof PDFJSDev === "undefined" ||
863
- PDFJSDev.test("!PRODUCTION || TESTING")
864
- ) {
865
- assert(
866
- Array.isArray(rect) && rect.length === 4,
867
- "A valid `rect` parameter must be provided."
868
- );
869
- }
870
-
871
- // Some corrupt PDF generators may provide the width as a `Name`,
872
- // rather than as a number (fixes issue 10385).
873
- if (isName(width)) {
874
- this.width = 0; // This is consistent with the behaviour in Adobe Reader.
875
- return;
876
- }
877
- if (Number.isInteger(width)) {
878
- if (width > 0) {
879
- const maxWidth = (rect[2] - rect[0]) / 2;
880
- const maxHeight = (rect[3] - rect[1]) / 2;
881
-
882
- // Ignore large `width`s, since they lead to the Annotation overflowing
883
- // the size set by the `Rect` entry thus causing the `annotationLayer`
884
- // to render it over the surrounding document (fixes bug1552113.pdf).
885
- if (
886
- maxWidth > 0 &&
887
- maxHeight > 0 &&
888
- (width > maxWidth || width > maxHeight)
889
- ) {
890
- warn(`AnnotationBorderStyle.setWidth - ignoring width: ${width}`);
891
- width = 1;
892
- }
893
- }
894
- this.width = width;
895
- }
896
- }
897
-
898
- /**
899
- * Set the style.
900
- *
901
- * @public
902
- * @memberof AnnotationBorderStyle
903
- * @param {Name} style - The annotation style.
904
- * @see {@link shared/util.js}
905
- */
906
- setStyle(style) {
907
- if (!isName(style)) {
908
- return;
909
- }
910
- switch (style.name) {
911
- case "S":
912
- this.style = AnnotationBorderStyleType.SOLID;
913
- break;
914
-
915
- case "D":
916
- this.style = AnnotationBorderStyleType.DASHED;
917
- break;
918
-
919
- case "B":
920
- this.style = AnnotationBorderStyleType.BEVELED;
921
- break;
922
-
923
- case "I":
924
- this.style = AnnotationBorderStyleType.INSET;
925
- break;
926
-
927
- case "U":
928
- this.style = AnnotationBorderStyleType.UNDERLINE;
929
- break;
930
-
931
- default:
932
- break;
933
- }
934
- }
935
-
936
- /**
937
- * Set the dash array.
938
- *
939
- * @public
940
- * @memberof AnnotationBorderStyle
941
- * @param {Array} dashArray - The dash array with at least one element
942
- */
943
- setDashArray(dashArray) {
944
- // We validate the dash array, but we do not use it because CSS does not
945
- // allow us to change spacing of dashes. For more information, visit
946
- // http://www.w3.org/TR/css3-background/#the-border-style.
947
- if (Array.isArray(dashArray) && dashArray.length > 0) {
948
- // According to the PDF specification: the elements in `dashArray`
949
- // shall be numbers that are nonnegative and not all equal to zero.
950
- let isValid = true;
951
- let allZeros = true;
952
- for (const element of dashArray) {
953
- const validNumber = +element >= 0;
954
- if (!validNumber) {
955
- isValid = false;
956
- break;
957
- } else if (element > 0) {
958
- allZeros = false;
959
- }
960
- }
961
- if (isValid && !allZeros) {
962
- this.dashArray = dashArray;
963
- } else {
964
- this.width = 0; // Adobe behavior when the array is invalid.
965
- }
966
- } else if (dashArray) {
967
- this.width = 0; // Adobe behavior when the array is invalid.
968
- }
969
- }
970
-
971
- /**
972
- * Set the horizontal corner radius (from a Border dictionary).
973
- *
974
- * @public
975
- * @memberof AnnotationBorderStyle
976
- * @param {number} radius - The horizontal corner radius.
977
- */
978
- setHorizontalCornerRadius(radius) {
979
- if (Number.isInteger(radius)) {
980
- this.horizontalCornerRadius = radius;
981
- }
982
- }
983
-
984
- /**
985
- * Set the vertical corner radius (from a Border dictionary).
986
- *
987
- * @public
988
- * @memberof AnnotationBorderStyle
989
- * @param {number} radius - The vertical corner radius.
990
- */
991
- setVerticalCornerRadius(radius) {
992
- if (Number.isInteger(radius)) {
993
- this.verticalCornerRadius = radius;
994
- }
995
- }
996
- }
997
-
998
- class MarkupAnnotation extends Annotation {
999
- constructor(parameters) {
1000
- super(parameters);
1001
-
1002
- const dict = parameters.dict;
1003
-
1004
- if (dict.has("IRT")) {
1005
- const rawIRT = dict.getRaw("IRT");
1006
- this.data.inReplyTo = isRef(rawIRT) ? rawIRT.toString() : null;
1007
-
1008
- const rt = dict.get("RT");
1009
- this.data.replyType = isName(rt) ? rt.name : AnnotationReplyType.REPLY;
1010
- }
1011
-
1012
- if (this.data.replyType === AnnotationReplyType.GROUP) {
1013
- // Subordinate annotations in a group should inherit
1014
- // the group attributes from the primary annotation.
1015
- const parent = dict.get("IRT");
1016
-
1017
- this.data.title = stringToPDFString(parent.get("T") || "");
1018
-
1019
- this.setContents(parent.get("Contents"));
1020
- this.data.contents = this.contents;
1021
-
1022
- if (!parent.has("CreationDate")) {
1023
- this.data.creationDate = null;
1024
- } else {
1025
- this.setCreationDate(parent.get("CreationDate"));
1026
- this.data.creationDate = this.creationDate;
1027
- }
1028
-
1029
- if (!parent.has("M")) {
1030
- this.data.modificationDate = null;
1031
- } else {
1032
- this.setModificationDate(parent.get("M"));
1033
- this.data.modificationDate = this.modificationDate;
1034
- }
1035
-
1036
- this.data.hasPopup = parent.has("Popup");
1037
-
1038
- if (!parent.has("C")) {
1039
- // Fall back to the default background color.
1040
- this.data.color = null;
1041
- } else {
1042
- this.setColor(parent.getArray("C"));
1043
- this.data.color = this.color;
1044
- }
1045
- } else {
1046
- this.data.title = stringToPDFString(dict.get("T") || "");
1047
-
1048
- this.setCreationDate(dict.get("CreationDate"));
1049
- this.data.creationDate = this.creationDate;
1050
-
1051
- this.data.hasPopup = dict.has("Popup");
1052
-
1053
- if (!dict.has("C")) {
1054
- // Fall back to the default background color.
1055
- this.data.color = null;
1056
- }
1057
- }
1058
- }
1059
-
1060
- /**
1061
- * Set the creation date.
1062
- *
1063
- * @public
1064
- * @memberof MarkupAnnotation
1065
- * @param {string} creationDate - PDF date string that indicates when the
1066
- * annotation was originally created
1067
- */
1068
- setCreationDate(creationDate) {
1069
- this.creationDate = isString(creationDate) ? creationDate : null;
1070
- }
1071
-
1072
- _setDefaultAppearance({
1073
- xref,
1074
- extra,
1075
- strokeColor,
1076
- fillColor,
1077
- blendMode,
1078
- strokeAlpha,
1079
- fillAlpha,
1080
- pointsCallback,
1081
- }) {
1082
- let minX = Number.MAX_VALUE;
1083
- let minY = Number.MAX_VALUE;
1084
- let maxX = Number.MIN_VALUE;
1085
- let maxY = Number.MIN_VALUE;
1086
-
1087
- const buffer = ["q"];
1088
- if (extra) {
1089
- buffer.push(extra);
1090
- }
1091
- if (strokeColor) {
1092
- buffer.push(`${strokeColor[0]} ${strokeColor[1]} ${strokeColor[2]} RG`);
1093
- }
1094
- if (fillColor) {
1095
- buffer.push(`${fillColor[0]} ${fillColor[1]} ${fillColor[2]} rg`);
1096
- }
1097
-
1098
- let pointsArray = this.data.quadPoints;
1099
- if (!pointsArray) {
1100
- // If there are no quadpoints, the rectangle should be used instead.
1101
- // Convert the rectangle definition to a points array similar to how the
1102
- // quadpoints are defined.
1103
- pointsArray = [
1104
- [
1105
- { x: this.rectangle[0], y: this.rectangle[3] },
1106
- { x: this.rectangle[2], y: this.rectangle[3] },
1107
- { x: this.rectangle[0], y: this.rectangle[1] },
1108
- { x: this.rectangle[2], y: this.rectangle[1] },
1109
- ],
1110
- ];
1111
- }
1112
-
1113
- for (const points of pointsArray) {
1114
- const [mX, MX, mY, MY] = pointsCallback(buffer, points);
1115
- minX = Math.min(minX, mX);
1116
- maxX = Math.max(maxX, MX);
1117
- minY = Math.min(minY, mY);
1118
- maxY = Math.max(maxY, MY);
1119
- }
1120
- buffer.push("Q");
1121
-
1122
- const formDict = new Dict(xref);
1123
- const appearanceStreamDict = new Dict(xref);
1124
- appearanceStreamDict.set("Subtype", Name.get("Form"));
1125
-
1126
- const appearanceStream = new StringStream(buffer.join(" "));
1127
- appearanceStream.dict = appearanceStreamDict;
1128
- formDict.set("Fm0", appearanceStream);
1129
-
1130
- const gsDict = new Dict(xref);
1131
- if (blendMode) {
1132
- gsDict.set("BM", Name.get(blendMode));
1133
- }
1134
- if (typeof strokeAlpha === "number") {
1135
- gsDict.set("CA", strokeAlpha);
1136
- }
1137
- if (typeof fillAlpha === "number") {
1138
- gsDict.set("ca", fillAlpha);
1139
- }
1140
-
1141
- const stateDict = new Dict(xref);
1142
- stateDict.set("GS0", gsDict);
1143
-
1144
- const resources = new Dict(xref);
1145
- resources.set("ExtGState", stateDict);
1146
- resources.set("XObject", formDict);
1147
-
1148
- const appearanceDict = new Dict(xref);
1149
- appearanceDict.set("Resources", resources);
1150
- const bbox = (this.data.rect = [minX, minY, maxX, maxY]);
1151
- appearanceDict.set("BBox", bbox);
1152
-
1153
- this.appearance = new StringStream("/GS0 gs /Fm0 Do");
1154
- this.appearance.dict = appearanceDict;
1155
-
1156
- // This method is only called if there is no appearance for the annotation,
1157
- // so `this.appearance` is not pushed yet in the `Annotation` constructor.
1158
- this._streams.push(this.appearance, appearanceStream);
1159
- }
1160
- }
1161
-
1162
- class WidgetAnnotation extends Annotation {
1163
- constructor(params) {
1164
- super(params);
1165
-
1166
- const dict = params.dict;
1167
- const data = this.data;
1168
- this.ref = params.ref;
1169
-
1170
- data.annotationType = AnnotationType.WIDGET;
1171
- if (data.fieldName === undefined) {
1172
- data.fieldName = this._constructFieldName(dict);
1173
- }
1174
- if (data.actions === undefined) {
1175
- data.actions = collectActions(
1176
- params.xref,
1177
- dict,
1178
- AnnotationActionEventType
1179
- );
1180
- }
1181
-
1182
- const fieldValue = getInheritableProperty({
1183
- dict,
1184
- key: "V",
1185
- getArray: true,
1186
- });
1187
- data.fieldValue = this._decodeFormValue(fieldValue);
1188
-
1189
- const defaultFieldValue = getInheritableProperty({
1190
- dict,
1191
- key: "DV",
1192
- getArray: true,
1193
- });
1194
- data.defaultFieldValue = this._decodeFormValue(defaultFieldValue);
1195
-
1196
- data.alternativeText = stringToPDFString(dict.get("TU") || "");
1197
-
1198
- const defaultAppearance =
1199
- getInheritableProperty({ dict, key: "DA" }) || params.acroForm.get("DA");
1200
- this._defaultAppearance = isString(defaultAppearance)
1201
- ? defaultAppearance
1202
- : "";
1203
- data.defaultAppearanceData = parseDefaultAppearance(
1204
- this._defaultAppearance
1205
- );
1206
-
1207
- const fieldType = getInheritableProperty({ dict, key: "FT" });
1208
- data.fieldType = isName(fieldType) ? fieldType.name : null;
1209
-
1210
- const localResources = getInheritableProperty({ dict, key: "DR" });
1211
- const acroFormResources = params.acroForm.get("DR");
1212
- const appearanceResources =
1213
- this.appearance && this.appearance.dict.get("Resources");
1214
-
1215
- this._fieldResources = {
1216
- localResources,
1217
- acroFormResources,
1218
- appearanceResources,
1219
- mergedResources: Dict.merge({
1220
- xref: params.xref,
1221
- dictArray: [localResources, appearanceResources, acroFormResources],
1222
- mergeSubDicts: true,
1223
- }),
1224
- };
1225
-
1226
- data.fieldFlags = getInheritableProperty({ dict, key: "Ff" });
1227
- if (!Number.isInteger(data.fieldFlags) || data.fieldFlags < 0) {
1228
- data.fieldFlags = 0;
1229
- }
1230
-
1231
- data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);
1232
- data.hidden = this._hasFlag(data.annotationFlags, AnnotationFlag.HIDDEN);
1233
- }
1234
-
1235
- /**
1236
- * Decode the given form value.
1237
- *
1238
- * @private
1239
- * @memberof WidgetAnnotation
1240
- * @param {Array<string>|Name|string} formValue - The (possibly encoded)
1241
- * form value.
1242
- * @returns {Array<string>|string|null}
1243
- */
1244
- _decodeFormValue(formValue) {
1245
- if (Array.isArray(formValue)) {
1246
- return formValue
1247
- .filter(item => isString(item))
1248
- .map(item => stringToPDFString(item));
1249
- } else if (isName(formValue)) {
1250
- return stringToPDFString(formValue.name);
1251
- } else if (isString(formValue)) {
1252
- return stringToPDFString(formValue);
1253
- }
1254
- return null;
1255
- }
1256
-
1257
- /**
1258
- * Check if a provided field flag is set.
1259
- *
1260
- * @public
1261
- * @memberof WidgetAnnotation
1262
- * @param {number} flag - Hexadecimal representation for an annotation
1263
- * field characteristic
1264
- * @returns {boolean}
1265
- * @see {@link shared/util.js}
1266
- */
1267
- hasFieldFlag(flag) {
1268
- return !!(this.data.fieldFlags & flag);
1269
- }
1270
-
1271
- getOperatorList(evaluator, task, renderForms, annotationStorage) {
1272
- // Do not render form elements on the canvas when interactive forms are
1273
- // enabled. The display layer is responsible for rendering them instead.
1274
- if (renderForms && !(this instanceof SignatureWidgetAnnotation)) {
1275
- return Promise.resolve(new OperatorList());
1276
- }
1277
-
1278
- if (!this._hasText) {
1279
- return super.getOperatorList(
1280
- evaluator,
1281
- task,
1282
- renderForms,
1283
- annotationStorage
1284
- );
1285
- }
1286
-
1287
- return this._getAppearance(evaluator, task, annotationStorage).then(
1288
- content => {
1289
- if (this.appearance && content === null) {
1290
- return super.getOperatorList(
1291
- evaluator,
1292
- task,
1293
- renderForms,
1294
- annotationStorage
1295
- );
1296
- }
1297
-
1298
- const operatorList = new OperatorList();
1299
-
1300
- // Even if there is an appearance stream, ignore it. This is the
1301
- // behaviour used by Adobe Reader.
1302
- if (!this._defaultAppearance || content === null) {
1303
- return operatorList;
1304
- }
1305
-
1306
- const matrix = [1, 0, 0, 1, 0, 0];
1307
- const bbox = [
1308
- 0,
1309
- 0,
1310
- this.data.rect[2] - this.data.rect[0],
1311
- this.data.rect[3] - this.data.rect[1],
1312
- ];
1313
-
1314
- const transform = getTransformMatrix(this.data.rect, bbox, matrix);
1315
- operatorList.addOp(OPS.beginAnnotation, [
1316
- this.data.id,
1317
- this.data.rect,
1318
- transform,
1319
- matrix,
1320
- ]);
1321
-
1322
- const stream = new StringStream(content);
1323
- return evaluator
1324
- .getOperatorList({
1325
- stream,
1326
- task,
1327
- resources: this._fieldResources.mergedResources,
1328
- operatorList,
1329
- })
1330
- .then(function () {
1331
- operatorList.addOp(OPS.endAnnotation, []);
1332
- return operatorList;
1333
- });
1334
- }
1335
- );
1336
- }
1337
-
1338
- async save(evaluator, task, annotationStorage) {
1339
- if (!annotationStorage) {
1340
- return null;
1341
- }
1342
- const storageEntry = annotationStorage.get(this.data.id);
1343
- const value = storageEntry && storageEntry.value;
1344
- if (value === this.data.fieldValue || value === undefined) {
1345
- return null;
1346
- }
1347
-
1348
- let appearance = await this._getAppearance(
1349
- evaluator,
1350
- task,
1351
- annotationStorage
1352
- );
1353
- if (appearance === null) {
1354
- return null;
1355
- }
1356
- const { xref } = evaluator;
1357
-
1358
- const dict = xref.fetchIfRef(this.ref);
1359
- if (!isDict(dict)) {
1360
- return null;
1361
- }
1362
-
1363
- const bbox = [
1364
- 0,
1365
- 0,
1366
- this.data.rect[2] - this.data.rect[0],
1367
- this.data.rect[3] - this.data.rect[1],
1368
- ];
1369
-
1370
- const xfa = {
1371
- path: stringToPDFString(dict.get("T") || ""),
1372
- value,
1373
- };
1374
-
1375
- const newRef = xref.getNewRef();
1376
- const AP = new Dict(xref);
1377
- AP.set("N", newRef);
1378
-
1379
- const encrypt = xref.encrypt;
1380
- let originalTransform = null;
1381
- let newTransform = null;
1382
- if (encrypt) {
1383
- originalTransform = encrypt.createCipherTransform(
1384
- this.ref.num,
1385
- this.ref.gen
1386
- );
1387
- newTransform = encrypt.createCipherTransform(newRef.num, newRef.gen);
1388
- appearance = newTransform.encryptString(appearance);
1389
- }
1390
-
1391
- dict.set("V", isAscii(value) ? value : stringToUTF16BEString(value));
1392
- dict.set("AP", AP);
1393
- dict.set("M", `D:${getModificationDate()}`);
1394
-
1395
- const appearanceDict = new Dict(xref);
1396
- appearanceDict.set("Length", appearance.length);
1397
- appearanceDict.set("Subtype", Name.get("Form"));
1398
- appearanceDict.set("Resources", this._getSaveFieldResources(xref));
1399
- appearanceDict.set("BBox", bbox);
1400
-
1401
- const bufferOriginal = [`${this.ref.num} ${this.ref.gen} obj\n`];
1402
- writeDict(dict, bufferOriginal, originalTransform);
1403
- bufferOriginal.push("\nendobj\n");
1404
-
1405
- const bufferNew = [`${newRef.num} ${newRef.gen} obj\n`];
1406
- writeDict(appearanceDict, bufferNew, newTransform);
1407
- bufferNew.push(" stream\n", appearance, "\nendstream\nendobj\n");
1408
-
1409
- return [
1410
- // data for the original object
1411
- // V field changed + reference for new AP
1412
- { ref: this.ref, data: bufferOriginal.join(""), xfa },
1413
- // data for the new AP
1414
- { ref: newRef, data: bufferNew.join(""), xfa: null },
1415
- ];
1416
- }
1417
-
1418
- async _getAppearance(evaluator, task, annotationStorage) {
1419
- const isPassword = this.hasFieldFlag(AnnotationFieldFlag.PASSWORD);
1420
- if (!annotationStorage || isPassword) {
1421
- return null;
1422
- }
1423
- const storageEntry = annotationStorage.get(this.data.id);
1424
- let value = storageEntry && storageEntry.value;
1425
- if (value === undefined) {
1426
- // The annotation hasn't been rendered so use the appearance
1427
- return null;
1428
- }
1429
-
1430
- value = value.trim();
1431
-
1432
- if (value === "") {
1433
- // the field is empty: nothing to render
1434
- return "";
1435
- }
1436
-
1437
- let lineCount = -1;
1438
- if (this.data.multiLine) {
1439
- lineCount = value.split(/\r\n|\r|\n/).length;
1440
- }
1441
-
1442
- const defaultPadding = 2;
1443
- const hPadding = defaultPadding;
1444
- const totalHeight = this.data.rect[3] - this.data.rect[1];
1445
- const totalWidth = this.data.rect[2] - this.data.rect[0];
1446
-
1447
- if (!this._defaultAppearance) {
1448
- // The DA is required and must be a string.
1449
- // If there is no font named Helvetica in the resource dictionary,
1450
- // the evaluator will fall back to a default font.
1451
- // Doing so prevents exceptions and allows saving/printing
1452
- // the file as expected.
1453
- this.data.defaultAppearanceData = parseDefaultAppearance(
1454
- (this._defaultAppearance = "/Helvetica 0 Tf 0 g")
1455
- );
1456
- }
1457
-
1458
- const [defaultAppearance, fontSize] = this._computeFontSize(
1459
- totalHeight,
1460
- lineCount
1461
- );
1462
-
1463
- const font = await this._getFontData(evaluator, task);
1464
-
1465
- let descent = font.descent;
1466
- if (isNaN(descent)) {
1467
- descent = 0;
1468
- }
1469
-
1470
- const vPadding = defaultPadding + Math.abs(descent) * fontSize;
1471
- const alignment = this.data.textAlignment;
1472
-
1473
- if (this.data.multiLine) {
1474
- return this._getMultilineAppearance(
1475
- defaultAppearance,
1476
- value,
1477
- font,
1478
- fontSize,
1479
- totalWidth,
1480
- totalHeight,
1481
- alignment,
1482
- hPadding,
1483
- vPadding
1484
- );
1485
- }
1486
-
1487
- // TODO: need to handle chars which are not in the font.
1488
- const encodedString = font.encodeString(value).join("");
1489
-
1490
- if (this.data.comb) {
1491
- return this._getCombAppearance(
1492
- defaultAppearance,
1493
- font,
1494
- encodedString,
1495
- totalWidth,
1496
- hPadding,
1497
- vPadding
1498
- );
1499
- }
1500
-
1501
- if (alignment === 0 || alignment > 2) {
1502
- // Left alignment: nothing to do
1503
- return (
1504
- "/Tx BMC q BT " +
1505
- defaultAppearance +
1506
- ` 1 0 0 1 ${hPadding} ${vPadding} Tm (${escapeString(
1507
- encodedString
1508
- )}) Tj` +
1509
- " ET Q EMC"
1510
- );
1511
- }
1512
-
1513
- const renderedText = this._renderText(
1514
- encodedString,
1515
- font,
1516
- fontSize,
1517
- totalWidth,
1518
- alignment,
1519
- hPadding,
1520
- vPadding
1521
- );
1522
- return (
1523
- "/Tx BMC q BT " +
1524
- defaultAppearance +
1525
- ` 1 0 0 1 0 0 Tm ${renderedText}` +
1526
- " ET Q EMC"
1527
- );
1528
- }
1529
-
1530
- async _getFontData(evaluator, task) {
1531
- const operatorList = new OperatorList();
1532
- const initialState = {
1533
- font: null,
1534
- clone() {
1535
- return this;
1536
- },
1537
- };
1538
-
1539
- const { fontName, fontSize } = this.data.defaultAppearanceData;
1540
- await evaluator.handleSetFont(
1541
- this._fieldResources.mergedResources,
1542
- [fontName && Name.get(fontName), fontSize],
1543
- /* fontRef = */ null,
1544
- operatorList,
1545
- task,
1546
- initialState,
1547
- /* fallbackFontDict = */ null
1548
- );
1549
-
1550
- return initialState.font;
1551
- }
1552
-
1553
- _computeFontSize(height, lineCount) {
1554
- let { fontSize } = this.data.defaultAppearanceData;
1555
- if (!fontSize) {
1556
- // A zero value for size means that the font shall be auto-sized:
1557
- // its size shall be computed as a function of the height of the
1558
- // annotation rectangle (see 12.7.3.3).
1559
-
1560
- const roundWithOneDigit = x => Math.round(x * 10) / 10;
1561
-
1562
- // Represent the percentage of the font size over the height
1563
- // of a single-line field.
1564
- const FONT_FACTOR = 0.8;
1565
- if (lineCount === -1) {
1566
- fontSize = roundWithOneDigit(FONT_FACTOR * height);
1567
- } else {
1568
- // Hard to guess how many lines there are.
1569
- // The field may have been sized to have 10 lines
1570
- // and the user entered only 1 so if we get font size from
1571
- // height and number of lines then we'll get something too big.
1572
- // So we compute a fake number of lines based on height and
1573
- // a font size equal to 10.
1574
- // Then we'll adjust font size to what we have really.
1575
- fontSize = 10;
1576
- let lineHeight = fontSize / FONT_FACTOR;
1577
- let numberOfLines = Math.round(height / lineHeight);
1578
- numberOfLines = Math.max(numberOfLines, lineCount);
1579
- lineHeight = height / numberOfLines;
1580
- fontSize = roundWithOneDigit(FONT_FACTOR * lineHeight);
1581
- }
1582
-
1583
- const { fontName, fontColor } = this.data.defaultAppearanceData;
1584
- this._defaultAppearance = createDefaultAppearance({
1585
- fontSize,
1586
- fontName,
1587
- fontColor,
1588
- });
1589
- }
1590
- return [this._defaultAppearance, fontSize];
1591
- }
1592
-
1593
- _renderText(text, font, fontSize, totalWidth, alignment, hPadding, vPadding) {
1594
- // We need to get the width of the text in order to align it correctly
1595
- const glyphs = font.charsToGlyphs(text);
1596
- const scale = fontSize / 1000;
1597
- let width = 0;
1598
- for (const glyph of glyphs) {
1599
- width += glyph.width * scale;
1600
- }
1601
-
1602
- let shift;
1603
- if (alignment === 1) {
1604
- // Center
1605
- shift = (totalWidth - width) / 2;
1606
- } else if (alignment === 2) {
1607
- // Right
1608
- shift = totalWidth - width - hPadding;
1609
- } else {
1610
- shift = hPadding;
1611
- }
1612
- shift = shift.toFixed(2);
1613
- vPadding = vPadding.toFixed(2);
1614
-
1615
- return `${shift} ${vPadding} Td (${escapeString(text)}) Tj`;
1616
- }
1617
-
1618
- /**
1619
- * @private
1620
- */
1621
- _getSaveFieldResources(xref) {
1622
- if (
1623
- typeof PDFJSDev === "undefined" ||
1624
- PDFJSDev.test("!PRODUCTION || TESTING")
1625
- ) {
1626
- assert(
1627
- this.data.defaultAppearanceData,
1628
- "Expected `_defaultAppearanceData` to have been set."
1629
- );
1630
- }
1631
- const { localResources, appearanceResources, acroFormResources } =
1632
- this._fieldResources;
1633
-
1634
- const fontName =
1635
- this.data.defaultAppearanceData &&
1636
- this.data.defaultAppearanceData.fontName;
1637
- if (!fontName) {
1638
- return localResources || Dict.empty;
1639
- }
1640
-
1641
- for (const resources of [localResources, appearanceResources]) {
1642
- if (resources instanceof Dict) {
1643
- const localFont = resources.get("Font");
1644
- if (localFont instanceof Dict && localFont.has(fontName)) {
1645
- return resources;
1646
- }
1647
- }
1648
- }
1649
- if (acroFormResources instanceof Dict) {
1650
- const acroFormFont = acroFormResources.get("Font");
1651
- if (acroFormFont instanceof Dict && acroFormFont.has(fontName)) {
1652
- const subFontDict = new Dict(xref);
1653
- subFontDict.set(fontName, acroFormFont.getRaw(fontName));
1654
-
1655
- const subResourcesDict = new Dict(xref);
1656
- subResourcesDict.set("Font", subFontDict);
1657
-
1658
- return Dict.merge({
1659
- xref,
1660
- dictArray: [subResourcesDict, localResources],
1661
- mergeSubDicts: true,
1662
- });
1663
- }
1664
- }
1665
- return localResources || Dict.empty;
1666
- }
1667
-
1668
- getFieldObject() {
1669
- return null;
1670
- }
1671
- }
1672
-
1673
- class TextWidgetAnnotation extends WidgetAnnotation {
1674
- constructor(params) {
1675
- super(params);
1676
-
1677
- this._hasText = true;
1678
-
1679
- const dict = params.dict;
1680
-
1681
- // The field value is always a string.
1682
- if (!isString(this.data.fieldValue)) {
1683
- this.data.fieldValue = "";
1684
- }
1685
-
1686
- // Determine the alignment of text in the field.
1687
- let alignment = getInheritableProperty({ dict, key: "Q" });
1688
- if (!Number.isInteger(alignment) || alignment < 0 || alignment > 2) {
1689
- alignment = null;
1690
- }
1691
- this.data.textAlignment = alignment;
1692
-
1693
- // Determine the maximum length of text in the field.
1694
- let maximumLength = getInheritableProperty({ dict, key: "MaxLen" });
1695
- if (!Number.isInteger(maximumLength) || maximumLength < 0) {
1696
- maximumLength = null;
1697
- }
1698
- this.data.maxLen = maximumLength;
1699
-
1700
- // Process field flags for the display layer.
1701
- this.data.multiLine = this.hasFieldFlag(AnnotationFieldFlag.MULTILINE);
1702
- this.data.comb =
1703
- this.hasFieldFlag(AnnotationFieldFlag.COMB) &&
1704
- !this.hasFieldFlag(AnnotationFieldFlag.MULTILINE) &&
1705
- !this.hasFieldFlag(AnnotationFieldFlag.PASSWORD) &&
1706
- !this.hasFieldFlag(AnnotationFieldFlag.FILESELECT) &&
1707
- this.data.maxLen !== null;
1708
- }
1709
-
1710
- _getCombAppearance(defaultAppearance, font, text, width, hPadding, vPadding) {
1711
- const combWidth = (width / this.data.maxLen).toFixed(2);
1712
- const buf = [];
1713
- const positions = font.getCharPositions(text);
1714
- for (const [start, end] of positions) {
1715
- buf.push(`(${escapeString(text.substring(start, end))}) Tj`);
1716
- }
1717
-
1718
- const renderedComb = buf.join(` ${combWidth} 0 Td `);
1719
- return (
1720
- "/Tx BMC q BT " +
1721
- defaultAppearance +
1722
- ` 1 0 0 1 ${hPadding} ${vPadding} Tm ${renderedComb}` +
1723
- " ET Q EMC"
1724
- );
1725
- }
1726
-
1727
- _getMultilineAppearance(
1728
- defaultAppearance,
1729
- text,
1730
- font,
1731
- fontSize,
1732
- width,
1733
- height,
1734
- alignment,
1735
- hPadding,
1736
- vPadding
1737
- ) {
1738
- const lines = text.split(/\r\n|\r|\n/);
1739
- const buf = [];
1740
- const totalWidth = width - 2 * hPadding;
1741
- for (const line of lines) {
1742
- const chunks = this._splitLine(line, font, fontSize, totalWidth);
1743
- for (const chunk of chunks) {
1744
- const padding = buf.length === 0 ? hPadding : 0;
1745
- buf.push(
1746
- this._renderText(
1747
- chunk,
1748
- font,
1749
- fontSize,
1750
- width,
1751
- alignment,
1752
- padding,
1753
- -fontSize // <0 because a line is below the previous one
1754
- )
1755
- );
1756
- }
1757
- }
1758
-
1759
- const renderedText = buf.join("\n");
1760
- return (
1761
- "/Tx BMC q BT " +
1762
- defaultAppearance +
1763
- ` 1 0 0 1 0 ${height} Tm ${renderedText}` +
1764
- " ET Q EMC"
1765
- );
1766
- }
1767
-
1768
- _splitLine(line, font, fontSize, width) {
1769
- // TODO: need to handle chars which are not in the font.
1770
- line = font.encodeString(line).join("");
1771
-
1772
- const glyphs = font.charsToGlyphs(line);
1773
-
1774
- if (glyphs.length <= 1) {
1775
- // Nothing to split
1776
- return [line];
1777
- }
1778
-
1779
- const positions = font.getCharPositions(line);
1780
- const scale = fontSize / 1000;
1781
- const chunks = [];
1782
-
1783
- let lastSpacePosInStringStart = -1,
1784
- lastSpacePosInStringEnd = -1,
1785
- lastSpacePos = -1,
1786
- startChunk = 0,
1787
- currentWidth = 0;
1788
-
1789
- for (let i = 0, ii = glyphs.length; i < ii; i++) {
1790
- const [start, end] = positions[i];
1791
- const glyph = glyphs[i];
1792
- const glyphWidth = glyph.width * scale;
1793
- if (glyph.unicode === " ") {
1794
- if (currentWidth + glyphWidth > width) {
1795
- // We can break here
1796
- chunks.push(line.substring(startChunk, start));
1797
- startChunk = start;
1798
- currentWidth = glyphWidth;
1799
- lastSpacePosInStringStart = -1;
1800
- lastSpacePos = -1;
1801
- } else {
1802
- currentWidth += glyphWidth;
1803
- lastSpacePosInStringStart = start;
1804
- lastSpacePosInStringEnd = end;
1805
- lastSpacePos = i;
1806
- }
1807
- } else {
1808
- if (currentWidth + glyphWidth > width) {
1809
- // We must break to the last white position (if available)
1810
- if (lastSpacePosInStringStart !== -1) {
1811
- chunks.push(line.substring(startChunk, lastSpacePosInStringEnd));
1812
- startChunk = lastSpacePosInStringEnd;
1813
- i = lastSpacePos + 1;
1814
- lastSpacePosInStringStart = -1;
1815
- currentWidth = 0;
1816
- } else {
1817
- // Just break in the middle of the word
1818
- chunks.push(line.substring(startChunk, start));
1819
- startChunk = start;
1820
- currentWidth = glyphWidth;
1821
- }
1822
- } else {
1823
- currentWidth += glyphWidth;
1824
- }
1825
- }
1826
- }
1827
-
1828
- if (startChunk < line.length) {
1829
- chunks.push(line.substring(startChunk, line.length));
1830
- }
1831
-
1832
- return chunks;
1833
- }
1834
-
1835
- getFieldObject() {
1836
- return {
1837
- id: this.data.id,
1838
- value: this.data.fieldValue,
1839
- defaultValue: this.data.defaultFieldValue,
1840
- multiline: this.data.multiLine,
1841
- password: this.hasFieldFlag(AnnotationFieldFlag.PASSWORD),
1842
- charLimit: this.data.maxLen,
1843
- comb: this.data.comb,
1844
- editable: !this.data.readOnly,
1845
- hidden: this.data.hidden,
1846
- name: this.data.fieldName,
1847
- rect: this.data.rect,
1848
- actions: this.data.actions,
1849
- page: this.data.pageIndex,
1850
- type: "text",
1851
- };
1852
- }
1853
- }
1854
-
1855
- class ButtonWidgetAnnotation extends WidgetAnnotation {
1856
- constructor(params) {
1857
- super(params);
1858
-
1859
- this.checkedAppearance = null;
1860
- this.uncheckedAppearance = null;
1861
-
1862
- this.data.checkBox =
1863
- !this.hasFieldFlag(AnnotationFieldFlag.RADIO) &&
1864
- !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON);
1865
- this.data.radioButton =
1866
- this.hasFieldFlag(AnnotationFieldFlag.RADIO) &&
1867
- !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON);
1868
- this.data.pushButton = this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON);
1869
- this.data.isTooltipOnly = false;
1870
-
1871
- if (this.data.checkBox) {
1872
- this._processCheckBox(params);
1873
- } else if (this.data.radioButton) {
1874
- this._processRadioButton(params);
1875
- } else if (this.data.pushButton) {
1876
- this._processPushButton(params);
1877
- } else {
1878
- warn("Invalid field flags for button widget annotation");
1879
- }
1880
- }
1881
-
1882
- getOperatorList(evaluator, task, renderForms, annotationStorage) {
1883
- if (this.data.pushButton) {
1884
- return super.getOperatorList(
1885
- evaluator,
1886
- task,
1887
- false, // we use normalAppearance to render the button
1888
- annotationStorage
1889
- );
1890
- }
1891
-
1892
- if (annotationStorage) {
1893
- const storageEntry = annotationStorage.get(this.data.id);
1894
- const value = storageEntry && storageEntry.value;
1895
- if (value === undefined) {
1896
- return super.getOperatorList(
1897
- evaluator,
1898
- task,
1899
- renderForms,
1900
- annotationStorage
1901
- );
1902
- }
1903
-
1904
- let appearance;
1905
- if (value) {
1906
- appearance = this.checkedAppearance;
1907
- } else {
1908
- appearance = this.uncheckedAppearance;
1909
- }
1910
-
1911
- if (appearance) {
1912
- const savedAppearance = this.appearance;
1913
- this.appearance = appearance;
1914
- const operatorList = super.getOperatorList(
1915
- evaluator,
1916
- task,
1917
- renderForms,
1918
- annotationStorage
1919
- );
1920
- this.appearance = savedAppearance;
1921
- return operatorList;
1922
- }
1923
-
1924
- // No appearance
1925
- return Promise.resolve(new OperatorList());
1926
- }
1927
- return super.getOperatorList(
1928
- evaluator,
1929
- task,
1930
- renderForms,
1931
- annotationStorage
1932
- );
1933
- }
1934
-
1935
- async save(evaluator, task, annotationStorage) {
1936
- if (this.data.checkBox) {
1937
- return this._saveCheckbox(evaluator, task, annotationStorage);
1938
- }
1939
-
1940
- if (this.data.radioButton) {
1941
- return this._saveRadioButton(evaluator, task, annotationStorage);
1942
- }
1943
-
1944
- // Nothing to save
1945
- return null;
1946
- }
1947
-
1948
- async _saveCheckbox(evaluator, task, annotationStorage) {
1949
- if (!annotationStorage) {
1950
- return null;
1951
- }
1952
- const storageEntry = annotationStorage.get(this.data.id);
1953
- const value = storageEntry && storageEntry.value;
1954
- if (value === undefined) {
1955
- return null;
1956
- }
1957
-
1958
- const defaultValue = this.data.fieldValue && this.data.fieldValue !== "Off";
1959
- if (defaultValue === value) {
1960
- return null;
1961
- }
1962
-
1963
- const dict = evaluator.xref.fetchIfRef(this.ref);
1964
- if (!isDict(dict)) {
1965
- return null;
1966
- }
1967
-
1968
- const xfa = {
1969
- path: stringToPDFString(dict.get("T") || ""),
1970
- value: value ? this.data.exportValue : "",
1971
- };
1972
-
1973
- const name = Name.get(value ? this.data.exportValue : "Off");
1974
- dict.set("V", name);
1975
- dict.set("AS", name);
1976
- dict.set("M", `D:${getModificationDate()}`);
1977
-
1978
- const encrypt = evaluator.xref.encrypt;
1979
- let originalTransform = null;
1980
- if (encrypt) {
1981
- originalTransform = encrypt.createCipherTransform(
1982
- this.ref.num,
1983
- this.ref.gen
1984
- );
1985
- }
1986
-
1987
- const buffer = [`${this.ref.num} ${this.ref.gen} obj\n`];
1988
- writeDict(dict, buffer, originalTransform);
1989
- buffer.push("\nendobj\n");
1990
-
1991
- return [{ ref: this.ref, data: buffer.join(""), xfa }];
1992
- }
1993
-
1994
- async _saveRadioButton(evaluator, task, annotationStorage) {
1995
- if (!annotationStorage) {
1996
- return null;
1997
- }
1998
- const storageEntry = annotationStorage.get(this.data.id);
1999
- const value = storageEntry && storageEntry.value;
2000
- if (value === undefined) {
2001
- return null;
2002
- }
2003
-
2004
- const defaultValue = this.data.fieldValue === this.data.buttonValue;
2005
- if (defaultValue === value) {
2006
- return null;
2007
- }
2008
-
2009
- const dict = evaluator.xref.fetchIfRef(this.ref);
2010
- if (!isDict(dict)) {
2011
- return null;
2012
- }
2013
-
2014
- const xfa = {
2015
- path: stringToPDFString(dict.get("T") || ""),
2016
- value: value ? this.data.buttonValue : "",
2017
- };
2018
-
2019
- const name = Name.get(value ? this.data.buttonValue : "Off");
2020
- let parentBuffer = null;
2021
- const encrypt = evaluator.xref.encrypt;
2022
-
2023
- if (value) {
2024
- if (isRef(this.parent)) {
2025
- const parent = evaluator.xref.fetch(this.parent);
2026
- let parentTransform = null;
2027
- if (encrypt) {
2028
- parentTransform = encrypt.createCipherTransform(
2029
- this.parent.num,
2030
- this.parent.gen
2031
- );
2032
- }
2033
- parent.set("V", name);
2034
- parentBuffer = [`${this.parent.num} ${this.parent.gen} obj\n`];
2035
- writeDict(parent, parentBuffer, parentTransform);
2036
- parentBuffer.push("\nendobj\n");
2037
- } else if (isDict(this.parent)) {
2038
- this.parent.set("V", name);
2039
- }
2040
- }
2041
-
2042
- dict.set("AS", name);
2043
- dict.set("M", `D:${getModificationDate()}`);
2044
-
2045
- let originalTransform = null;
2046
- if (encrypt) {
2047
- originalTransform = encrypt.createCipherTransform(
2048
- this.ref.num,
2049
- this.ref.gen
2050
- );
2051
- }
2052
-
2053
- const buffer = [`${this.ref.num} ${this.ref.gen} obj\n`];
2054
- writeDict(dict, buffer, originalTransform);
2055
- buffer.push("\nendobj\n");
2056
-
2057
- const newRefs = [{ ref: this.ref, data: buffer.join(""), xfa }];
2058
- if (parentBuffer !== null) {
2059
- newRefs.push({
2060
- ref: this.parent,
2061
- data: parentBuffer.join(""),
2062
- xfa: null,
2063
- });
2064
- }
2065
-
2066
- return newRefs;
2067
- }
2068
-
2069
- _processCheckBox(params) {
2070
- const customAppearance = params.dict.get("AP");
2071
- if (!isDict(customAppearance)) {
2072
- return;
2073
- }
2074
-
2075
- const normalAppearance = customAppearance.get("N");
2076
- if (!isDict(normalAppearance)) {
2077
- return;
2078
- }
2079
-
2080
- const exportValues = normalAppearance.getKeys();
2081
- if (!exportValues.includes("Off")) {
2082
- // The /Off appearance is optional.
2083
- exportValues.push("Off");
2084
- }
2085
- // Don't use a "V" entry pointing to a non-existent appearance state,
2086
- // see e.g. bug1720411.pdf where it's an *empty* Name-instance.
2087
- if (!exportValues.includes(this.data.fieldValue)) {
2088
- this.data.fieldValue = null;
2089
- }
2090
- if (exportValues.length !== 2) {
2091
- return;
2092
- }
2093
-
2094
- this.data.exportValue =
2095
- exportValues[0] === "Off" ? exportValues[1] : exportValues[0];
2096
-
2097
- this.checkedAppearance = normalAppearance.get(this.data.exportValue);
2098
- this.uncheckedAppearance = normalAppearance.get("Off") || null;
2099
-
2100
- this._streams.push(this.checkedAppearance);
2101
- if (this.uncheckedAppearance) {
2102
- this._streams.push(this.uncheckedAppearance);
2103
- }
2104
- this._fallbackFontDict = this.fallbackFontDict;
2105
- }
2106
-
2107
- _processRadioButton(params) {
2108
- this.data.fieldValue = this.data.buttonValue = null;
2109
-
2110
- // The parent field's `V` entry holds a `Name` object with the appearance
2111
- // state of whichever child field is currently in the "on" state.
2112
- const fieldParent = params.dict.get("Parent");
2113
- if (isDict(fieldParent)) {
2114
- this.parent = params.dict.getRaw("Parent");
2115
- const fieldParentValue = fieldParent.get("V");
2116
- if (isName(fieldParentValue)) {
2117
- this.data.fieldValue = this._decodeFormValue(fieldParentValue);
2118
- }
2119
- }
2120
-
2121
- // The button's value corresponds to its appearance state.
2122
- const appearanceStates = params.dict.get("AP");
2123
- if (!isDict(appearanceStates)) {
2124
- return;
2125
- }
2126
- const normalAppearance = appearanceStates.get("N");
2127
- if (!isDict(normalAppearance)) {
2128
- return;
2129
- }
2130
- for (const key of normalAppearance.getKeys()) {
2131
- if (key !== "Off") {
2132
- this.data.buttonValue = this._decodeFormValue(key);
2133
- break;
2134
- }
2135
- }
2136
-
2137
- this.checkedAppearance = normalAppearance.get(this.data.buttonValue);
2138
- this.uncheckedAppearance = normalAppearance.get("Off") || null;
2139
-
2140
- this._streams.push(this.checkedAppearance);
2141
- if (this.uncheckedAppearance) {
2142
- this._streams.push(this.uncheckedAppearance);
2143
- }
2144
- this._fallbackFontDict = this.fallbackFontDict;
2145
- }
2146
-
2147
- _processPushButton(params) {
2148
- if (
2149
- !params.dict.has("A") &&
2150
- !params.dict.has("AA") &&
2151
- !this.data.alternativeText
2152
- ) {
2153
- warn("Push buttons without action dictionaries are not supported");
2154
- return;
2155
- }
2156
-
2157
- this.data.isTooltipOnly = !params.dict.has("A") && !params.dict.has("AA");
2158
-
2159
- Catalog.parseDestDictionary({
2160
- destDict: params.dict,
2161
- resultObj: this.data,
2162
- docBaseUrl: params.pdfManager.docBaseUrl,
2163
- });
2164
- }
2165
-
2166
- getFieldObject() {
2167
- let type = "button";
2168
- let exportValues;
2169
- if (this.data.checkBox) {
2170
- type = "checkbox";
2171
- exportValues = this.data.exportValue;
2172
- } else if (this.data.radioButton) {
2173
- type = "radiobutton";
2174
- exportValues = this.data.buttonValue;
2175
- }
2176
- return {
2177
- id: this.data.id,
2178
- value: this.data.fieldValue || "Off",
2179
- defaultValue: this.data.defaultFieldValue,
2180
- exportValues,
2181
- editable: !this.data.readOnly,
2182
- name: this.data.fieldName,
2183
- rect: this.data.rect,
2184
- hidden: this.data.hidden,
2185
- actions: this.data.actions,
2186
- page: this.data.pageIndex,
2187
- type,
2188
- };
2189
- }
2190
-
2191
- get fallbackFontDict() {
2192
- const dict = new Dict();
2193
- dict.set("BaseFont", Name.get("ZapfDingbats"));
2194
- dict.set("Type", Name.get("FallbackType"));
2195
- dict.set("Subtype", Name.get("FallbackType"));
2196
- dict.set("Encoding", Name.get("ZapfDingbatsEncoding"));
2197
-
2198
- return shadow(this, "fallbackFontDict", dict);
2199
- }
2200
- }
2201
-
2202
- class ChoiceWidgetAnnotation extends WidgetAnnotation {
2203
- constructor(params) {
2204
- super(params);
2205
-
2206
- // Determine the options. The options array may consist of strings or
2207
- // arrays. If the array consists of arrays, then the first element of
2208
- // each array is the export value and the second element of each array is
2209
- // the display value. If the array consists of strings, then these
2210
- // represent both the export and display value. In this case, we convert
2211
- // it to an array of arrays as well for convenience in the display layer.
2212
- // Note that the specification does not state that the `Opt` field is
2213
- // inheritable, but in practice PDF generators do make annotations
2214
- // inherit the options from a parent annotation (issue 8094).
2215
- this.data.options = [];
2216
-
2217
- const options = getInheritableProperty({ dict: params.dict, key: "Opt" });
2218
- if (Array.isArray(options)) {
2219
- const xref = params.xref;
2220
- for (let i = 0, ii = options.length; i < ii; i++) {
2221
- const option = xref.fetchIfRef(options[i]);
2222
- const isOptionArray = Array.isArray(option);
2223
-
2224
- this.data.options[i] = {
2225
- exportValue: this._decodeFormValue(
2226
- isOptionArray ? xref.fetchIfRef(option[0]) : option
2227
- ),
2228
- displayValue: this._decodeFormValue(
2229
- isOptionArray ? xref.fetchIfRef(option[1]) : option
2230
- ),
2231
- };
2232
- }
2233
- }
2234
-
2235
- // The field value can be `null` if no item is selected, a string if one
2236
- // item is selected or an array of strings if multiple items are selected.
2237
- // For consistency in the API and convenience in the display layer, we
2238
- // always make the field value an array with zero, one or multiple items.
2239
- if (isString(this.data.fieldValue)) {
2240
- this.data.fieldValue = [this.data.fieldValue];
2241
- } else if (!this.data.fieldValue) {
2242
- this.data.fieldValue = [];
2243
- }
2244
-
2245
- // Process field flags for the display layer.
2246
- this.data.combo = this.hasFieldFlag(AnnotationFieldFlag.COMBO);
2247
- this.data.multiSelect = this.hasFieldFlag(AnnotationFieldFlag.MULTISELECT);
2248
- this._hasText = true;
2249
- }
2250
-
2251
- getFieldObject() {
2252
- const type = this.data.combo ? "combobox" : "listbox";
2253
- const value =
2254
- this.data.fieldValue.length > 0 ? this.data.fieldValue[0] : null;
2255
- return {
2256
- id: this.data.id,
2257
- value,
2258
- defaultValue: this.data.defaultFieldValue,
2259
- editable: !this.data.readOnly,
2260
- name: this.data.fieldName,
2261
- rect: this.data.rect,
2262
- numItems: this.data.fieldValue.length,
2263
- multipleSelection: this.data.multiSelect,
2264
- hidden: this.data.hidden,
2265
- actions: this.data.actions,
2266
- items: this.data.options,
2267
- page: this.data.pageIndex,
2268
- type,
2269
- };
2270
- }
2271
- }
2272
-
2273
- class SignatureWidgetAnnotation extends WidgetAnnotation {
2274
- constructor(params) {
2275
- super(params);
2276
-
2277
- // Unset the fieldValue since it's (most likely) a `Dict` which is
2278
- // non-serializable and will thus cause errors when sending annotations
2279
- // to the main-thread (issue 10347).
2280
- this.data.fieldValue = null;
2281
- }
2282
-
2283
- getFieldObject() {
2284
- return {
2285
- id: this.data.id,
2286
- value: null,
2287
- page: this.data.pageIndex,
2288
- type: "signature",
2289
- };
2290
- }
2291
- }
2292
-
2293
- class TextAnnotation extends MarkupAnnotation {
2294
- constructor(parameters) {
2295
- const DEFAULT_ICON_SIZE = 22; // px
2296
-
2297
- super(parameters);
2298
-
2299
- const dict = parameters.dict;
2300
- this.data.annotationType = AnnotationType.TEXT;
2301
-
2302
- if (this.data.hasAppearance) {
2303
- this.data.name = "NoIcon";
2304
- } else {
2305
- this.data.rect[1] = this.data.rect[3] - DEFAULT_ICON_SIZE;
2306
- this.data.rect[2] = this.data.rect[0] + DEFAULT_ICON_SIZE;
2307
- this.data.name = dict.has("Name") ? dict.get("Name").name : "Note";
2308
- }
2309
-
2310
- if (dict.has("State")) {
2311
- this.data.state = dict.get("State") || null;
2312
- this.data.stateModel = dict.get("StateModel") || null;
2313
- } else {
2314
- this.data.state = null;
2315
- this.data.stateModel = null;
2316
- }
2317
- }
2318
- }
2319
-
2320
- class LinkAnnotation extends Annotation {
2321
- constructor(params) {
2322
- super(params);
2323
-
2324
- this.data.annotationType = AnnotationType.LINK;
2325
-
2326
- const quadPoints = getQuadPoints(params.dict, this.rectangle);
2327
- if (quadPoints) {
2328
- this.data.quadPoints = quadPoints;
2329
- }
2330
-
2331
- Catalog.parseDestDictionary({
2332
- destDict: params.dict,
2333
- resultObj: this.data,
2334
- docBaseUrl: params.pdfManager.docBaseUrl,
2335
- });
2336
- }
2337
- }
2338
-
2339
- class PopupAnnotation extends Annotation {
2340
- constructor(parameters) {
2341
- super(parameters);
2342
-
2343
- this.data.annotationType = AnnotationType.POPUP;
2344
-
2345
- let parentItem = parameters.dict.get("Parent");
2346
- if (!parentItem) {
2347
- warn("Popup annotation has a missing or invalid parent annotation.");
2348
- return;
2349
- }
2350
-
2351
- const parentSubtype = parentItem.get("Subtype");
2352
- this.data.parentType = isName(parentSubtype) ? parentSubtype.name : null;
2353
- const rawParent = parameters.dict.getRaw("Parent");
2354
- this.data.parentId = isRef(rawParent) ? rawParent.toString() : null;
2355
-
2356
- const parentRect = parentItem.getArray("Rect");
2357
- if (Array.isArray(parentRect) && parentRect.length === 4) {
2358
- this.data.parentRect = Util.normalizeRect(parentRect);
2359
- } else {
2360
- this.data.parentRect = [0, 0, 0, 0];
2361
- }
2362
-
2363
- const rt = parentItem.get("RT");
2364
- if (isName(rt, AnnotationReplyType.GROUP)) {
2365
- // Subordinate annotations in a group should inherit
2366
- // the group attributes from the primary annotation.
2367
- parentItem = parentItem.get("IRT");
2368
- }
2369
-
2370
- if (!parentItem.has("M")) {
2371
- this.data.modificationDate = null;
2372
- } else {
2373
- this.setModificationDate(parentItem.get("M"));
2374
- this.data.modificationDate = this.modificationDate;
2375
- }
2376
-
2377
- if (!parentItem.has("C")) {
2378
- // Fall back to the default background color.
2379
- this.data.color = null;
2380
- } else {
2381
- this.setColor(parentItem.getArray("C"));
2382
- this.data.color = this.color;
2383
- }
2384
-
2385
- // If the Popup annotation is not viewable, but the parent annotation is,
2386
- // that is most likely a bug. Fallback to inherit the flags from the parent
2387
- // annotation (this is consistent with the behaviour in Adobe Reader).
2388
- if (!this.viewable) {
2389
- const parentFlags = parentItem.get("F");
2390
- if (this._isViewable(parentFlags)) {
2391
- this.setFlags(parentFlags);
2392
- }
2393
- }
2394
-
2395
- this.data.title = stringToPDFString(parentItem.get("T") || "");
2396
- this.data.contents = stringToPDFString(parentItem.get("Contents") || "");
2397
- }
2398
- }
2399
-
2400
- class FreeTextAnnotation extends MarkupAnnotation {
2401
- constructor(parameters) {
2402
- super(parameters);
2403
-
2404
- this.data.annotationType = AnnotationType.FREETEXT;
2405
- }
2406
- }
2407
-
2408
- class LineAnnotation extends MarkupAnnotation {
2409
- constructor(parameters) {
2410
- super(parameters);
2411
-
2412
- this.data.annotationType = AnnotationType.LINE;
2413
-
2414
- const lineCoordinates = parameters.dict.getArray("L");
2415
- this.data.lineCoordinates = Util.normalizeRect(lineCoordinates);
2416
-
2417
- if (!this.appearance) {
2418
- // The default stroke color is black.
2419
- const strokeColor = this.color
2420
- ? Array.from(this.color).map(c => c / 255)
2421
- : [0, 0, 0];
2422
- const strokeAlpha = parameters.dict.get("CA");
2423
-
2424
- // The default fill color is transparent. Setting the fill colour is
2425
- // necessary if/when we want to add support for non-default line endings.
2426
- let fillColor = null,
2427
- interiorColor = parameters.dict.getArray("IC");
2428
- if (interiorColor) {
2429
- interiorColor = getRgbColor(interiorColor);
2430
- fillColor = interiorColor
2431
- ? Array.from(interiorColor).map(c => c / 255)
2432
- : null;
2433
- }
2434
- const fillAlpha = fillColor ? strokeAlpha : null;
2435
-
2436
- const borderWidth = this.borderStyle.width || 1,
2437
- borderAdjust = 2 * borderWidth;
2438
-
2439
- // If the /Rect-entry is empty/wrong, create a fallback rectangle so that
2440
- // we get similar rendering/highlighting behaviour as in Adobe Reader.
2441
- const bbox = [
2442
- this.data.lineCoordinates[0] - borderAdjust,
2443
- this.data.lineCoordinates[1] - borderAdjust,
2444
- this.data.lineCoordinates[2] + borderAdjust,
2445
- this.data.lineCoordinates[3] + borderAdjust,
2446
- ];
2447
- if (!Util.intersect(this.rectangle, bbox)) {
2448
- this.rectangle = bbox;
2449
- }
2450
-
2451
- this._setDefaultAppearance({
2452
- xref: parameters.xref,
2453
- extra: `${borderWidth} w`,
2454
- strokeColor,
2455
- fillColor,
2456
- strokeAlpha,
2457
- fillAlpha,
2458
- pointsCallback: (buffer, points) => {
2459
- buffer.push(
2460
- `${lineCoordinates[0]} ${lineCoordinates[1]} m`,
2461
- `${lineCoordinates[2]} ${lineCoordinates[3]} l`,
2462
- "S"
2463
- );
2464
- return [
2465
- points[0].x - borderWidth,
2466
- points[1].x + borderWidth,
2467
- points[3].y - borderWidth,
2468
- points[1].y + borderWidth,
2469
- ];
2470
- },
2471
- });
2472
- }
2473
- }
2474
- }
2475
-
2476
- class SquareAnnotation extends MarkupAnnotation {
2477
- constructor(parameters) {
2478
- super(parameters);
2479
-
2480
- this.data.annotationType = AnnotationType.SQUARE;
2481
-
2482
- if (!this.appearance) {
2483
- // The default stroke color is black.
2484
- const strokeColor = this.color
2485
- ? Array.from(this.color).map(c => c / 255)
2486
- : [0, 0, 0];
2487
- const strokeAlpha = parameters.dict.get("CA");
2488
-
2489
- // The default fill color is transparent.
2490
- let fillColor = null,
2491
- interiorColor = parameters.dict.getArray("IC");
2492
- if (interiorColor) {
2493
- interiorColor = getRgbColor(interiorColor);
2494
- fillColor = interiorColor
2495
- ? Array.from(interiorColor).map(c => c / 255)
2496
- : null;
2497
- }
2498
- const fillAlpha = fillColor ? strokeAlpha : null;
2499
-
2500
- this._setDefaultAppearance({
2501
- xref: parameters.xref,
2502
- extra: `${this.borderStyle.width} w`,
2503
- strokeColor,
2504
- fillColor,
2505
- strokeAlpha,
2506
- fillAlpha,
2507
- pointsCallback: (buffer, points) => {
2508
- const x = points[2].x + this.borderStyle.width / 2;
2509
- const y = points[2].y + this.borderStyle.width / 2;
2510
- const width = points[3].x - points[2].x - this.borderStyle.width;
2511
- const height = points[1].y - points[3].y - this.borderStyle.width;
2512
- buffer.push(`${x} ${y} ${width} ${height} re`);
2513
- if (fillColor) {
2514
- buffer.push("B");
2515
- } else {
2516
- buffer.push("S");
2517
- }
2518
- return [points[0].x, points[1].x, points[3].y, points[1].y];
2519
- },
2520
- });
2521
- }
2522
- }
2523
- }
2524
-
2525
- class CircleAnnotation extends MarkupAnnotation {
2526
- constructor(parameters) {
2527
- super(parameters);
2528
-
2529
- this.data.annotationType = AnnotationType.CIRCLE;
2530
-
2531
- if (!this.appearance) {
2532
- // The default stroke color is black.
2533
- const strokeColor = this.color
2534
- ? Array.from(this.color).map(c => c / 255)
2535
- : [0, 0, 0];
2536
- const strokeAlpha = parameters.dict.get("CA");
2537
-
2538
- // The default fill color is transparent.
2539
- let fillColor = null;
2540
- let interiorColor = parameters.dict.getArray("IC");
2541
- if (interiorColor) {
2542
- interiorColor = getRgbColor(interiorColor);
2543
- fillColor = interiorColor
2544
- ? Array.from(interiorColor).map(c => c / 255)
2545
- : null;
2546
- }
2547
- const fillAlpha = fillColor ? strokeAlpha : null;
2548
-
2549
- // Circles are approximated by Bézier curves with four segments since
2550
- // there is no circle primitive in the PDF specification. For the control
2551
- // points distance, see https://stackoverflow.com/a/27863181.
2552
- const controlPointsDistance = (4 / 3) * Math.tan(Math.PI / (2 * 4));
2553
-
2554
- this._setDefaultAppearance({
2555
- xref: parameters.xref,
2556
- extra: `${this.borderStyle.width} w`,
2557
- strokeColor,
2558
- fillColor,
2559
- strokeAlpha,
2560
- fillAlpha,
2561
- pointsCallback: (buffer, points) => {
2562
- const x0 = points[0].x + this.borderStyle.width / 2;
2563
- const y0 = points[0].y - this.borderStyle.width / 2;
2564
- const x1 = points[3].x - this.borderStyle.width / 2;
2565
- const y1 = points[3].y + this.borderStyle.width / 2;
2566
- const xMid = x0 + (x1 - x0) / 2;
2567
- const yMid = y0 + (y1 - y0) / 2;
2568
- const xOffset = ((x1 - x0) / 2) * controlPointsDistance;
2569
- const yOffset = ((y1 - y0) / 2) * controlPointsDistance;
2570
-
2571
- buffer.push(
2572
- `${xMid} ${y1} m`,
2573
- `${xMid + xOffset} ${y1} ${x1} ${yMid + yOffset} ${x1} ${yMid} c`,
2574
- `${x1} ${yMid - yOffset} ${xMid + xOffset} ${y0} ${xMid} ${y0} c`,
2575
- `${xMid - xOffset} ${y0} ${x0} ${yMid - yOffset} ${x0} ${yMid} c`,
2576
- `${x0} ${yMid + yOffset} ${xMid - xOffset} ${y1} ${xMid} ${y1} c`,
2577
- "h"
2578
- );
2579
- if (fillColor) {
2580
- buffer.push("B");
2581
- } else {
2582
- buffer.push("S");
2583
- }
2584
- return [points[0].x, points[1].x, points[3].y, points[1].y];
2585
- },
2586
- });
2587
- }
2588
- }
2589
- }
2590
-
2591
- class PolylineAnnotation extends MarkupAnnotation {
2592
- constructor(parameters) {
2593
- super(parameters);
2594
-
2595
- this.data.annotationType = AnnotationType.POLYLINE;
2596
- this.data.vertices = [];
2597
-
2598
- // The vertices array is an array of numbers representing the alternating
2599
- // horizontal and vertical coordinates, respectively, of each vertex.
2600
- // Convert this to an array of objects with x and y coordinates.
2601
- const rawVertices = parameters.dict.getArray("Vertices");
2602
- if (!Array.isArray(rawVertices)) {
2603
- return;
2604
- }
2605
- for (let i = 0, ii = rawVertices.length; i < ii; i += 2) {
2606
- this.data.vertices.push({
2607
- x: rawVertices[i],
2608
- y: rawVertices[i + 1],
2609
- });
2610
- }
2611
-
2612
- if (!this.appearance) {
2613
- // The default stroke color is black.
2614
- const strokeColor = this.color
2615
- ? Array.from(this.color).map(c => c / 255)
2616
- : [0, 0, 0];
2617
- const strokeAlpha = parameters.dict.get("CA");
2618
-
2619
- const borderWidth = this.borderStyle.width || 1,
2620
- borderAdjust = 2 * borderWidth;
2621
-
2622
- // If the /Rect-entry is empty/wrong, create a fallback rectangle so that
2623
- // we get similar rendering/highlighting behaviour as in Adobe Reader.
2624
- const bbox = [Infinity, Infinity, -Infinity, -Infinity];
2625
- for (const vertex of this.data.vertices) {
2626
- bbox[0] = Math.min(bbox[0], vertex.x - borderAdjust);
2627
- bbox[1] = Math.min(bbox[1], vertex.y - borderAdjust);
2628
- bbox[2] = Math.max(bbox[2], vertex.x + borderAdjust);
2629
- bbox[3] = Math.max(bbox[3], vertex.y + borderAdjust);
2630
- }
2631
- if (!Util.intersect(this.rectangle, bbox)) {
2632
- this.rectangle = bbox;
2633
- }
2634
-
2635
- this._setDefaultAppearance({
2636
- xref: parameters.xref,
2637
- extra: `${borderWidth} w`,
2638
- strokeColor,
2639
- strokeAlpha,
2640
- pointsCallback: (buffer, points) => {
2641
- const vertices = this.data.vertices;
2642
- for (let i = 0, ii = vertices.length; i < ii; i++) {
2643
- buffer.push(
2644
- `${vertices[i].x} ${vertices[i].y} ${i === 0 ? "m" : "l"}`
2645
- );
2646
- }
2647
- buffer.push("S");
2648
- return [points[0].x, points[1].x, points[3].y, points[1].y];
2649
- },
2650
- });
2651
- }
2652
- }
2653
- }
2654
-
2655
- class PolygonAnnotation extends PolylineAnnotation {
2656
- constructor(parameters) {
2657
- // Polygons are specific forms of polylines, so reuse their logic.
2658
- super(parameters);
2659
-
2660
- this.data.annotationType = AnnotationType.POLYGON;
2661
- }
2662
- }
2663
-
2664
- class CaretAnnotation extends MarkupAnnotation {
2665
- constructor(parameters) {
2666
- super(parameters);
2667
-
2668
- this.data.annotationType = AnnotationType.CARET;
2669
- }
2670
- }
2671
-
2672
- class InkAnnotation extends MarkupAnnotation {
2673
- constructor(parameters) {
2674
- super(parameters);
2675
-
2676
- this.data.annotationType = AnnotationType.INK;
2677
- this.data.inkLists = [];
2678
-
2679
- const rawInkLists = parameters.dict.getArray("InkList");
2680
- if (!Array.isArray(rawInkLists)) {
2681
- return;
2682
- }
2683
- const xref = parameters.xref;
2684
- for (let i = 0, ii = rawInkLists.length; i < ii; ++i) {
2685
- // The raw ink lists array contains arrays of numbers representing
2686
- // the alternating horizontal and vertical coordinates, respectively,
2687
- // of each vertex. Convert this to an array of objects with x and y
2688
- // coordinates.
2689
- this.data.inkLists.push([]);
2690
- for (let j = 0, jj = rawInkLists[i].length; j < jj; j += 2) {
2691
- this.data.inkLists[i].push({
2692
- x: xref.fetchIfRef(rawInkLists[i][j]),
2693
- y: xref.fetchIfRef(rawInkLists[i][j + 1]),
2694
- });
2695
- }
2696
- }
2697
-
2698
- if (!this.appearance) {
2699
- // The default stroke color is black.
2700
- const strokeColor = this.color
2701
- ? Array.from(this.color).map(c => c / 255)
2702
- : [0, 0, 0];
2703
- const strokeAlpha = parameters.dict.get("CA");
2704
-
2705
- const borderWidth = this.borderStyle.width || 1,
2706
- borderAdjust = 2 * borderWidth;
2707
-
2708
- // If the /Rect-entry is empty/wrong, create a fallback rectangle so that
2709
- // we get similar rendering/highlighting behaviour as in Adobe Reader.
2710
- const bbox = [Infinity, Infinity, -Infinity, -Infinity];
2711
- for (const inkLists of this.data.inkLists) {
2712
- for (const vertex of inkLists) {
2713
- bbox[0] = Math.min(bbox[0], vertex.x - borderAdjust);
2714
- bbox[1] = Math.min(bbox[1], vertex.y - borderAdjust);
2715
- bbox[2] = Math.max(bbox[2], vertex.x + borderAdjust);
2716
- bbox[3] = Math.max(bbox[3], vertex.y + borderAdjust);
2717
- }
2718
- }
2719
- if (!Util.intersect(this.rectangle, bbox)) {
2720
- this.rectangle = bbox;
2721
- }
2722
-
2723
- this._setDefaultAppearance({
2724
- xref: parameters.xref,
2725
- extra: `${borderWidth} w`,
2726
- strokeColor,
2727
- strokeAlpha,
2728
- pointsCallback: (buffer, points) => {
2729
- // According to the specification, see "12.5.6.13 Ink Annotations":
2730
- // When drawn, the points shall be connected by straight lines or
2731
- // curves in an implementation-dependent way.
2732
- // In order to simplify things, we utilize straight lines for now.
2733
- for (const inkList of this.data.inkLists) {
2734
- for (let i = 0, ii = inkList.length; i < ii; i++) {
2735
- buffer.push(
2736
- `${inkList[i].x} ${inkList[i].y} ${i === 0 ? "m" : "l"}`
2737
- );
2738
- }
2739
- buffer.push("S");
2740
- }
2741
- return [points[0].x, points[1].x, points[3].y, points[1].y];
2742
- },
2743
- });
2744
- }
2745
- }
2746
- }
2747
-
2748
- class HighlightAnnotation extends MarkupAnnotation {
2749
- constructor(parameters) {
2750
- super(parameters);
2751
-
2752
- this.data.annotationType = AnnotationType.HIGHLIGHT;
2753
- const quadPoints = (this.data.quadPoints = getQuadPoints(
2754
- parameters.dict,
2755
- null
2756
- ));
2757
- if (quadPoints) {
2758
- const resources =
2759
- this.appearance && this.appearance.dict.get("Resources");
2760
-
2761
- if (!this.appearance || !(resources && resources.has("ExtGState"))) {
2762
- if (this.appearance) {
2763
- // Workaround for cases where there's no /ExtGState-entry directly
2764
- // available, e.g. when the appearance stream contains a /XObject of
2765
- // the /Form-type, since that causes the highlighting to completely
2766
- // obsure the PDF content below it (fixes issue13242.pdf).
2767
- warn("HighlightAnnotation - ignoring built-in appearance stream.");
2768
- }
2769
- // Default color is yellow in Acrobat Reader
2770
- const fillColor = this.color
2771
- ? Array.from(this.color).map(c => c / 255)
2772
- : [1, 1, 0];
2773
- const fillAlpha = parameters.dict.get("CA");
2774
-
2775
- this._setDefaultAppearance({
2776
- xref: parameters.xref,
2777
- fillColor,
2778
- blendMode: "Multiply",
2779
- fillAlpha,
2780
- pointsCallback: (buffer, points) => {
2781
- buffer.push(
2782
- `${points[0].x} ${points[0].y} m`,
2783
- `${points[1].x} ${points[1].y} l`,
2784
- `${points[3].x} ${points[3].y} l`,
2785
- `${points[2].x} ${points[2].y} l`,
2786
- "f"
2787
- );
2788
- return [points[0].x, points[1].x, points[3].y, points[1].y];
2789
- },
2790
- });
2791
- }
2792
- } else {
2793
- this.data.hasPopup = false;
2794
- }
2795
- }
2796
- }
2797
-
2798
- class UnderlineAnnotation extends MarkupAnnotation {
2799
- constructor(parameters) {
2800
- super(parameters);
2801
-
2802
- this.data.annotationType = AnnotationType.UNDERLINE;
2803
- const quadPoints = (this.data.quadPoints = getQuadPoints(
2804
- parameters.dict,
2805
- null
2806
- ));
2807
- if (quadPoints) {
2808
- if (!this.appearance) {
2809
- // Default color is black
2810
- const strokeColor = this.color
2811
- ? Array.from(this.color).map(c => c / 255)
2812
- : [0, 0, 0];
2813
- const strokeAlpha = parameters.dict.get("CA");
2814
-
2815
- this._setDefaultAppearance({
2816
- xref: parameters.xref,
2817
- extra: "[] 0 d 1 w",
2818
- strokeColor,
2819
- strokeAlpha,
2820
- pointsCallback: (buffer, points) => {
2821
- buffer.push(
2822
- `${points[2].x} ${points[2].y} m`,
2823
- `${points[3].x} ${points[3].y} l`,
2824
- "S"
2825
- );
2826
- return [points[0].x, points[1].x, points[3].y, points[1].y];
2827
- },
2828
- });
2829
- }
2830
- } else {
2831
- this.data.hasPopup = false;
2832
- }
2833
- }
2834
- }
2835
-
2836
- class SquigglyAnnotation extends MarkupAnnotation {
2837
- constructor(parameters) {
2838
- super(parameters);
2839
-
2840
- this.data.annotationType = AnnotationType.SQUIGGLY;
2841
-
2842
- const quadPoints = (this.data.quadPoints = getQuadPoints(
2843
- parameters.dict,
2844
- null
2845
- ));
2846
- if (quadPoints) {
2847
- if (!this.appearance) {
2848
- // Default color is black
2849
- const strokeColor = this.color
2850
- ? Array.from(this.color).map(c => c / 255)
2851
- : [0, 0, 0];
2852
- const strokeAlpha = parameters.dict.get("CA");
2853
-
2854
- this._setDefaultAppearance({
2855
- xref: parameters.xref,
2856
- extra: "[] 0 d 1 w",
2857
- strokeColor,
2858
- strokeAlpha,
2859
- pointsCallback: (buffer, points) => {
2860
- const dy = (points[0].y - points[2].y) / 6;
2861
- let shift = dy;
2862
- let x = points[2].x;
2863
- const y = points[2].y;
2864
- const xEnd = points[3].x;
2865
- buffer.push(`${x} ${y + shift} m`);
2866
- do {
2867
- x += 2;
2868
- shift = shift === 0 ? dy : 0;
2869
- buffer.push(`${x} ${y + shift} l`);
2870
- } while (x < xEnd);
2871
- buffer.push("S");
2872
- return [points[2].x, xEnd, y - 2 * dy, y + 2 * dy];
2873
- },
2874
- });
2875
- }
2876
- } else {
2877
- this.data.hasPopup = false;
2878
- }
2879
- }
2880
- }
2881
-
2882
- class StrikeOutAnnotation extends MarkupAnnotation {
2883
- constructor(parameters) {
2884
- super(parameters);
2885
-
2886
- this.data.annotationType = AnnotationType.STRIKEOUT;
2887
-
2888
- const quadPoints = (this.data.quadPoints = getQuadPoints(
2889
- parameters.dict,
2890
- null
2891
- ));
2892
- if (quadPoints) {
2893
- if (!this.appearance) {
2894
- // Default color is black
2895
- const strokeColor = this.color
2896
- ? Array.from(this.color).map(c => c / 255)
2897
- : [0, 0, 0];
2898
- const strokeAlpha = parameters.dict.get("CA");
2899
-
2900
- this._setDefaultAppearance({
2901
- xref: parameters.xref,
2902
- extra: "[] 0 d 1 w",
2903
- strokeColor,
2904
- strokeAlpha,
2905
- pointsCallback: (buffer, points) => {
2906
- buffer.push(
2907
- `${(points[0].x + points[2].x) / 2} ` +
2908
- `${(points[0].y + points[2].y) / 2} m`,
2909
- `${(points[1].x + points[3].x) / 2} ` +
2910
- `${(points[1].y + points[3].y) / 2} l`,
2911
- "S"
2912
- );
2913
- return [points[0].x, points[1].x, points[3].y, points[1].y];
2914
- },
2915
- });
2916
- }
2917
- } else {
2918
- this.data.hasPopup = false;
2919
- }
2920
- }
2921
- }
2922
-
2923
- class StampAnnotation extends MarkupAnnotation {
2924
- constructor(parameters) {
2925
- super(parameters);
2926
-
2927
- this.data.annotationType = AnnotationType.STAMP;
2928
- }
2929
- }
2930
-
2931
- class FileAttachmentAnnotation extends MarkupAnnotation {
2932
- constructor(parameters) {
2933
- super(parameters);
2934
-
2935
- const file = new FileSpec(parameters.dict.get("FS"), parameters.xref);
2936
-
2937
- this.data.annotationType = AnnotationType.FILEATTACHMENT;
2938
- this.data.file = file.serializable;
2939
- }
2940
- }
2941
-
2942
- export {
2943
- Annotation,
2944
- AnnotationBorderStyle,
2945
- AnnotationFactory,
2946
- getQuadPoints,
2947
- MarkupAnnotation,
2948
- };