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

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 (172) hide show
  1. package/README.md +1 -1
  2. package/package.json +42 -26
  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/l10n_utils.js +140 -0
  6. package/{src/shared → pdfjs/dist}/message_handler.js +243 -259
  7. package/{src/display → pdfjs/dist}/network.js +149 -87
  8. package/{src/display/content_disposition.js → pdfjs/dist/network_utils.js} +167 -55
  9. package/{src/display → pdfjs/dist}/node_stream.js +133 -98
  10. package/pdfjs/dist/pdf.js +12778 -0
  11. package/pdfjs/dist/pdf_link_service.js +638 -0
  12. package/pdfjs/dist/pdf_rendering_queue.js +199 -0
  13. package/pdfjs/dist/pdf_thumbnail_viewer.js +819 -0
  14. package/pdfjs/dist/pdf_viewer.js +3598 -0
  15. package/pdfjs/dist/ui_utils.js +1033 -0
  16. package/{src/shared → pdfjs/dist}/util.js +301 -287
  17. package/pdfjs/dist/worker.js +62813 -0
  18. package/src/vcf-pdf-viewer.js +98 -46
  19. package/theme/lumo/vcf-pdf-viewer-styles.js +4 -4
  20. package/theme/material/vcf-pdf-viewer-styles.js +4 -4
  21. package/theme/material/vcf-pdf-viewer.js +2 -2
  22. package/src/core/.eslintrc +0 -13
  23. package/src/core/annotation.js +0 -2948
  24. package/src/core/arithmetic_decoder.js +0 -182
  25. package/src/core/ascii_85_stream.js +0 -98
  26. package/src/core/ascii_hex_stream.js +0 -79
  27. package/src/core/base_stream.js +0 -110
  28. package/src/core/bidi.js +0 -438
  29. package/src/core/calibri_factors.js +0 -308
  30. package/src/core/catalog.js +0 -1459
  31. package/src/core/ccitt.js +0 -1062
  32. package/src/core/ccitt_stream.js +0 -60
  33. package/src/core/cff_font.js +0 -116
  34. package/src/core/cff_parser.js +0 -1949
  35. package/src/core/charsets.js +0 -119
  36. package/src/core/chunked_stream.js +0 -557
  37. package/src/core/cmap.js +0 -1039
  38. package/src/core/colorspace.js +0 -1533
  39. package/src/core/core_utils.js +0 -464
  40. package/src/core/crypto.js +0 -1900
  41. package/src/core/decode_stream.js +0 -170
  42. package/src/core/decrypt_stream.js +0 -59
  43. package/src/core/default_appearance.js +0 -99
  44. package/src/core/document.js +0 -1456
  45. package/src/core/encodings.js +0 -301
  46. package/src/core/evaluator.js +0 -4601
  47. package/src/core/file_spec.js +0 -108
  48. package/src/core/flate_stream.js +0 -402
  49. package/src/core/font_renderer.js +0 -882
  50. package/src/core/fonts.js +0 -3260
  51. package/src/core/fonts_utils.js +0 -221
  52. package/src/core/function.js +0 -1257
  53. package/src/core/glyf.js +0 -706
  54. package/src/core/glyphlist.js +0 -4558
  55. package/src/core/helvetica_factors.js +0 -353
  56. package/src/core/image.js +0 -802
  57. package/src/core/image_utils.js +0 -291
  58. package/src/core/jbig2.js +0 -2572
  59. package/src/core/jbig2_stream.js +0 -73
  60. package/src/core/jpeg_stream.js +0 -105
  61. package/src/core/jpg.js +0 -1416
  62. package/src/core/jpx.js +0 -2343
  63. package/src/core/jpx_stream.js +0 -87
  64. package/src/core/liberationsans_widths.js +0 -221
  65. package/src/core/lzw_stream.js +0 -150
  66. package/src/core/metadata_parser.js +0 -146
  67. package/src/core/metrics.js +0 -2970
  68. package/src/core/murmurhash3.js +0 -139
  69. package/src/core/myriadpro_factors.js +0 -290
  70. package/src/core/name_number_tree.js +0 -153
  71. package/src/core/object_loader.js +0 -149
  72. package/src/core/opentype_file_builder.js +0 -154
  73. package/src/core/operator_list.js +0 -734
  74. package/src/core/parser.js +0 -1416
  75. package/src/core/pattern.js +0 -985
  76. package/src/core/pdf_manager.js +0 -217
  77. package/src/core/predictor_stream.js +0 -238
  78. package/src/core/primitives.js +0 -402
  79. package/src/core/ps_parser.js +0 -272
  80. package/src/core/run_length_stream.js +0 -61
  81. package/src/core/segoeui_factors.js +0 -308
  82. package/src/core/standard_fonts.js +0 -817
  83. package/src/core/stream.js +0 -103
  84. package/src/core/struct_tree.js +0 -335
  85. package/src/core/to_unicode_map.js +0 -103
  86. package/src/core/type1_font.js +0 -421
  87. package/src/core/type1_parser.js +0 -776
  88. package/src/core/unicode.js +0 -1649
  89. package/src/core/worker.js +0 -848
  90. package/src/core/worker_stream.js +0 -135
  91. package/src/core/writer.js +0 -278
  92. package/src/core/xfa/bind.js +0 -652
  93. package/src/core/xfa/builder.js +0 -207
  94. package/src/core/xfa/config.js +0 -1926
  95. package/src/core/xfa/connection_set.js +0 -202
  96. package/src/core/xfa/data.js +0 -82
  97. package/src/core/xfa/datasets.js +0 -76
  98. package/src/core/xfa/factory.js +0 -111
  99. package/src/core/xfa/fonts.js +0 -181
  100. package/src/core/xfa/formcalc_lexer.js +0 -385
  101. package/src/core/xfa/formcalc_parser.js +0 -1340
  102. package/src/core/xfa/html_utils.js +0 -639
  103. package/src/core/xfa/layout.js +0 -383
  104. package/src/core/xfa/locale_set.js +0 -345
  105. package/src/core/xfa/namespaces.js +0 -81
  106. package/src/core/xfa/parser.js +0 -184
  107. package/src/core/xfa/setup.js +0 -38
  108. package/src/core/xfa/signature.js +0 -40
  109. package/src/core/xfa/som.js +0 -338
  110. package/src/core/xfa/stylesheet.js +0 -40
  111. package/src/core/xfa/template.js +0 -6260
  112. package/src/core/xfa/text.js +0 -290
  113. package/src/core/xfa/unknown.js +0 -29
  114. package/src/core/xfa/utils.js +0 -217
  115. package/src/core/xfa/xdp.js +0 -59
  116. package/src/core/xfa/xfa_object.js +0 -1130
  117. package/src/core/xfa/xhtml.js +0 -543
  118. package/src/core/xfa_fonts.js +0 -208
  119. package/src/core/xml_parser.js +0 -507
  120. package/src/core/xref.js +0 -899
  121. package/src/display/annotation_layer.js +0 -2107
  122. package/src/display/annotation_storage.js +0 -113
  123. package/src/display/api.js +0 -3292
  124. package/src/display/base_factory.js +0 -180
  125. package/src/display/canvas.js +0 -2828
  126. package/src/display/font_loader.js +0 -484
  127. package/src/display/metadata.js +0 -41
  128. package/src/display/network_utils.js +0 -100
  129. package/src/display/node_utils.js +0 -83
  130. package/src/display/optional_content_config.js +0 -189
  131. package/src/display/pattern_helper.js +0 -659
  132. package/src/display/svg.js +0 -1709
  133. package/src/display/text_layer.js +0 -847
  134. package/src/display/transport_stream.js +0 -303
  135. package/src/display/worker_options.js +0 -40
  136. package/src/display/xfa_layer.js +0 -204
  137. package/src/doc_helper.js +0 -25
  138. package/src/images/logo.svg +0 -41
  139. package/src/interfaces.js +0 -169
  140. package/src/license_header.js +0 -14
  141. package/src/license_header_libre.js +0 -21
  142. package/src/pdf.image_decoders.js +0 -46
  143. package/src/pdf.js +0 -146
  144. package/src/pdf.sandbox.external.js +0 -181
  145. package/src/pdf.sandbox.js +0 -151
  146. package/src/pdf.scripting.js +0 -25
  147. package/src/pdf.worker.entry.js +0 -19
  148. package/src/pdf.worker.js +0 -23
  149. package/src/scripting_api/aform.js +0 -608
  150. package/src/scripting_api/app.js +0 -621
  151. package/src/scripting_api/color.js +0 -129
  152. package/src/scripting_api/common.js +0 -58
  153. package/src/scripting_api/console.js +0 -38
  154. package/src/scripting_api/constants.js +0 -208
  155. package/src/scripting_api/doc.js +0 -1195
  156. package/src/scripting_api/error.js +0 -23
  157. package/src/scripting_api/event.js +0 -232
  158. package/src/scripting_api/field.js +0 -620
  159. package/src/scripting_api/fullscreen.js +0 -145
  160. package/src/scripting_api/initialization.js +0 -223
  161. package/src/scripting_api/pdf_object.js +0 -24
  162. package/src/scripting_api/print_params.js +0 -146
  163. package/src/scripting_api/proxy.js +0 -139
  164. package/src/scripting_api/thermometer.js +0 -69
  165. package/src/scripting_api/util.js +0 -581
  166. package/src/shared/.eslintrc +0 -13
  167. package/src/shared/cffStandardStrings.js +0 -311
  168. package/src/shared/compatibility.js +0 -114
  169. package/src/shared/fonts_utils.js +0 -429
  170. package/src/shared/is_node.js +0 -27
  171. package/src/shared/scripting_utils.js +0 -85
  172. package/src/worker_loader.js +0 -32
