@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,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 };