@@ -1,2107 +0,0 @@
1
- /* Copyright 2014 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
- addLinkAttributes,
18
- DOMSVGFactory,
19
- getFilenameFromUrl,
20
- LinkTarget,
21
- PDFDateString,
22
- } from "./display_utils.js";
23
- import {
24
- AnnotationBorderStyleType,
25
- AnnotationType,
26
- assert,
27
- stringToPDFString,
28
- unreachable,
29
- Util,
30
- warn,
31
- } from "../shared/util.js";
32
- import { AnnotationStorage } from "./annotation_storage.js";
33
- import { ColorConverters } from "../shared/scripting_utils.js";
34
-
35
- /**
36
- * @typedef {Object} AnnotationElementParameters
37
- * @property {Object} data
38
- * @property {HTMLDivElement} layer
39
- * @property {PDFPage} page
40
- * @property {PageViewport} viewport
41
- * @property {IPDFLinkService} linkService
42
- * @property {DownloadManager} downloadManager
43
- * @property {AnnotationStorage} [annotationStorage]
44
- * @property {string} [imageResourcesPath] - Path for image resources, mainly
45
- * for annotation icons. Include trailing slash.
46
- * @property {boolean} renderInteractiveForms
47
- * @property {Object} svgFactory
48
- * @property {boolean} [enableScripting]
49
- * @property {boolean} [hasJSActions]
50
- * @property {Object} [mouseState]
51
- */
52
-
53
- class AnnotationElementFactory {
54
- /**
55
- * @param {AnnotationElementParameters} parameters
56
- * @returns {AnnotationElement}
57
- */
58
- static create(parameters) {
59
- const subtype = parameters.data.annotationType;
60
-
61
- switch (subtype) {
62
- case AnnotationType.LINK:
63
- return new LinkAnnotationElement(parameters);
64
-
65
- case AnnotationType.TEXT:
66
- return new TextAnnotationElement(parameters);
67
-
68
- case AnnotationType.WIDGET:
69
- const fieldType = parameters.data.fieldType;
70
-
71
- switch (fieldType) {
72
- case "Tx":
73
- return new TextWidgetAnnotationElement(parameters);
74
- case "Btn":
75
- if (parameters.data.radioButton) {
76
- return new RadioButtonWidgetAnnotationElement(parameters);
77
- } else if (parameters.data.checkBox) {
78
- return new CheckboxWidgetAnnotationElement(parameters);
79
- }
80
- return new PushButtonWidgetAnnotationElement(parameters);
81
- case "Ch":
82
- return new ChoiceWidgetAnnotationElement(parameters);
83
- }
84
- return new WidgetAnnotationElement(parameters);
85
-
86
- case AnnotationType.POPUP:
87
- return new PopupAnnotationElement(parameters);
88
-
89
- case AnnotationType.FREETEXT:
90
- return new FreeTextAnnotationElement(parameters);
91
-
92
- case AnnotationType.LINE:
93
- return new LineAnnotationElement(parameters);
94
-
95
- case AnnotationType.SQUARE:
96
- return new SquareAnnotationElement(parameters);
97
-
98
- case AnnotationType.CIRCLE:
99
- return new CircleAnnotationElement(parameters);
100
-
101
- case AnnotationType.POLYLINE:
102
- return new PolylineAnnotationElement(parameters);
103
-
104
- case AnnotationType.CARET:
105
- return new CaretAnnotationElement(parameters);
106
-
107
- case AnnotationType.INK:
108
- return new InkAnnotationElement(parameters);
109
-
110
- case AnnotationType.POLYGON:
111
- return new PolygonAnnotationElement(parameters);
112
-
113
- case AnnotationType.HIGHLIGHT:
114
- return new HighlightAnnotationElement(parameters);
115
-
116
- case AnnotationType.UNDERLINE:
117
- return new UnderlineAnnotationElement(parameters);
118
-
119
- case AnnotationType.SQUIGGLY:
120
- return new SquigglyAnnotationElement(parameters);
121
-
122
- case AnnotationType.STRIKEOUT:
123
- return new StrikeOutAnnotationElement(parameters);
124
-
125
- case AnnotationType.STAMP:
126
- return new StampAnnotationElement(parameters);
127
-
128
- case AnnotationType.FILEATTACHMENT:
129
- return new FileAttachmentAnnotationElement(parameters);
130
-
131
- default:
132
- return new AnnotationElement(parameters);
133
- }
134
- }
135
- }
136
-
137
- class AnnotationElement {
138
- constructor(
139
- parameters,
140
- {
141
- isRenderable = false,
142
- ignoreBorder = false,
143
- createQuadrilaterals = false,
144
- } = {}
145
- ) {
146
- this.isRenderable = isRenderable;
147
- this.data = parameters.data;
148
- this.layer = parameters.layer;
149
- this.page = parameters.page;
150
- this.viewport = parameters.viewport;
151
- this.linkService = parameters.linkService;
152
- this.downloadManager = parameters.downloadManager;
153
- this.imageResourcesPath = parameters.imageResourcesPath;
154
- this.renderInteractiveForms = parameters.renderInteractiveForms;
155
- this.svgFactory = parameters.svgFactory;
156
- this.annotationStorage = parameters.annotationStorage;
157
- this.enableScripting = parameters.enableScripting;
158
- this.hasJSActions = parameters.hasJSActions;
159
- this._mouseState = parameters.mouseState;
160
-
161
- if (isRenderable) {
162
- this.container = this._createContainer(ignoreBorder);
163
- }
164
- if (createQuadrilaterals) {
165
- this.quadrilaterals = this._createQuadrilaterals(ignoreBorder);
166
- }
167
- }
168
-
169
- /**
170
- * Create an empty container for the annotation's HTML element.
171
- *
172
- * @private
173
- * @param {boolean} ignoreBorder
174
- * @memberof AnnotationElement
175
- * @returns {HTMLSectionElement}
176
- */
177
- _createContainer(ignoreBorder = false) {
178
- const data = this.data,
179
- page = this.page,
180
- viewport = this.viewport;
181
- const container = document.createElement("section");
182
- let width = data.rect[2] - data.rect[0];
183
- let height = data.rect[3] - data.rect[1];
184
-
185
- container.setAttribute("data-annotation-id", data.id);
186
-
187
- // Do *not* modify `data.rect`, since that will corrupt the annotation
188
- // position on subsequent calls to `_createContainer` (see issue 6804).
189
- const rect = Util.normalizeRect([
190
- data.rect[0],
191
- page.view[3] - data.rect[1] + page.view[1],
192
- data.rect[2],
193
- page.view[3] - data.rect[3] + page.view[1],
194
- ]);
195
-
196
- container.style.transform = `matrix(${viewport.transform.join(",")})`;
197
- container.style.transformOrigin = `${-rect[0]}px ${-rect[1]}px`;
198
-
199
- if (!ignoreBorder && data.borderStyle.width > 0) {
200
- container.style.borderWidth = `${data.borderStyle.width}px`;
201
- if (data.borderStyle.style !== AnnotationBorderStyleType.UNDERLINE) {
202
- // Underline styles only have a bottom border, so we do not need
203
- // to adjust for all borders. This yields a similar result as
204
- // Adobe Acrobat/Reader.
205
- width -= 2 * data.borderStyle.width;
206
- height -= 2 * data.borderStyle.width;
207
- }
208
-
209
- const horizontalRadius = data.borderStyle.horizontalCornerRadius;
210
- const verticalRadius = data.borderStyle.verticalCornerRadius;
211
- if (horizontalRadius > 0 || verticalRadius > 0) {
212
- const radius = `${horizontalRadius}px / ${verticalRadius}px`;
213
- container.style.borderRadius = radius;
214
- }
215
-
216
- switch (data.borderStyle.style) {
217
- case AnnotationBorderStyleType.SOLID:
218
- container.style.borderStyle = "solid";
219
- break;
220
-
221
- case AnnotationBorderStyleType.DASHED:
222
- container.style.borderStyle = "dashed";
223
- break;
224
-
225
- case AnnotationBorderStyleType.BEVELED:
226
- warn("Unimplemented border style: beveled");
227
- break;
228
-
229
- case AnnotationBorderStyleType.INSET:
230
- warn("Unimplemented border style: inset");
231
- break;
232
-
233
- case AnnotationBorderStyleType.UNDERLINE:
234
- container.style.borderBottomStyle = "solid";
235
- break;
236
-
237
- default:
238
- break;
239
- }
240
-
241
- if (data.color) {
242
- container.style.borderColor = Util.makeHexColor(
243
- data.color[0] | 0,
244
- data.color[1] | 0,
245
- data.color[2] | 0
246
- );
247
- } else {
248
- // Transparent (invisible) border, so do not draw it at all.
249
- container.style.borderWidth = 0;
250
- }
251
- }
252
-
253
- container.style.left = `${rect[0]}px`;
254
- container.style.top = `${rect[1]}px`;
255
- container.style.width = `${width}px`;
256
- container.style.height = `${height}px`;
257
- return container;
258
- }
259
-
260
- /**
261
- * Create quadrilaterals from the annotation's quadpoints.
262
- *
263
- * @private
264
- * @param {boolean} ignoreBorder
265
- * @memberof AnnotationElement
266
- * @returns {Array<HTMLSectionElement>}
267
- */
268
- _createQuadrilaterals(ignoreBorder = false) {
269
- if (!this.data.quadPoints) {
270
- return null;
271
- }
272
-
273
- const quadrilaterals = [];
274
- const savedRect = this.data.rect;
275
- for (const quadPoint of this.data.quadPoints) {
276
- this.data.rect = [
277
- quadPoint[2].x,
278
- quadPoint[2].y,
279
- quadPoint[1].x,
280
- quadPoint[1].y,
281
- ];
282
- quadrilaterals.push(this._createContainer(ignoreBorder));
283
- }
284
- this.data.rect = savedRect;
285
- return quadrilaterals;
286
- }
287
-
288
- /**
289
- * Create a popup for the annotation's HTML element. This is used for
290
- * annotations that do not have a Popup entry in the dictionary, but
291
- * are of a type that works with popups (such as Highlight annotations).
292
- *
293
- * @private
294
- * @param {HTMLDivElement|HTMLImageElement|null} trigger
295
- * @param {Object} data
296
- * @memberof AnnotationElement
297
- */
298
- _createPopup(trigger, data) {
299
- let container = this.container;
300
- if (this.quadrilaterals) {
301
- trigger = trigger || this.quadrilaterals;
302
- container = this.quadrilaterals[0];
303
- }
304
-
305
- // If no trigger element is specified, create it.
306
- if (!trigger) {
307
- trigger = document.createElement("div");
308
- trigger.style.height = container.style.height;
309
- trigger.style.width = container.style.width;
310
- container.appendChild(trigger);
311
- }
312
-
313
- const popupElement = new PopupElement({
314
- container,
315
- trigger,
316
- color: data.color,
317
- title: data.title,
318
- modificationDate: data.modificationDate,
319
- contents: data.contents,
320
- hideWrapper: true,
321
- });
322
- const popup = popupElement.render();
323
-
324
- // Position the popup next to the annotation's container.
325
- popup.style.left = container.style.width;
326
-
327
- container.appendChild(popup);
328
- }
329
-
330
- /**
331
- * Render the quadrilaterals of the annotation.
332
- *
333
- * @private
334
- * @param {string} className
335
- * @memberof AnnotationElement
336
- * @returns {Array<HTMLSectionElement>}
337
- */
338
- _renderQuadrilaterals(className) {
339
- if (
340
- typeof PDFJSDev === "undefined" ||
341
- PDFJSDev.test("!PRODUCTION || TESTING")
342
- ) {
343
- assert(this.quadrilaterals, "Missing quadrilaterals during rendering");
344
- }
345
-
346
- for (const quadrilateral of this.quadrilaterals) {
347
- quadrilateral.className = className;
348
- }
349
- return this.quadrilaterals;
350
- }
351
-
352
- /**
353
- * Render the annotation's HTML element(s).
354
- *
355
- * @public
356
- * @memberof AnnotationElement
357
- * @returns {HTMLSectionElement|Array<HTMLSectionElement>}
358
- */
359
- render() {
360
- unreachable("Abstract method `AnnotationElement.render` called");
361
- }
362
- }
363
-
364
- class LinkAnnotationElement extends AnnotationElement {
365
- constructor(parameters) {
366
- const isRenderable = !!(
367
- parameters.data.url ||
368
- parameters.data.dest ||
369
- parameters.data.action ||
370
- parameters.data.isTooltipOnly ||
371
- (parameters.data.actions &&
372
- (parameters.data.actions.Action ||
373
- parameters.data.actions["Mouse Up"] ||
374
- parameters.data.actions["Mouse Down"]))
375
- );
376
- super(parameters, { isRenderable, createQuadrilaterals: true });
377
- }
378
-
379
- render() {
380
- const { data, linkService } = this;
381
- const link = document.createElement("a");
382
-
383
- if (data.url) {
384
- addLinkAttributes(link, {
385
- url: data.url,
386
- target: data.newWindow
387
- ? LinkTarget.BLANK
388
- : linkService.externalLinkTarget,
389
- rel: linkService.externalLinkRel,
390
- enabled: linkService.externalLinkEnabled,
391
- });
392
- } else if (data.action) {
393
- this._bindNamedAction(link, data.action);
394
- } else if (data.dest) {
395
- this._bindLink(link, data.dest);
396
- } else if (
397
- data.actions &&
398
- (data.actions.Action ||
399
- data.actions["Mouse Up"] ||
400
- data.actions["Mouse Down"]) &&
401
- this.enableScripting &&
402
- this.hasJSActions
403
- ) {
404
- this._bindJSAction(link, data);
405
- } else {
406
- this._bindLink(link, "");
407
- }
408
-
409
- if (this.quadrilaterals) {
410
- return this._renderQuadrilaterals("linkAnnotation").map(
411
- (quadrilateral, index) => {
412
- const linkElement = index === 0 ? link : link.cloneNode();
413
- quadrilateral.appendChild(linkElement);
414
- return quadrilateral;
415
- }
416
- );
417
- }
418
-
419
- this.container.className = "linkAnnotation";
420
- this.container.appendChild(link);
421
- return this.container;
422
- }
423
-
424
- /**
425
- * Bind internal links to the link element.
426
- *
427
- * @private
428
- * @param {Object} link
429
- * @param {Object} destination
430
- * @memberof LinkAnnotationElement
431
- */
432
- _bindLink(link, destination) {
433
- link.href = this.linkService.getDestinationHash(destination);
434
- link.onclick = () => {
435
- if (destination) {
436
- this.linkService.goToDestination(destination);
437
- }
438
- return false;
439
- };
440
- if (destination || destination === /* isTooltipOnly = */ "") {
441
- link.className = "internalLink";
442
- }
443
- }
444
-
445
- /**
446
- * Bind named actions to the link element.
447
- *
448
- * @private
449
- * @param {Object} link
450
- * @param {Object} action
451
- * @memberof LinkAnnotationElement
452
- */
453
- _bindNamedAction(link, action) {
454
- link.href = this.linkService.getAnchorUrl("");
455
- link.onclick = () => {
456
- this.linkService.executeNamedAction(action);
457
- return false;
458
- };
459
- link.className = "internalLink";
460
- }
461
-
462
- /**
463
- * Bind JS actions to the link element.
464
- *
465
- * @private
466
- * @param {Object} link
467
- * @param {Object} data
468
- * @memberof LinkAnnotationElement
469
- */
470
- _bindJSAction(link, data) {
471
- link.href = this.linkService.getAnchorUrl("");
472
- const map = new Map([
473
- ["Action", "onclick"],
474
- ["Mouse Up", "onmouseup"],
475
- ["Mouse Down", "onmousedown"],
476
- ]);
477
- for (const name of Object.keys(data.actions)) {
478
- const jsName = map.get(name);
479
- if (!jsName) {
480
- continue;
481
- }
482
- link[jsName] = () => {
483
- this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
484
- source: this,
485
- detail: {
486
- id: data.id,
487
- name,
488
- },
489
- });
490
- return false;
491
- };
492
- }
493
- link.className = "internalLink";
494
- }
495
- }
496
-
497
- class TextAnnotationElement extends AnnotationElement {
498
- constructor(parameters) {
499
- const isRenderable = !!(
500
- parameters.data.hasPopup ||
501
- parameters.data.title ||
502
- parameters.data.contents
503
- );
504
- super(parameters, { isRenderable });
505
- }
506
-
507
- render() {
508
- this.container.className = "textAnnotation";
509
-
510
- const image = document.createElement("img");
511
- image.style.height = this.container.style.height;
512
- image.style.width = this.container.style.width;
513
- image.src =
514
- this.imageResourcesPath +
515
- "annotation-" +
516
- this.data.name.toLowerCase() +
517
- ".svg";
518
- image.alt = "[{{type}} Annotation]";
519
- image.dataset.l10nId = "text_annotation_type";
520
- image.dataset.l10nArgs = JSON.stringify({ type: this.data.name });
521
-
522
- if (!this.data.hasPopup) {
523
- this._createPopup(image, this.data);
524
- }
525
-
526
- this.container.appendChild(image);
527
- return this.container;
528
- }
529
- }
530
-
531
- class WidgetAnnotationElement extends AnnotationElement {
532
- render() {
533
- // Show only the container for unsupported field types.
534
- if (this.data.alternativeText) {
535
- this.container.title = this.data.alternativeText;
536
- }
537
-
538
- return this.container;
539
- }
540
-
541
- _getKeyModifier(event) {
542
- return (
543
- (navigator.platform.includes("Win") && event.ctrlKey) ||
544
- (navigator.platform.includes("Mac") && event.metaKey)
545
- );
546
- }
547
-
548
- _setEventListener(element, baseName, eventName, valueGetter) {
549
- if (baseName.includes("mouse")) {
550
- // Mouse events
551
- element.addEventListener(baseName, event => {
552
- this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
553
- source: this,
554
- detail: {
555
- id: this.data.id,
556
- name: eventName,
557
- value: valueGetter(event),
558
- shift: event.shiftKey,
559
- modifier: this._getKeyModifier(event),
560
- },
561
- });
562
- });
563
- } else {
564
- // Non mouse event
565
- element.addEventListener(baseName, event => {
566
- this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
567
- source: this,
568
- detail: {
569
- id: this.data.id,
570
- name: eventName,
571
- value: event.target.checked,
572
- },
573
- });
574
- });
575
- }
576
- }
577
-
578
- _setEventListeners(element, names, getter) {
579
- for (const [baseName, eventName] of names) {
580
- if (eventName === "Action" || this.data.actions?.[eventName]) {
581
- this._setEventListener(element, baseName, eventName, getter);
582
- }
583
- }
584
- }
585
-
586
- _dispatchEventFromSandbox(actions, jsEvent) {
587
- const setColor = (jsName, styleName, event) => {
588
- const color = event.detail[jsName];
589
- event.target.style[styleName] = ColorConverters[`${color[0]}_HTML`](
590
- color.slice(1)
591
- );
592
- };
593
-
594
- const commonActions = {
595
- display: event => {
596
- const hidden = event.detail.display % 2 === 1;
597
- event.target.style.visibility = hidden ? "hidden" : "visible";
598
- this.annotationStorage.setValue(this.data.id, {
599
- hidden,
600
- print: event.detail.display === 0 || event.detail.display === 3,
601
- });
602
- },
603
- print: event => {
604
- this.annotationStorage.setValue(this.data.id, {
605
- print: event.detail.print,
606
- });
607
- },
608
- hidden: event => {
609
- event.target.style.visibility = event.detail.hidden
610
- ? "hidden"
611
- : "visible";
612
- this.annotationStorage.setValue(this.data.id, {
613
- hidden: event.detail.hidden,
614
- });
615
- },
616
- focus: event => {
617
- setTimeout(() => event.target.focus({ preventScroll: false }), 0);
618
- },
619
- userName: event => {
620
- // tooltip
621
- event.target.title = event.detail.userName;
622
- },
623
- readonly: event => {
624
- if (event.detail.readonly) {
625
- event.target.setAttribute("readonly", "");
626
- } else {
627
- event.target.removeAttribute("readonly");
628
- }
629
- },
630
- required: event => {
631
- if (event.detail.required) {
632
- event.target.setAttribute("required", "");
633
- } else {
634
- event.target.removeAttribute("required");
635
- }
636
- },
637
- bgColor: event => {
638
- setColor("bgColor", "backgroundColor", event);
639
- },
640
- fillColor: event => {
641
- setColor("fillColor", "backgroundColor", event);
642
- },
643
- fgColor: event => {
644
- setColor("fgColor", "color", event);
645
- },
646
- textColor: event => {
647
- setColor("textColor", "color", event);
648
- },
649
- borderColor: event => {
650
- setColor("borderColor", "borderColor", event);
651
- },
652
- strokeColor: event => {
653
- setColor("strokeColor", "borderColor", event);
654
- },
655
- };
656
-
657
- for (const name of Object.keys(jsEvent.detail)) {
658
- const action = actions[name] || commonActions[name];
659
- if (action) {
660
- action(jsEvent);
661
- }
662
- }
663
- }
664
- }
665
-
666
- class TextWidgetAnnotationElement extends WidgetAnnotationElement {
667
- constructor(parameters) {
668
- const isRenderable =
669
- parameters.renderInteractiveForms ||
670
- (!parameters.data.hasAppearance && !!parameters.data.fieldValue);
671
- super(parameters, { isRenderable });
672
- }
673
-
674
- setPropertyOnSiblings(base, key, value, keyInStorage) {
675
- const storage = this.annotationStorage;
676
- for (const element of document.getElementsByName(base.name)) {
677
- if (element !== base) {
678
- element[key] = value;
679
- const data = Object.create(null);
680
- data[keyInStorage] = value;
681
- storage.setValue(element.getAttribute("id"), data);
682
- }
683
- }
684
- }
685
-
686
- render() {
687
- const storage = this.annotationStorage;
688
- const id = this.data.id;
689
-
690
- this.container.className = "textWidgetAnnotation";
691
-
692
- let element = null;
693
- if (this.renderInteractiveForms) {
694
- // NOTE: We cannot set the values using `element.value` below, since it
695
- // prevents the AnnotationLayer rasterizer in `test/driver.js`
696
- // from parsing the elements correctly for the reference tests.
697
- const storedData = storage.getValue(id, {
698
- value: this.data.fieldValue,
699
- valueAsString: this.data.fieldValue,
700
- });
701
- const textContent = storedData.valueAsString || storedData.value || "";
702
- const elementData = {
703
- userValue: null,
704
- formattedValue: null,
705
- beforeInputSelectionRange: null,
706
- beforeInputValue: null,
707
- };
708
-
709
- if (this.data.multiLine) {
710
- element = document.createElement("textarea");
711
- element.textContent = textContent;
712
- } else {
713
- element = document.createElement("input");
714
- element.type = "text";
715
- element.setAttribute("value", textContent);
716
- }
717
-
718
- elementData.userValue = textContent;
719
- element.setAttribute("id", id);
720
-
721
- element.addEventListener("input", event => {
722
- storage.setValue(id, { value: event.target.value });
723
- this.setPropertyOnSiblings(
724
- element,
725
- "value",
726
- event.target.value,
727
- "value"
728
- );
729
- });
730
-
731
- let blurListener = event => {
732
- if (elementData.formattedValue) {
733
- event.target.value = elementData.formattedValue;
734
- }
735
- // Reset the cursor position to the start of the field (issue 12359).
736
- event.target.scrollLeft = 0;
737
- elementData.beforeInputSelectionRange = null;
738
- };
739
-
740
- if (this.enableScripting && this.hasJSActions) {
741
- element.addEventListener("focus", event => {
742
- if (elementData.userValue) {
743
- event.target.value = elementData.userValue;
744
- }
745
- });
746
-
747
- element.addEventListener("updatefromsandbox", jsEvent => {
748
- const actions = {
749
- value(event) {
750
- elementData.userValue = event.detail.value || "";
751
- storage.setValue(id, { value: elementData.userValue.toString() });
752
- if (!elementData.formattedValue) {
753
- event.target.value = elementData.userValue;
754
- }
755
- },
756
- valueAsString(event) {
757
- elementData.formattedValue = event.detail.valueAsString || "";
758
- if (event.target !== document.activeElement) {
759
- // Input hasn't the focus so display formatted string
760
- event.target.value = elementData.formattedValue;
761
- }
762
- storage.setValue(id, {
763
- formattedValue: elementData.formattedValue,
764
- });
765
- },
766
- selRange(event) {
767
- const [selStart, selEnd] = event.detail.selRange;
768
- if (selStart >= 0 && selEnd < event.target.value.length) {
769
- event.target.setSelectionRange(selStart, selEnd);
770
- }
771
- },
772
- };
773
- this._dispatchEventFromSandbox(actions, jsEvent);
774
- });
775
-
776
- // Even if the field hasn't any actions
777
- // leaving it can still trigger some actions with Calculate
778
- element.addEventListener("keydown", event => {
779
- elementData.beforeInputValue = event.target.value;
780
- // if the key is one of Escape, Enter or Tab
781
- // then the data are committed
782
- let commitKey = -1;
783
- if (event.key === "Escape") {
784
- commitKey = 0;
785
- } else if (event.key === "Enter") {
786
- commitKey = 2;
787
- } else if (event.key === "Tab") {
788
- commitKey = 3;
789
- }
790
- if (commitKey === -1) {
791
- return;
792
- }
793
- // Save the entered value
794
- elementData.userValue = event.target.value;
795
- this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
796
- source: this,
797
- detail: {
798
- id,
799
- name: "Keystroke",
800
- value: event.target.value,
801
- willCommit: true,
802
- commitKey,
803
- selStart: event.target.selectionStart,
804
- selEnd: event.target.selectionEnd,
805
- },
806
- });
807
- });
808
- const _blurListener = blurListener;
809
- blurListener = null;
810
- element.addEventListener("blur", event => {
811
- if (this._mouseState.isDown) {
812
- // Focus out using the mouse: data are committed
813
- elementData.userValue = event.target.value;
814
- this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
815
- source: this,
816
- detail: {
817
- id,
818
- name: "Keystroke",
819
- value: event.target.value,
820
- willCommit: true,
821
- commitKey: 1,
822
- selStart: event.target.selectionStart,
823
- selEnd: event.target.selectionEnd,
824
- },
825
- });
826
- }
827
- _blurListener(event);
828
- });
829
- element.addEventListener("mousedown", event => {
830
- elementData.beforeInputValue = event.target.value;
831
- elementData.beforeInputSelectionRange = null;
832
- });
833
- element.addEventListener("keyup", event => {
834
- // keyup is triggered after input
835
- if (event.target.selectionStart === event.target.selectionEnd) {
836
- elementData.beforeInputSelectionRange = null;
837
- }
838
- });
839
- element.addEventListener("select", event => {
840
- elementData.beforeInputSelectionRange = [
841
- event.target.selectionStart,
842
- event.target.selectionEnd,
843
- ];
844
- });
845
-
846
- if (this.data.actions?.Keystroke) {
847
- // We should use beforeinput but this
848
- // event isn't available in Firefox
849
- element.addEventListener("input", event => {
850
- let selStart = -1;
851
- let selEnd = -1;
852
- if (elementData.beforeInputSelectionRange) {
853
- [selStart, selEnd] = elementData.beforeInputSelectionRange;
854
- }
855
- this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
856
- source: this,
857
- detail: {
858
- id,
859
- name: "Keystroke",
860
- value: elementData.beforeInputValue,
861
- change: event.data,
862
- willCommit: false,
863
- selStart,
864
- selEnd,
865
- },
866
- });
867
- });
868
- }
869
-
870
- this._setEventListeners(
871
- element,
872
- [
873
- ["focus", "Focus"],
874
- ["blur", "Blur"],
875
- ["mousedown", "Mouse Down"],
876
- ["mouseenter", "Mouse Enter"],
877
- ["mouseleave", "Mouse Exit"],
878
- ["mouseup", "Mouse Up"],
879
- ],
880
- event => event.target.value
881
- );
882
- }
883
-
884
- if (blurListener) {
885
- element.addEventListener("blur", blurListener);
886
- }
887
-
888
- element.disabled = this.data.readOnly;
889
- element.name = this.data.fieldName;
890
-
891
- if (this.data.maxLen !== null) {
892
- element.maxLength = this.data.maxLen;
893
- }
894
-
895
- if (this.data.comb) {
896
- const fieldWidth = this.data.rect[2] - this.data.rect[0];
897
- const combWidth = fieldWidth / this.data.maxLen;
898
-
899
- element.classList.add("comb");
900
- element.style.letterSpacing = `calc(${combWidth}px - 1ch)`;
901
- }
902
- } else {
903
- element = document.createElement("div");
904
- element.textContent = this.data.fieldValue;
905
- element.style.verticalAlign = "middle";
906
- element.style.display = "table-cell";
907
- }
908
-
909
- this._setTextStyle(element);
910
-
911
- this.container.appendChild(element);
912
- return this.container;
913
- }
914
-
915
- /**
916
- * Apply text styles to the text in the element.
917
- *
918
- * @private
919
- * @param {HTMLDivElement} element
920
- * @memberof TextWidgetAnnotationElement
921
- */
922
- _setTextStyle(element) {
923
- const TEXT_ALIGNMENT = ["left", "center", "right"];
924
- const { fontSize, fontColor } = this.data.defaultAppearanceData;
925
- const style = element.style;
926
-
927
- // TODO: If the font-size is zero, calculate it based on the height and
928
- // width of the element.
929
- // Not setting `style.fontSize` will use the default font-size for now.
930
- if (fontSize) {
931
- style.fontSize = `${fontSize}px`;
932
- }
933
-
934
- style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]);
935
-
936
- if (this.data.textAlignment !== null) {
937
- style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment];
938
- }
939
- }
940
- }
941
-
942
- class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
943
- constructor(parameters) {
944
- super(parameters, { isRenderable: parameters.renderInteractiveForms });
945
- }
946
-
947
- render() {
948
- const storage = this.annotationStorage;
949
- const data = this.data;
950
- const id = data.id;
951
- let value = storage.getValue(id, {
952
- value:
953
- data.fieldValue &&
954
- ((data.exportValue && data.exportValue === data.fieldValue) ||
955
- (!data.exportValue && data.fieldValue !== "Off")),
956
- }).value;
957
- if (typeof value === "string") {
958
- // The value has been changed through js and set in annotationStorage.
959
- value = value !== "Off";
960
- storage.setValue(id, { value });
961
- }
962
-
963
- this.container.className = "buttonWidgetAnnotation checkBox";
964
-
965
- const element = document.createElement("input");
966
- element.disabled = data.readOnly;
967
- element.type = "checkbox";
968
- element.name = this.data.fieldName;
969
- if (value) {
970
- element.setAttribute("checked", true);
971
- }
972
- element.setAttribute("id", id);
973
-
974
- element.addEventListener("change", function (event) {
975
- const name = event.target.name;
976
- for (const checkbox of document.getElementsByName(name)) {
977
- if (checkbox !== event.target) {
978
- checkbox.checked = false;
979
- storage.setValue(
980
- checkbox.parentNode.getAttribute("data-annotation-id"),
981
- { value: false }
982
- );
983
- }
984
- }
985
- storage.setValue(id, { value: event.target.checked });
986
- });
987
-
988
- if (this.enableScripting && this.hasJSActions) {
989
- element.addEventListener("updatefromsandbox", jsEvent => {
990
- const actions = {
991
- value(event) {
992
- event.target.checked = event.detail.value !== "Off";
993
- storage.setValue(id, { value: event.target.checked });
994
- },
995
- };
996
- this._dispatchEventFromSandbox(actions, jsEvent);
997
- });
998
-
999
- this._setEventListeners(
1000
- element,
1001
- [
1002
- ["change", "Validate"],
1003
- ["change", "Action"],
1004
- ["focus", "Focus"],
1005
- ["blur", "Blur"],
1006
- ["mousedown", "Mouse Down"],
1007
- ["mouseenter", "Mouse Enter"],
1008
- ["mouseleave", "Mouse Exit"],
1009
- ["mouseup", "Mouse Up"],
1010
- ],
1011
- event => event.target.checked
1012
- );
1013
- }
1014
-
1015
- this.container.appendChild(element);
1016
- return this.container;
1017
- }
1018
- }
1019
-
1020
- class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
1021
- constructor(parameters) {
1022
- super(parameters, { isRenderable: parameters.renderInteractiveForms });
1023
- }
1024
-
1025
- render() {
1026
- this.container.className = "buttonWidgetAnnotation radioButton";
1027
- const storage = this.annotationStorage;
1028
- const data = this.data;
1029
- const id = data.id;
1030
- let value = storage.getValue(id, {
1031
- value: data.fieldValue === data.buttonValue,
1032
- }).value;
1033
- if (typeof value === "string") {
1034
- // The value has been changed through js and set in annotationStorage.
1035
- value = value !== data.buttonValue;
1036
- storage.setValue(id, { value });
1037
- }
1038
-
1039
- const element = document.createElement("input");
1040
- element.disabled = data.readOnly;
1041
- element.type = "radio";
1042
- element.name = data.fieldName;
1043
- if (value) {
1044
- element.setAttribute("checked", true);
1045
- }
1046
- element.setAttribute("id", id);
1047
-
1048
- element.addEventListener("change", function (event) {
1049
- const { target } = event;
1050
- for (const radio of document.getElementsByName(target.name)) {
1051
- if (radio !== target) {
1052
- storage.setValue(radio.getAttribute("id"), { value: false });
1053
- }
1054
- }
1055
- storage.setValue(id, { value: target.checked });
1056
- });
1057
-
1058
- if (this.enableScripting && this.hasJSActions) {
1059
- const pdfButtonValue = data.buttonValue;
1060
- element.addEventListener("updatefromsandbox", jsEvent => {
1061
- const actions = {
1062
- value(event) {
1063
- const checked = pdfButtonValue === event.detail.value;
1064
- for (const radio of document.getElementsByName(event.target.name)) {
1065
- const radioId = radio.getAttribute("id");
1066
- radio.checked = radioId === id && checked;
1067
- storage.setValue(radioId, { value: radio.checked });
1068
- }
1069
- },
1070
- };
1071
- this._dispatchEventFromSandbox(actions, jsEvent);
1072
- });
1073
-
1074
- this._setEventListeners(
1075
- element,
1076
- [
1077
- ["change", "Validate"],
1078
- ["change", "Action"],
1079
- ["focus", "Focus"],
1080
- ["blur", "Blur"],
1081
- ["mousedown", "Mouse Down"],
1082
- ["mouseenter", "Mouse Enter"],
1083
- ["mouseleave", "Mouse Exit"],
1084
- ["mouseup", "Mouse Up"],
1085
- ],
1086
- event => event.target.checked
1087
- );
1088
- }
1089
-
1090
- this.container.appendChild(element);
1091
- return this.container;
1092
- }
1093
- }
1094
-
1095
- class PushButtonWidgetAnnotationElement extends LinkAnnotationElement {
1096
- render() {
1097
- // The rendering and functionality of a push button widget annotation is
1098
- // equal to that of a link annotation, but may have more functionality, such
1099
- // as performing actions on form fields (resetting, submitting, et cetera).
1100
- const container = super.render();
1101
- container.className = "buttonWidgetAnnotation pushButton";
1102
-
1103
- if (this.data.alternativeText) {
1104
- container.title = this.data.alternativeText;
1105
- }
1106
-
1107
- return container;
1108
- }
1109
- }
1110
-
1111
- class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
1112
- constructor(parameters) {
1113
- super(parameters, { isRenderable: parameters.renderInteractiveForms });
1114
- }
1115
-
1116
- render() {
1117
- this.container.className = "choiceWidgetAnnotation";
1118
- const storage = this.annotationStorage;
1119
- const id = this.data.id;
1120
-
1121
- // For printing/saving we currently only support choice widgets with one
1122
- // option selection. Therefore, listboxes (#12189) and comboboxes (#12224)
1123
- // are not properly printed/saved yet, so we only store the first item in
1124
- // the field value array instead of the entire array. Once support for those
1125
- // two field types is implemented, we should use the same pattern as the
1126
- // other interactive widgets where the return value of `getValue`
1127
- // is used and the full array of field values is stored.
1128
- storage.getValue(id, {
1129
- value:
1130
- this.data.fieldValue.length > 0 ? this.data.fieldValue[0] : undefined,
1131
- });
1132
-
1133
- const selectElement = document.createElement("select");
1134
- selectElement.disabled = this.data.readOnly;
1135
- selectElement.name = this.data.fieldName;
1136
- selectElement.setAttribute("id", id);
1137
-
1138
- if (!this.data.combo) {
1139
- // List boxes have a size and (optionally) multiple selection.
1140
- selectElement.size = this.data.options.length;
1141
- if (this.data.multiSelect) {
1142
- selectElement.multiple = true;
1143
- }
1144
- }
1145
-
1146
- // Insert the options into the choice field.
1147
- for (const option of this.data.options) {
1148
- const optionElement = document.createElement("option");
1149
- optionElement.textContent = option.displayValue;
1150
- optionElement.value = option.exportValue;
1151
- if (this.data.fieldValue.includes(option.exportValue)) {
1152
- optionElement.setAttribute("selected", true);
1153
- }
1154
- selectElement.appendChild(optionElement);
1155
- }
1156
-
1157
- const getValue = (event, isExport) => {
1158
- const name = isExport ? "value" : "textContent";
1159
- const options = event.target.options;
1160
- if (!event.target.multiple) {
1161
- return options.selectedIndex === -1
1162
- ? null
1163
- : options[options.selectedIndex][name];
1164
- }
1165
- return Array.prototype.filter
1166
- .call(options, option => option.selected)
1167
- .map(option => option[name]);
1168
- };
1169
-
1170
- const getItems = event => {
1171
- const options = event.target.options;
1172
- return Array.prototype.map.call(options, option => {
1173
- return { displayValue: option.textContent, exportValue: option.value };
1174
- });
1175
- };
1176
-
1177
- if (this.enableScripting && this.hasJSActions) {
1178
- selectElement.addEventListener("updatefromsandbox", jsEvent => {
1179
- const actions = {
1180
- value(event) {
1181
- const options = selectElement.options;
1182
- const value = event.detail.value;
1183
- const values = new Set(Array.isArray(value) ? value : [value]);
1184
- Array.prototype.forEach.call(options, option => {
1185
- option.selected = values.has(option.value);
1186
- });
1187
- storage.setValue(id, {
1188
- value: getValue(event, /* isExport */ true),
1189
- });
1190
- },
1191
- multipleSelection(event) {
1192
- selectElement.multiple = true;
1193
- },
1194
- remove(event) {
1195
- const options = selectElement.options;
1196
- const index = event.detail.remove;
1197
- options[index].selected = false;
1198
- selectElement.remove(index);
1199
- if (options.length > 0) {
1200
- const i = Array.prototype.findIndex.call(
1201
- options,
1202
- option => option.selected
1203
- );
1204
- if (i === -1) {
1205
- options[0].selected = true;
1206
- }
1207
- }
1208
- storage.setValue(id, {
1209
- value: getValue(event, /* isExport */ true),
1210
- items: getItems(event),
1211
- });
1212
- },
1213
- clear(event) {
1214
- while (selectElement.length !== 0) {
1215
- selectElement.remove(0);
1216
- }
1217
- storage.setValue(id, { value: null, items: [] });
1218
- },
1219
- insert(event) {
1220
- const { index, displayValue, exportValue } = event.detail.insert;
1221
- const optionElement = document.createElement("option");
1222
- optionElement.textContent = displayValue;
1223
- optionElement.value = exportValue;
1224
- selectElement.insertBefore(
1225
- optionElement,
1226
- selectElement.children[index]
1227
- );
1228
- storage.setValue(id, {
1229
- value: getValue(event, /* isExport */ true),
1230
- items: getItems(event),
1231
- });
1232
- },
1233
- items(event) {
1234
- const { items } = event.detail;
1235
- while (selectElement.length !== 0) {
1236
- selectElement.remove(0);
1237
- }
1238
- for (const item of items) {
1239
- const { displayValue, exportValue } = item;
1240
- const optionElement = document.createElement("option");
1241
- optionElement.textContent = displayValue;
1242
- optionElement.value = exportValue;
1243
- selectElement.appendChild(optionElement);
1244
- }
1245
- if (selectElement.options.length > 0) {
1246
- selectElement.options[0].selected = true;
1247
- }
1248
- storage.setValue(id, {
1249
- value: getValue(event, /* isExport */ true),
1250
- items: getItems(event),
1251
- });
1252
- },
1253
- indices(event) {
1254
- const indices = new Set(event.detail.indices);
1255
- const options = event.target.options;
1256
- Array.prototype.forEach.call(options, (option, i) => {
1257
- option.selected = indices.has(i);
1258
- });
1259
- storage.setValue(id, {
1260
- value: getValue(event, /* isExport */ true),
1261
- });
1262
- },
1263
- editable(event) {
1264
- event.target.disabled = !event.detail.editable;
1265
- },
1266
- };
1267
- this._dispatchEventFromSandbox(actions, jsEvent);
1268
- });
1269
-
1270
- selectElement.addEventListener("input", event => {
1271
- const exportValue = getValue(event, /* isExport */ true);
1272
- const value = getValue(event, /* isExport */ false);
1273
- storage.setValue(id, { value: exportValue });
1274
-
1275
- this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
1276
- source: this,
1277
- detail: {
1278
- id,
1279
- name: "Keystroke",
1280
- value,
1281
- changeEx: exportValue,
1282
- willCommit: true,
1283
- commitKey: 1,
1284
- keyDown: false,
1285
- },
1286
- });
1287
- });
1288
-
1289
- this._setEventListeners(
1290
- selectElement,
1291
- [
1292
- ["focus", "Focus"],
1293
- ["blur", "Blur"],
1294
- ["mousedown", "Mouse Down"],
1295
- ["mouseenter", "Mouse Enter"],
1296
- ["mouseleave", "Mouse Exit"],
1297
- ["mouseup", "Mouse Up"],
1298
- ["input", "Action"],
1299
- ],
1300
- event => event.target.checked
1301
- );
1302
- } else {
1303
- selectElement.addEventListener("input", function (event) {
1304
- storage.setValue(id, { value: getValue(event) });
1305
- });
1306
- }
1307
-
1308
- this.container.appendChild(selectElement);
1309
- return this.container;
1310
- }
1311
- }
1312
-
1313
- class PopupAnnotationElement extends AnnotationElement {
1314
- constructor(parameters) {
1315
- const isRenderable = !!(parameters.data.title || parameters.data.contents);
1316
- super(parameters, { isRenderable });
1317
- }
1318
-
1319
- render() {
1320
- // Do not render popup annotations for parent elements with these types as
1321
- // they create the popups themselves (because of custom trigger divs).
1322
- const IGNORE_TYPES = [
1323
- "Line",
1324
- "Square",
1325
- "Circle",
1326
- "PolyLine",
1327
- "Polygon",
1328
- "Ink",
1329
- ];
1330
-
1331
- this.container.className = "popupAnnotation";
1332
-
1333
- if (IGNORE_TYPES.includes(this.data.parentType)) {
1334
- return this.container;
1335
- }
1336
-
1337
- const selector = `[data-annotation-id="${this.data.parentId}"]`;
1338
- const parentElements = this.layer.querySelectorAll(selector);
1339
- if (parentElements.length === 0) {
1340
- return this.container;
1341
- }
1342
-
1343
- const popup = new PopupElement({
1344
- container: this.container,
1345
- trigger: Array.from(parentElements),
1346
- color: this.data.color,
1347
- title: this.data.title,
1348
- modificationDate: this.data.modificationDate,
1349
- contents: this.data.contents,
1350
- });
1351
-
1352
- // Position the popup next to the parent annotation's container.
1353
- // PDF viewers ignore a popup annotation's rectangle.
1354
- const page = this.page;
1355
- const rect = Util.normalizeRect([
1356
- this.data.parentRect[0],
1357
- page.view[3] - this.data.parentRect[1] + page.view[1],
1358
- this.data.parentRect[2],
1359
- page.view[3] - this.data.parentRect[3] + page.view[1],
1360
- ]);
1361
- const popupLeft =
1362
- rect[0] + this.data.parentRect[2] - this.data.parentRect[0];
1363
- const popupTop = rect[1];
1364
-
1365
- this.container.style.transformOrigin = `${-popupLeft}px ${-popupTop}px`;
1366
- this.container.style.left = `${popupLeft}px`;
1367
- this.container.style.top = `${popupTop}px`;
1368
-
1369
- this.container.appendChild(popup.render());
1370
- return this.container;
1371
- }
1372
- }
1373
-
1374
- class PopupElement {
1375
- constructor(parameters) {
1376
- this.container = parameters.container;
1377
- this.trigger = parameters.trigger;
1378
- this.color = parameters.color;
1379
- this.title = parameters.title;
1380
- this.modificationDate = parameters.modificationDate;
1381
- this.contents = parameters.contents;
1382
- this.hideWrapper = parameters.hideWrapper || false;
1383
-
1384
- this.pinned = false;
1385
- }
1386
-
1387
- render() {
1388
- const BACKGROUND_ENLIGHT = 0.7;
1389
-
1390
- const wrapper = document.createElement("div");
1391
- wrapper.className = "popupWrapper";
1392
-
1393
- // For Popup annotations we hide the entire section because it contains
1394
- // only the popup. However, for Text annotations without a separate Popup
1395
- // annotation, we cannot hide the entire container as the image would
1396
- // disappear too. In that special case, hiding the wrapper suffices.
1397
- this.hideElement = this.hideWrapper ? wrapper : this.container;
1398
- this.hideElement.hidden = true;
1399
-
1400
- const popup = document.createElement("div");
1401
- popup.className = "popup";
1402
-
1403
- const color = this.color;
1404
- if (color) {
1405
- // Enlighten the color.
1406
- const r = BACKGROUND_ENLIGHT * (255 - color[0]) + color[0];
1407
- const g = BACKGROUND_ENLIGHT * (255 - color[1]) + color[1];
1408
- const b = BACKGROUND_ENLIGHT * (255 - color[2]) + color[2];
1409
- popup.style.backgroundColor = Util.makeHexColor(r | 0, g | 0, b | 0);
1410
- }
1411
-
1412
- const title = document.createElement("h1");
1413
- title.textContent = this.title;
1414
- popup.appendChild(title);
1415
-
1416
- // The modification date is shown in the popup instead of the creation
1417
- // date if it is available and can be parsed correctly, which is
1418
- // consistent with other viewers such as Adobe Acrobat.
1419
- const dateObject = PDFDateString.toDateObject(this.modificationDate);
1420
- if (dateObject) {
1421
- const modificationDate = document.createElement("span");
1422
- modificationDate.textContent = "{{date}}, {{time}}";
1423
- modificationDate.dataset.l10nId = "annotation_date_string";
1424
- modificationDate.dataset.l10nArgs = JSON.stringify({
1425
- date: dateObject.toLocaleDateString(),
1426
- time: dateObject.toLocaleTimeString(),
1427
- });
1428
- popup.appendChild(modificationDate);
1429
- }
1430
-
1431
- const contents = this._formatContents(this.contents);
1432
- popup.appendChild(contents);
1433
-
1434
- if (!Array.isArray(this.trigger)) {
1435
- this.trigger = [this.trigger];
1436
- }
1437
-
1438
- // Attach the event listeners to the trigger element.
1439
- for (const element of this.trigger) {
1440
- element.addEventListener("click", this._toggle.bind(this));
1441
- element.addEventListener("mouseover", this._show.bind(this, false));
1442
- element.addEventListener("mouseout", this._hide.bind(this, false));
1443
- }
1444
- popup.addEventListener("click", this._hide.bind(this, true));
1445
-
1446
- wrapper.appendChild(popup);
1447
- return wrapper;
1448
- }
1449
-
1450
- /**
1451
- * Format the contents of the popup by adding newlines where necessary.
1452
- *
1453
- * @private
1454
- * @param {string} contents
1455
- * @memberof PopupElement
1456
- * @returns {HTMLParagraphElement}
1457
- */
1458
- _formatContents(contents) {
1459
- const p = document.createElement("p");
1460
- const lines = contents.split(/(?:\r\n?|\n)/);
1461
- for (let i = 0, ii = lines.length; i < ii; ++i) {
1462
- const line = lines[i];
1463
- p.appendChild(document.createTextNode(line));
1464
- if (i < ii - 1) {
1465
- p.appendChild(document.createElement("br"));
1466
- }
1467
- }
1468
- return p;
1469
- }
1470
-
1471
- /**
1472
- * Toggle the visibility of the popup.
1473
- *
1474
- * @private
1475
- * @memberof PopupElement
1476
- */
1477
- _toggle() {
1478
- if (this.pinned) {
1479
- this._hide(true);
1480
- } else {
1481
- this._show(true);
1482
- }
1483
- }
1484
-
1485
- /**
1486
- * Show the popup.
1487
- *
1488
- * @private
1489
- * @param {boolean} pin
1490
- * @memberof PopupElement
1491
- */
1492
- _show(pin = false) {
1493
- if (pin) {
1494
- this.pinned = true;
1495
- }
1496
- if (this.hideElement.hidden) {
1497
- this.hideElement.hidden = false;
1498
- this.container.style.zIndex += 1;
1499
- }
1500
- }
1501
-
1502
- /**
1503
- * Hide the popup.
1504
- *
1505
- * @private
1506
- * @param {boolean} unpin
1507
- * @memberof PopupElement
1508
- */
1509
- _hide(unpin = true) {
1510
- if (unpin) {
1511
- this.pinned = false;
1512
- }
1513
- if (!this.hideElement.hidden && !this.pinned) {
1514
- this.hideElement.hidden = true;
1515
- this.container.style.zIndex -= 1;
1516
- }
1517
- }
1518
- }
1519
-
1520
- class FreeTextAnnotationElement extends AnnotationElement {
1521
- constructor(parameters) {
1522
- const isRenderable = !!(
1523
- parameters.data.hasPopup ||
1524
- parameters.data.title ||
1525
- parameters.data.contents
1526
- );
1527
- super(parameters, { isRenderable, ignoreBorder: true });
1528
- }
1529
-
1530
- render() {
1531
- this.container.className = "freeTextAnnotation";
1532
-
1533
- if (!this.data.hasPopup) {
1534
- this._createPopup(null, this.data);
1535
- }
1536
- return this.container;
1537
- }
1538
- }
1539
-
1540
- class LineAnnotationElement extends AnnotationElement {
1541
- constructor(parameters) {
1542
- const isRenderable = !!(
1543
- parameters.data.hasPopup ||
1544
- parameters.data.title ||
1545
- parameters.data.contents
1546
- );
1547
- super(parameters, { isRenderable, ignoreBorder: true });
1548
- }
1549
-
1550
- render() {
1551
- this.container.className = "lineAnnotation";
1552
-
1553
- // Create an invisible line with the same starting and ending coordinates
1554
- // that acts as the trigger for the popup. Only the line itself should
1555
- // trigger the popup, not the entire container.
1556
- const data = this.data;
1557
- const width = data.rect[2] - data.rect[0];
1558
- const height = data.rect[3] - data.rect[1];
1559
- const svg = this.svgFactory.create(width, height);
1560
-
1561
- // PDF coordinates are calculated from a bottom left origin, so transform
1562
- // the line coordinates to a top left origin for the SVG element.
1563
- const line = this.svgFactory.createElement("svg:line");
1564
- line.setAttribute("x1", data.rect[2] - data.lineCoordinates[0]);
1565
- line.setAttribute("y1", data.rect[3] - data.lineCoordinates[1]);
1566
- line.setAttribute("x2", data.rect[2] - data.lineCoordinates[2]);
1567
- line.setAttribute("y2", data.rect[3] - data.lineCoordinates[3]);
1568
- // Ensure that the 'stroke-width' is always non-zero, since otherwise it
1569
- // won't be possible to open/close the popup (note e.g. issue 11122).
1570
- line.setAttribute("stroke-width", data.borderStyle.width || 1);
1571
- line.setAttribute("stroke", "transparent");
1572
-
1573
- svg.appendChild(line);
1574
- this.container.append(svg);
1575
-
1576
- // Create the popup ourselves so that we can bind it to the line instead
1577
- // of to the entire container (which is the default).
1578
- this._createPopup(line, data);
1579
-
1580
- return this.container;
1581
- }
1582
- }
1583
-
1584
- class SquareAnnotationElement extends AnnotationElement {
1585
- constructor(parameters) {
1586
- const isRenderable = !!(
1587
- parameters.data.hasPopup ||
1588
- parameters.data.title ||
1589
- parameters.data.contents
1590
- );
1591
- super(parameters, { isRenderable, ignoreBorder: true });
1592
- }
1593
-
1594
- render() {
1595
- this.container.className = "squareAnnotation";
1596
-
1597
- // Create an invisible square with the same rectangle that acts as the
1598
- // trigger for the popup. Only the square itself should trigger the
1599
- // popup, not the entire container.
1600
- const data = this.data;
1601
- const width = data.rect[2] - data.rect[0];
1602
- const height = data.rect[3] - data.rect[1];
1603
- const svg = this.svgFactory.create(width, height);
1604
-
1605
- // The browser draws half of the borders inside the square and half of
1606
- // the borders outside the square by default. This behavior cannot be
1607
- // changed programmatically, so correct for that here.
1608
- const borderWidth = data.borderStyle.width;
1609
- const square = this.svgFactory.createElement("svg:rect");
1610
- square.setAttribute("x", borderWidth / 2);
1611
- square.setAttribute("y", borderWidth / 2);
1612
- square.setAttribute("width", width - borderWidth);
1613
- square.setAttribute("height", height - borderWidth);
1614
- // Ensure that the 'stroke-width' is always non-zero, since otherwise it
1615
- // won't be possible to open/close the popup (note e.g. issue 11122).
1616
- square.setAttribute("stroke-width", borderWidth || 1);
1617
- square.setAttribute("stroke", "transparent");
1618
- square.setAttribute("fill", "none");
1619
-
1620
- svg.appendChild(square);
1621
- this.container.append(svg);
1622
-
1623
- // Create the popup ourselves so that we can bind it to the square instead
1624
- // of to the entire container (which is the default).
1625
- this._createPopup(square, data);
1626
-
1627
- return this.container;
1628
- }
1629
- }
1630
-
1631
- class CircleAnnotationElement extends AnnotationElement {
1632
- constructor(parameters) {
1633
- const isRenderable = !!(
1634
- parameters.data.hasPopup ||
1635
- parameters.data.title ||
1636
- parameters.data.contents
1637
- );
1638
- super(parameters, { isRenderable, ignoreBorder: true });
1639
- }
1640
-
1641
- render() {
1642
- this.container.className = "circleAnnotation";
1643
-
1644
- // Create an invisible circle with the same ellipse that acts as the
1645
- // trigger for the popup. Only the circle itself should trigger the
1646
- // popup, not the entire container.
1647
- const data = this.data;
1648
- const width = data.rect[2] - data.rect[0];
1649
- const height = data.rect[3] - data.rect[1];
1650
- const svg = this.svgFactory.create(width, height);
1651
-
1652
- // The browser draws half of the borders inside the circle and half of
1653
- // the borders outside the circle by default. This behavior cannot be
1654
- // changed programmatically, so correct for that here.
1655
- const borderWidth = data.borderStyle.width;
1656
- const circle = this.svgFactory.createElement("svg:ellipse");
1657
- circle.setAttribute("cx", width / 2);
1658
- circle.setAttribute("cy", height / 2);
1659
- circle.setAttribute("rx", width / 2 - borderWidth / 2);
1660
- circle.setAttribute("ry", height / 2 - borderWidth / 2);
1661
- // Ensure that the 'stroke-width' is always non-zero, since otherwise it
1662
- // won't be possible to open/close the popup (note e.g. issue 11122).
1663
- circle.setAttribute("stroke-width", borderWidth || 1);
1664
- circle.setAttribute("stroke", "transparent");
1665
- circle.setAttribute("fill", "none");
1666
-
1667
- svg.appendChild(circle);
1668
- this.container.append(svg);
1669
-
1670
- // Create the popup ourselves so that we can bind it to the circle instead
1671
- // of to the entire container (which is the default).
1672
- this._createPopup(circle, data);
1673
-
1674
- return this.container;
1675
- }
1676
- }
1677
-
1678
- class PolylineAnnotationElement extends AnnotationElement {
1679
- constructor(parameters) {
1680
- const isRenderable = !!(
1681
- parameters.data.hasPopup ||
1682
- parameters.data.title ||
1683
- parameters.data.contents
1684
- );
1685
- super(parameters, { isRenderable, ignoreBorder: true });
1686
-
1687
- this.containerClassName = "polylineAnnotation";
1688
- this.svgElementName = "svg:polyline";
1689
- }
1690
-
1691
- render() {
1692
- this.container.className = this.containerClassName;
1693
-
1694
- // Create an invisible polyline with the same points that acts as the
1695
- // trigger for the popup. Only the polyline itself should trigger the
1696
- // popup, not the entire container.
1697
- const data = this.data;
1698
- const width = data.rect[2] - data.rect[0];
1699
- const height = data.rect[3] - data.rect[1];
1700
- const svg = this.svgFactory.create(width, height);
1701
-
1702
- // Convert the vertices array to a single points string that the SVG
1703
- // polyline element expects ("x1,y1 x2,y2 ..."). PDF coordinates are
1704
- // calculated from a bottom left origin, so transform the polyline
1705
- // coordinates to a top left origin for the SVG element.
1706
- let points = [];
1707
- for (const coordinate of data.vertices) {
1708
- const x = coordinate.x - data.rect[0];
1709
- const y = data.rect[3] - coordinate.y;
1710
- points.push(x + "," + y);
1711
- }
1712
- points = points.join(" ");
1713
-
1714
- const polyline = this.svgFactory.createElement(this.svgElementName);
1715
- polyline.setAttribute("points", points);
1716
- // Ensure that the 'stroke-width' is always non-zero, since otherwise it
1717
- // won't be possible to open/close the popup (note e.g. issue 11122).
1718
- polyline.setAttribute("stroke-width", data.borderStyle.width || 1);
1719
- polyline.setAttribute("stroke", "transparent");
1720
- polyline.setAttribute("fill", "none");
1721
-
1722
- svg.appendChild(polyline);
1723
- this.container.append(svg);
1724
-
1725
- // Create the popup ourselves so that we can bind it to the polyline
1726
- // instead of to the entire container (which is the default).
1727
- this._createPopup(polyline, data);
1728
-
1729
- return this.container;
1730
- }
1731
- }
1732
-
1733
- class PolygonAnnotationElement extends PolylineAnnotationElement {
1734
- constructor(parameters) {
1735
- // Polygons are specific forms of polylines, so reuse their logic.
1736
- super(parameters);
1737
-
1738
- this.containerClassName = "polygonAnnotation";
1739
- this.svgElementName = "svg:polygon";
1740
- }
1741
- }
1742
-
1743
- class CaretAnnotationElement extends AnnotationElement {
1744
- constructor(parameters) {
1745
- const isRenderable = !!(
1746
- parameters.data.hasPopup ||
1747
- parameters.data.title ||
1748
- parameters.data.contents
1749
- );
1750
- super(parameters, { isRenderable, ignoreBorder: true });
1751
- }
1752
-
1753
- render() {
1754
- this.container.className = "caretAnnotation";
1755
-
1756
- if (!this.data.hasPopup) {
1757
- this._createPopup(null, this.data);
1758
- }
1759
- return this.container;
1760
- }
1761
- }
1762
-
1763
- class InkAnnotationElement extends AnnotationElement {
1764
- constructor(parameters) {
1765
- const isRenderable = !!(
1766
- parameters.data.hasPopup ||
1767
- parameters.data.title ||
1768
- parameters.data.contents
1769
- );
1770
- super(parameters, { isRenderable, ignoreBorder: true });
1771
-
1772
- this.containerClassName = "inkAnnotation";
1773
-
1774
- // Use the polyline SVG element since it allows us to use coordinates
1775
- // directly and to draw both straight lines and curves.
1776
- this.svgElementName = "svg:polyline";
1777
- }
1778
-
1779
- render() {
1780
- this.container.className = this.containerClassName;
1781
-
1782
- // Create an invisible polyline with the same points that acts as the
1783
- // trigger for the popup.
1784
- const data = this.data;
1785
- const width = data.rect[2] - data.rect[0];
1786
- const height = data.rect[3] - data.rect[1];
1787
- const svg = this.svgFactory.create(width, height);
1788
-
1789
- for (const inkList of data.inkLists) {
1790
- // Convert the ink list to a single points string that the SVG
1791
- // polyline element expects ("x1,y1 x2,y2 ..."). PDF coordinates are
1792
- // calculated from a bottom left origin, so transform the polyline
1793
- // coordinates to a top left origin for the SVG element.
1794
- let points = [];
1795
- for (const coordinate of inkList) {
1796
- const x = coordinate.x - data.rect[0];
1797
- const y = data.rect[3] - coordinate.y;
1798
- points.push(`${x},${y}`);
1799
- }
1800
- points = points.join(" ");
1801
-
1802
- const polyline = this.svgFactory.createElement(this.svgElementName);
1803
- polyline.setAttribute("points", points);
1804
- // Ensure that the 'stroke-width' is always non-zero, since otherwise it
1805
- // won't be possible to open/close the popup (note e.g. issue 11122).
1806
- polyline.setAttribute("stroke-width", data.borderStyle.width || 1);
1807
- polyline.setAttribute("stroke", "transparent");
1808
- polyline.setAttribute("fill", "none");
1809
-
1810
- // Create the popup ourselves so that we can bind it to the polyline
1811
- // instead of to the entire container (which is the default).
1812
- this._createPopup(polyline, data);
1813
-
1814
- svg.appendChild(polyline);
1815
- }
1816
-
1817
- this.container.append(svg);
1818
- return this.container;
1819
- }
1820
- }
1821
-
1822
- class HighlightAnnotationElement extends AnnotationElement {
1823
- constructor(parameters) {
1824
- const isRenderable = !!(
1825
- parameters.data.hasPopup ||
1826
- parameters.data.title ||
1827
- parameters.data.contents
1828
- );
1829
- super(parameters, {
1830
- isRenderable,
1831
- ignoreBorder: true,
1832
- createQuadrilaterals: true,
1833
- });
1834
- }
1835
-
1836
- render() {
1837
- if (!this.data.hasPopup) {
1838
- this._createPopup(null, this.data);
1839
- }
1840
-
1841
- if (this.quadrilaterals) {
1842
- return this._renderQuadrilaterals("highlightAnnotation");
1843
- }
1844
-
1845
- this.container.className = "highlightAnnotation";
1846
- return this.container;
1847
- }
1848
- }
1849
-
1850
- class UnderlineAnnotationElement extends AnnotationElement {
1851
- constructor(parameters) {
1852
- const isRenderable = !!(
1853
- parameters.data.hasPopup ||
1854
- parameters.data.title ||
1855
- parameters.data.contents
1856
- );
1857
- super(parameters, {
1858
- isRenderable,
1859
- ignoreBorder: true,
1860
- createQuadrilaterals: true,
1861
- });
1862
- }
1863
-
1864
- render() {
1865
- if (!this.data.hasPopup) {
1866
- this._createPopup(null, this.data);
1867
- }
1868
-
1869
- if (this.quadrilaterals) {
1870
- return this._renderQuadrilaterals("underlineAnnotation");
1871
- }
1872
-
1873
- this.container.className = "underlineAnnotation";
1874
- return this.container;
1875
- }
1876
- }
1877
-
1878
- class SquigglyAnnotationElement extends AnnotationElement {
1879
- constructor(parameters) {
1880
- const isRenderable = !!(
1881
- parameters.data.hasPopup ||
1882
- parameters.data.title ||
1883
- parameters.data.contents
1884
- );
1885
- super(parameters, {
1886
- isRenderable,
1887
- ignoreBorder: true,
1888
- createQuadrilaterals: true,
1889
- });
1890
- }
1891
-
1892
- render() {
1893
- if (!this.data.hasPopup) {
1894
- this._createPopup(null, this.data);
1895
- }
1896
-
1897
- if (this.quadrilaterals) {
1898
- return this._renderQuadrilaterals("squigglyAnnotation");
1899
- }
1900
-
1901
- this.container.className = "squigglyAnnotation";
1902
- return this.container;
1903
- }
1904
- }
1905
-
1906
- class StrikeOutAnnotationElement extends AnnotationElement {
1907
- constructor(parameters) {
1908
- const isRenderable = !!(
1909
- parameters.data.hasPopup ||
1910
- parameters.data.title ||
1911
- parameters.data.contents
1912
- );
1913
- super(parameters, {
1914
- isRenderable,
1915
- ignoreBorder: true,
1916
- createQuadrilaterals: true,
1917
- });
1918
- }
1919
-
1920
- render() {
1921
- if (!this.data.hasPopup) {
1922
- this._createPopup(null, this.data);
1923
- }
1924
-
1925
- if (this.quadrilaterals) {
1926
- return this._renderQuadrilaterals("strikeoutAnnotation");
1927
- }
1928
-
1929
- this.container.className = "strikeoutAnnotation";
1930
- return this.container;
1931
- }
1932
- }
1933
-
1934
- class StampAnnotationElement extends AnnotationElement {
1935
- constructor(parameters) {
1936
- const isRenderable = !!(
1937
- parameters.data.hasPopup ||
1938
- parameters.data.title ||
1939
- parameters.data.contents
1940
- );
1941
- super(parameters, { isRenderable, ignoreBorder: true });
1942
- }
1943
-
1944
- render() {
1945
- this.container.className = "stampAnnotation";
1946
-
1947
- if (!this.data.hasPopup) {
1948
- this._createPopup(null, this.data);
1949
- }
1950
- return this.container;
1951
- }
1952
- }
1953
-
1954
- class FileAttachmentAnnotationElement extends AnnotationElement {
1955
- constructor(parameters) {
1956
- super(parameters, { isRenderable: true });
1957
-
1958
- const { filename, content } = this.data.file;
1959
- this.filename = getFilenameFromUrl(filename);
1960
- this.content = content;
1961
-
1962
- this.linkService.eventBus?.dispatch("fileattachmentannotation", {
1963
- source: this,
1964
- id: stringToPDFString(filename),
1965
- filename,
1966
- content,
1967
- });
1968
- }
1969
-
1970
- render() {
1971
- this.container.className = "fileAttachmentAnnotation";
1972
-
1973
- const trigger = document.createElement("div");
1974
- trigger.style.height = this.container.style.height;
1975
- trigger.style.width = this.container.style.width;
1976
- trigger.addEventListener("dblclick", this._download.bind(this));
1977
-
1978
- if (!this.data.hasPopup && (this.data.title || this.data.contents)) {
1979
- this._createPopup(trigger, this.data);
1980
- }
1981
-
1982
- this.container.appendChild(trigger);
1983
- return this.container;
1984
- }
1985
-
1986
- /**
1987
- * Download the file attachment associated with this annotation.
1988
- *
1989
- * @private
1990
- * @memberof FileAttachmentAnnotationElement
1991
- */
1992
- _download() {
1993
- this.downloadManager?.openOrDownloadData(
1994
- this.container,
1995
- this.content,
1996
- this.filename
1997
- );
1998
- }
1999
- }
2000
-
2001
- /**
2002
- * @typedef {Object} AnnotationLayerParameters
2003
- * @property {PageViewport} viewport
2004
- * @property {HTMLDivElement} div
2005
- * @property {Array} annotations
2006
- * @property {PDFPage} page
2007
- * @property {IPDFLinkService} linkService
2008
- * @property {DownloadManager} downloadManager
2009
- * @property {string} [imageResourcesPath] - Path for image resources, mainly
2010
- * for annotation icons. Include trailing slash.
2011
- * @property {boolean} renderInteractiveForms
2012
- * @property {boolean} [enableScripting] - Enable embedded script execution.
2013
- * @property {boolean} [hasJSActions] - Some fields have JS actions.
2014
- * The default value is `false`.
2015
- */
2016
-
2017
- class AnnotationLayer {
2018
- /**
2019
- * Render a new annotation layer with all annotation elements.
2020
- *
2021
- * @public
2022
- * @param {AnnotationLayerParameters} parameters
2023
- * @memberof AnnotationLayer
2024
- */
2025
- static render(parameters) {
2026
- const sortedAnnotations = [],
2027
- popupAnnotations = [];
2028
- // Ensure that Popup annotations are handled last, since they're dependant
2029
- // upon the parent annotation having already been rendered (please refer to
2030
- // the `PopupAnnotationElement.render` method); fixes issue 11362.
2031
- for (const data of parameters.annotations) {
2032
- if (!data) {
2033
- continue;
2034
- }
2035
- if (data.annotationType === AnnotationType.POPUP) {
2036
- popupAnnotations.push(data);
2037
- continue;
2038
- }
2039
- sortedAnnotations.push(data);
2040
- }
2041
- if (popupAnnotations.length) {
2042
- sortedAnnotations.push(...popupAnnotations);
2043
- }
2044
-
2045
- for (const data of sortedAnnotations) {
2046
- const element = AnnotationElementFactory.create({
2047
- data,
2048
- layer: parameters.div,
2049
- page: parameters.page,
2050
- viewport: parameters.viewport,
2051
- linkService: parameters.linkService,
2052
- downloadManager: parameters.downloadManager,
2053
- imageResourcesPath: parameters.imageResourcesPath || "",
2054
- renderInteractiveForms: parameters.renderInteractiveForms !== false,
2055
- svgFactory: new DOMSVGFactory(),
2056
- annotationStorage:
2057
- parameters.annotationStorage || new AnnotationStorage(),
2058
- enableScripting: parameters.enableScripting,
2059
- hasJSActions: parameters.hasJSActions,
2060
- mouseState: parameters.mouseState || { isDown: false },
2061
- });
2062
- if (element.isRenderable) {
2063
- const rendered = element.render();
2064
- if (data.hidden) {
2065
- rendered.style.visibility = "hidden";
2066
- }
2067
- if (Array.isArray(rendered)) {
2068
- for (const renderedElement of rendered) {
2069
- parameters.div.appendChild(renderedElement);
2070
- }
2071
- } else {
2072
- if (element instanceof PopupAnnotationElement) {
2073
- // Popup annotation elements should not be on top of other
2074
- // annotation elements to prevent interfering with mouse events.
2075
- parameters.div.prepend(rendered);
2076
- } else {
2077
- parameters.div.appendChild(rendered);
2078
- }
2079
- }
2080
- }
2081
- }
2082
- }
2083
-
2084
- /**
2085
- * Update the annotation elements on existing annotation layer.
2086
- *
2087
- * @public
2088
- * @param {AnnotationLayerParameters} parameters
2089
- * @memberof AnnotationLayer
2090
- */
2091
- static update(parameters) {
2092
- const transform = `matrix(${parameters.viewport.transform.join(",")})`;
2093
- for (const data of parameters.annotations) {
2094
- const elements = parameters.div.querySelectorAll(
2095
- `[data-annotation-id="${data.id}"]`
2096
- );
2097
- if (elements) {
2098
- for (const element of elements) {
2099
- element.style.transform = transform;
2100
- }
2101
- }
2102
- }
2103
- parameters.div.hidden = false;
2104
- }
2105
- }
2106
-
2107
- export { AnnotationLayer };