@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.
- package/README.md +1 -1
- package/package.json +42 -26
- package/{src/display → pdfjs/dist}/display_utils.js +344 -139
- package/{src/display → pdfjs/dist}/fetch_stream.js +115 -97
- package/pdfjs/dist/l10n_utils.js +140 -0
- package/{src/shared → pdfjs/dist}/message_handler.js +243 -259
- package/{src/display → pdfjs/dist}/network.js +149 -87
- package/{src/display/content_disposition.js → pdfjs/dist/network_utils.js} +167 -55
- package/{src/display → pdfjs/dist}/node_stream.js +133 -98
- package/pdfjs/dist/pdf.js +12778 -0
- package/pdfjs/dist/pdf_link_service.js +638 -0
- package/pdfjs/dist/pdf_rendering_queue.js +199 -0
- package/pdfjs/dist/pdf_thumbnail_viewer.js +819 -0
- package/pdfjs/dist/pdf_viewer.js +3598 -0
- package/pdfjs/dist/ui_utils.js +1033 -0
- package/{src/shared → pdfjs/dist}/util.js +301 -287
- package/pdfjs/dist/worker.js +62813 -0
- package/src/vcf-pdf-viewer.js +98 -46
- package/theme/lumo/vcf-pdf-viewer-styles.js +4 -4
- package/theme/material/vcf-pdf-viewer-styles.js +4 -4
- package/theme/material/vcf-pdf-viewer.js +2 -2
- package/src/core/.eslintrc +0 -13
- package/src/core/annotation.js +0 -2948
- package/src/core/arithmetic_decoder.js +0 -182
- package/src/core/ascii_85_stream.js +0 -98
- package/src/core/ascii_hex_stream.js +0 -79
- package/src/core/base_stream.js +0 -110
- package/src/core/bidi.js +0 -438
- package/src/core/calibri_factors.js +0 -308
- package/src/core/catalog.js +0 -1459
- package/src/core/ccitt.js +0 -1062
- package/src/core/ccitt_stream.js +0 -60
- package/src/core/cff_font.js +0 -116
- package/src/core/cff_parser.js +0 -1949
- package/src/core/charsets.js +0 -119
- package/src/core/chunked_stream.js +0 -557
- package/src/core/cmap.js +0 -1039
- package/src/core/colorspace.js +0 -1533
- package/src/core/core_utils.js +0 -464
- package/src/core/crypto.js +0 -1900
- package/src/core/decode_stream.js +0 -170
- package/src/core/decrypt_stream.js +0 -59
- package/src/core/default_appearance.js +0 -99
- package/src/core/document.js +0 -1456
- package/src/core/encodings.js +0 -301
- package/src/core/evaluator.js +0 -4601
- package/src/core/file_spec.js +0 -108
- package/src/core/flate_stream.js +0 -402
- package/src/core/font_renderer.js +0 -882
- package/src/core/fonts.js +0 -3260
- package/src/core/fonts_utils.js +0 -221
- package/src/core/function.js +0 -1257
- package/src/core/glyf.js +0 -706
- package/src/core/glyphlist.js +0 -4558
- package/src/core/helvetica_factors.js +0 -353
- package/src/core/image.js +0 -802
- package/src/core/image_utils.js +0 -291
- package/src/core/jbig2.js +0 -2572
- package/src/core/jbig2_stream.js +0 -73
- package/src/core/jpeg_stream.js +0 -105
- package/src/core/jpg.js +0 -1416
- package/src/core/jpx.js +0 -2343
- package/src/core/jpx_stream.js +0 -87
- package/src/core/liberationsans_widths.js +0 -221
- package/src/core/lzw_stream.js +0 -150
- package/src/core/metadata_parser.js +0 -146
- package/src/core/metrics.js +0 -2970
- package/src/core/murmurhash3.js +0 -139
- package/src/core/myriadpro_factors.js +0 -290
- package/src/core/name_number_tree.js +0 -153
- package/src/core/object_loader.js +0 -149
- package/src/core/opentype_file_builder.js +0 -154
- package/src/core/operator_list.js +0 -734
- package/src/core/parser.js +0 -1416
- package/src/core/pattern.js +0 -985
- package/src/core/pdf_manager.js +0 -217
- package/src/core/predictor_stream.js +0 -238
- package/src/core/primitives.js +0 -402
- package/src/core/ps_parser.js +0 -272
- package/src/core/run_length_stream.js +0 -61
- package/src/core/segoeui_factors.js +0 -308
- package/src/core/standard_fonts.js +0 -817
- package/src/core/stream.js +0 -103
- package/src/core/struct_tree.js +0 -335
- package/src/core/to_unicode_map.js +0 -103
- package/src/core/type1_font.js +0 -421
- package/src/core/type1_parser.js +0 -776
- package/src/core/unicode.js +0 -1649
- package/src/core/worker.js +0 -848
- package/src/core/worker_stream.js +0 -135
- package/src/core/writer.js +0 -278
- package/src/core/xfa/bind.js +0 -652
- package/src/core/xfa/builder.js +0 -207
- package/src/core/xfa/config.js +0 -1926
- package/src/core/xfa/connection_set.js +0 -202
- package/src/core/xfa/data.js +0 -82
- package/src/core/xfa/datasets.js +0 -76
- package/src/core/xfa/factory.js +0 -111
- package/src/core/xfa/fonts.js +0 -181
- package/src/core/xfa/formcalc_lexer.js +0 -385
- package/src/core/xfa/formcalc_parser.js +0 -1340
- package/src/core/xfa/html_utils.js +0 -639
- package/src/core/xfa/layout.js +0 -383
- package/src/core/xfa/locale_set.js +0 -345
- package/src/core/xfa/namespaces.js +0 -81
- package/src/core/xfa/parser.js +0 -184
- package/src/core/xfa/setup.js +0 -38
- package/src/core/xfa/signature.js +0 -40
- package/src/core/xfa/som.js +0 -338
- package/src/core/xfa/stylesheet.js +0 -40
- package/src/core/xfa/template.js +0 -6260
- package/src/core/xfa/text.js +0 -290
- package/src/core/xfa/unknown.js +0 -29
- package/src/core/xfa/utils.js +0 -217
- package/src/core/xfa/xdp.js +0 -59
- package/src/core/xfa/xfa_object.js +0 -1130
- package/src/core/xfa/xhtml.js +0 -543
- package/src/core/xfa_fonts.js +0 -208
- package/src/core/xml_parser.js +0 -507
- package/src/core/xref.js +0 -899
- package/src/display/annotation_layer.js +0 -2107
- package/src/display/annotation_storage.js +0 -113
- package/src/display/api.js +0 -3292
- package/src/display/base_factory.js +0 -180
- package/src/display/canvas.js +0 -2828
- package/src/display/font_loader.js +0 -484
- package/src/display/metadata.js +0 -41
- package/src/display/network_utils.js +0 -100
- package/src/display/node_utils.js +0 -83
- package/src/display/optional_content_config.js +0 -189
- package/src/display/pattern_helper.js +0 -659
- package/src/display/svg.js +0 -1709
- package/src/display/text_layer.js +0 -847
- package/src/display/transport_stream.js +0 -303
- package/src/display/worker_options.js +0 -40
- package/src/display/xfa_layer.js +0 -204
- package/src/doc_helper.js +0 -25
- package/src/images/logo.svg +0 -41
- package/src/interfaces.js +0 -169
- package/src/license_header.js +0 -14
- package/src/license_header_libre.js +0 -21
- package/src/pdf.image_decoders.js +0 -46
- package/src/pdf.js +0 -146
- package/src/pdf.sandbox.external.js +0 -181
- package/src/pdf.sandbox.js +0 -151
- package/src/pdf.scripting.js +0 -25
- package/src/pdf.worker.entry.js +0 -19
- package/src/pdf.worker.js +0 -23
- package/src/scripting_api/aform.js +0 -608
- package/src/scripting_api/app.js +0 -621
- package/src/scripting_api/color.js +0 -129
- package/src/scripting_api/common.js +0 -58
- package/src/scripting_api/console.js +0 -38
- package/src/scripting_api/constants.js +0 -208
- package/src/scripting_api/doc.js +0 -1195
- package/src/scripting_api/error.js +0 -23
- package/src/scripting_api/event.js +0 -232
- package/src/scripting_api/field.js +0 -620
- package/src/scripting_api/fullscreen.js +0 -145
- package/src/scripting_api/initialization.js +0 -223
- package/src/scripting_api/pdf_object.js +0 -24
- package/src/scripting_api/print_params.js +0 -146
- package/src/scripting_api/proxy.js +0 -139
- package/src/scripting_api/thermometer.js +0 -69
- package/src/scripting_api/util.js +0 -581
- package/src/shared/.eslintrc +0 -13
- package/src/shared/cffStandardStrings.js +0 -311
- package/src/shared/compatibility.js +0 -114
- package/src/shared/fonts_utils.js +0 -429
- package/src/shared/is_node.js +0 -27
- package/src/shared/scripting_utils.js +0 -85
- package/src/worker_loader.js +0 -32
|
@@ -0,0 +1,3598 @@
|
|
|
1
|
+
import { DEFAULT_SCALE, TextLayerMode, RendererType, CSS_UNITS, getOutputScale, approximateFraction, roundToDivide, watchScroll, PresentationModeState, UNKNOWN_SCALE, isValidRotation, SpreadMode, ScrollMode, scrollIntoView, isPortraitOrientation, MAX_AUTO_SCALE, SCROLLBAR_PADDING, VERTICAL_PADDING, DEFAULT_SCALE_VALUE, getVisibleElements, isValidScrollMode, isValidSpreadMode, moveToEndOfArray } from './ui_utils.js';
|
|
2
|
+
import { AnnotationLayer, SVGGraphics, renderTextLayer, XfaLayer, version } from './pdf.js';
|
|
3
|
+
import { RenderingStates, PDFRenderingQueue } from './pdf_rendering_queue.js';
|
|
4
|
+
import { NullL10n } from './l10n_utils.js';
|
|
5
|
+
import { l as createPromiseCapability, s as shadow } from './util.js';
|
|
6
|
+
import { R as RenderingCancelledException } from './display_utils.js';
|
|
7
|
+
import { SimpleLinkService } from './pdf_link_service.js';
|
|
8
|
+
import './message_handler.js';
|
|
9
|
+
|
|
10
|
+
/* Copyright 2014 Mozilla Foundation
|
|
11
|
+
*
|
|
12
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
+
* you may not use this file except in compliance with the License.
|
|
14
|
+
* You may obtain a copy of the License at
|
|
15
|
+
*
|
|
16
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
+
*
|
|
18
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
19
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
+
* See the License for the specific language governing permissions and
|
|
22
|
+
* limitations under the License.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {Object} AnnotationLayerBuilderOptions
|
|
26
|
+
* @property {HTMLDivElement} pageDiv
|
|
27
|
+
* @property {PDFPage} pdfPage
|
|
28
|
+
* @property {AnnotationStorage} [annotationStorage]
|
|
29
|
+
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
|
30
|
+
* for annotation icons. Include trailing slash.
|
|
31
|
+
* @property {boolean} renderInteractiveForms
|
|
32
|
+
* @property {IPDFLinkService} linkService
|
|
33
|
+
* @property {DownloadManager} downloadManager
|
|
34
|
+
* @property {IL10n} l10n - Localization service.
|
|
35
|
+
* @property {boolean} [enableScripting]
|
|
36
|
+
* @property {Promise<boolean>} [hasJSActionsPromise]
|
|
37
|
+
* @property {Object} [mouseState]
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
class AnnotationLayerBuilder {
|
|
41
|
+
/**
|
|
42
|
+
* @param {AnnotationLayerBuilderOptions} options
|
|
43
|
+
*/
|
|
44
|
+
constructor({
|
|
45
|
+
pageDiv,
|
|
46
|
+
pdfPage,
|
|
47
|
+
linkService,
|
|
48
|
+
downloadManager,
|
|
49
|
+
annotationStorage = null,
|
|
50
|
+
imageResourcesPath = "",
|
|
51
|
+
renderInteractiveForms = true,
|
|
52
|
+
l10n = NullL10n,
|
|
53
|
+
enableScripting = false,
|
|
54
|
+
hasJSActionsPromise = null,
|
|
55
|
+
mouseState = null
|
|
56
|
+
}) {
|
|
57
|
+
this.pageDiv = pageDiv;
|
|
58
|
+
this.pdfPage = pdfPage;
|
|
59
|
+
this.linkService = linkService;
|
|
60
|
+
this.downloadManager = downloadManager;
|
|
61
|
+
this.imageResourcesPath = imageResourcesPath;
|
|
62
|
+
this.renderInteractiveForms = renderInteractiveForms;
|
|
63
|
+
this.l10n = l10n;
|
|
64
|
+
this.annotationStorage = annotationStorage;
|
|
65
|
+
this.enableScripting = enableScripting;
|
|
66
|
+
this._hasJSActionsPromise = hasJSActionsPromise;
|
|
67
|
+
this._mouseState = mouseState;
|
|
68
|
+
this.div = null;
|
|
69
|
+
this._cancelled = false;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* @param {PageViewport} viewport
|
|
73
|
+
* @param {string} intent (default value is 'display')
|
|
74
|
+
* @returns {Promise<void>} A promise that is resolved when rendering of the
|
|
75
|
+
* annotations is complete.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
render(viewport, intent = "display") {
|
|
80
|
+
return Promise.all([this.pdfPage.getAnnotations({
|
|
81
|
+
intent
|
|
82
|
+
}), this._hasJSActionsPromise]).then(([annotations, hasJSActions = false]) => {
|
|
83
|
+
if (this._cancelled) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (annotations.length === 0) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const parameters = {
|
|
92
|
+
viewport: viewport.clone({
|
|
93
|
+
dontFlip: true
|
|
94
|
+
}),
|
|
95
|
+
div: this.div,
|
|
96
|
+
annotations,
|
|
97
|
+
page: this.pdfPage,
|
|
98
|
+
imageResourcesPath: this.imageResourcesPath,
|
|
99
|
+
renderInteractiveForms: this.renderInteractiveForms,
|
|
100
|
+
linkService: this.linkService,
|
|
101
|
+
downloadManager: this.downloadManager,
|
|
102
|
+
annotationStorage: this.annotationStorage,
|
|
103
|
+
enableScripting: this.enableScripting,
|
|
104
|
+
hasJSActions,
|
|
105
|
+
mouseState: this._mouseState
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
if (this.div) {
|
|
109
|
+
// If an annotationLayer already exists, refresh its children's
|
|
110
|
+
// transformation matrices.
|
|
111
|
+
AnnotationLayer.update(parameters);
|
|
112
|
+
} else {
|
|
113
|
+
// Create an annotation layer div and render the annotations
|
|
114
|
+
// if there is at least one annotation.
|
|
115
|
+
this.div = document.createElement("div");
|
|
116
|
+
this.div.className = "annotationLayer";
|
|
117
|
+
this.pageDiv.appendChild(this.div);
|
|
118
|
+
parameters.div = this.div;
|
|
119
|
+
AnnotationLayer.render(parameters);
|
|
120
|
+
this.l10n.translate(this.div);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
cancel() {
|
|
126
|
+
this._cancelled = true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
hide() {
|
|
130
|
+
if (!this.div) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.div.hidden = true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Copyright 2018 Mozilla Foundation
|
|
140
|
+
*
|
|
141
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
142
|
+
* you may not use this file except in compliance with the License.
|
|
143
|
+
* You may obtain a copy of the License at
|
|
144
|
+
*
|
|
145
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
146
|
+
*
|
|
147
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
148
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
149
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
150
|
+
* See the License for the specific language governing permissions and
|
|
151
|
+
* limitations under the License.
|
|
152
|
+
*/
|
|
153
|
+
const compatibilityParams = Object.create(null);
|
|
154
|
+
|
|
155
|
+
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
|
156
|
+
const userAgent = typeof navigator !== "undefined" && navigator.userAgent || "";
|
|
157
|
+
const platform = typeof navigator !== "undefined" && navigator.platform || "";
|
|
158
|
+
const maxTouchPoints = typeof navigator !== "undefined" && navigator.maxTouchPoints || 1;
|
|
159
|
+
const isAndroid = /Android/.test(userAgent);
|
|
160
|
+
const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent) || platform === "MacIntel" && maxTouchPoints > 1;
|
|
161
|
+
const isIOSChrome = /CriOS/.test(userAgent); // Checks if possible to use URL.createObjectURL()
|
|
162
|
+
// Support: IE, Chrome on iOS
|
|
163
|
+
|
|
164
|
+
(function checkOnBlobSupport() {
|
|
165
|
+
// Sometimes Chrome on iOS loses data created with createObjectURL(),
|
|
166
|
+
// see issue #8081.
|
|
167
|
+
if (isIOSChrome) {
|
|
168
|
+
compatibilityParams.disableCreateObjectURL = true;
|
|
169
|
+
}
|
|
170
|
+
})(); // Limit canvas size to 5 mega-pixels on mobile.
|
|
171
|
+
// Support: Android, iOS
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
(function checkCanvasSizeLimitation() {
|
|
175
|
+
if (isIOS || isAndroid) {
|
|
176
|
+
compatibilityParams.maxCanvasPixels = 5242880;
|
|
177
|
+
}
|
|
178
|
+
})();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const viewerCompatibilityParams = Object.freeze(compatibilityParams);
|
|
182
|
+
|
|
183
|
+
/* Copyright 2012 Mozilla Foundation
|
|
184
|
+
*
|
|
185
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
186
|
+
* you may not use this file except in compliance with the License.
|
|
187
|
+
* You may obtain a copy of the License at
|
|
188
|
+
*
|
|
189
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
190
|
+
*
|
|
191
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
192
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
193
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
194
|
+
* See the License for the specific language governing permissions and
|
|
195
|
+
* limitations under the License.
|
|
196
|
+
*/
|
|
197
|
+
/**
|
|
198
|
+
* @typedef {Object} PDFPageViewOptions
|
|
199
|
+
* @property {HTMLDivElement} container - The viewer element.
|
|
200
|
+
* @property {EventBus} eventBus - The application event bus.
|
|
201
|
+
* @property {number} id - The page unique ID (normally its number).
|
|
202
|
+
* @property {number} scale - The page scale display.
|
|
203
|
+
* @property {PageViewport} defaultViewport - The page viewport.
|
|
204
|
+
* @property {Promise<OptionalContentConfig>} [optionalContentConfigPromise] -
|
|
205
|
+
* A promise that is resolved with an {@link OptionalContentConfig} instance.
|
|
206
|
+
* The default value is `null`.
|
|
207
|
+
* @property {PDFRenderingQueue} renderingQueue - The rendering queue object.
|
|
208
|
+
* @property {IPDFTextLayerFactory} textLayerFactory
|
|
209
|
+
* @property {number} [textLayerMode] - Controls if the text layer used for
|
|
210
|
+
* selection and searching is created, and if the improved text selection
|
|
211
|
+
* behaviour is enabled. The constants from {TextLayerMode} should be used.
|
|
212
|
+
* The default value is `TextLayerMode.ENABLE`.
|
|
213
|
+
* @property {IPDFAnnotationLayerFactory} annotationLayerFactory
|
|
214
|
+
* @property {IPDFXfaLayerFactory} xfaLayerFactory
|
|
215
|
+
* @property {IPDFStructTreeLayerFactory} structTreeLayerFactory
|
|
216
|
+
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
|
217
|
+
* for annotation icons. Include trailing slash.
|
|
218
|
+
* @property {boolean} renderInteractiveForms - Turns on rendering of
|
|
219
|
+
* interactive form elements. The default value is `true`.
|
|
220
|
+
* @property {string} renderer - 'canvas' or 'svg'. The default is 'canvas'.
|
|
221
|
+
* @property {boolean} [useOnlyCssZoom] - Enables CSS only zooming. The default
|
|
222
|
+
* value is `false`.
|
|
223
|
+
* @property {number} [maxCanvasPixels] - The maximum supported canvas size in
|
|
224
|
+
* total pixels, i.e. width * height. Use -1 for no limit. The default value
|
|
225
|
+
* is 4096 * 4096 (16 mega-pixels).
|
|
226
|
+
* @property {IL10n} l10n - Localization service.
|
|
227
|
+
*/
|
|
228
|
+
|
|
229
|
+
const MAX_CANVAS_PIXELS = viewerCompatibilityParams.maxCanvasPixels || 16777216;
|
|
230
|
+
/**
|
|
231
|
+
* @implements {IRenderableView}
|
|
232
|
+
*/
|
|
233
|
+
|
|
234
|
+
class PDFPageView {
|
|
235
|
+
/**
|
|
236
|
+
* @param {PDFPageViewOptions} options
|
|
237
|
+
*/
|
|
238
|
+
constructor(options) {
|
|
239
|
+
const container = options.container;
|
|
240
|
+
const defaultViewport = options.defaultViewport;
|
|
241
|
+
this.id = options.id;
|
|
242
|
+
this.renderingId = "page" + this.id;
|
|
243
|
+
this.pdfPage = null;
|
|
244
|
+
this.pageLabel = null;
|
|
245
|
+
this.rotation = 0;
|
|
246
|
+
this.scale = options.scale || DEFAULT_SCALE;
|
|
247
|
+
this.viewport = defaultViewport;
|
|
248
|
+
this.pdfPageRotate = defaultViewport.rotation;
|
|
249
|
+
this._optionalContentConfigPromise = options.optionalContentConfigPromise || null;
|
|
250
|
+
this.hasRestrictedScaling = false;
|
|
251
|
+
this.textLayerMode = Number.isInteger(options.textLayerMode) ? options.textLayerMode : TextLayerMode.ENABLE;
|
|
252
|
+
this.imageResourcesPath = options.imageResourcesPath || "";
|
|
253
|
+
this.renderInteractiveForms = options.renderInteractiveForms !== false;
|
|
254
|
+
this.useOnlyCssZoom = options.useOnlyCssZoom || false;
|
|
255
|
+
this.maxCanvasPixels = options.maxCanvasPixels || MAX_CANVAS_PIXELS;
|
|
256
|
+
this.eventBus = options.eventBus;
|
|
257
|
+
this.renderingQueue = options.renderingQueue;
|
|
258
|
+
this.textLayerFactory = options.textLayerFactory;
|
|
259
|
+
this.annotationLayerFactory = options.annotationLayerFactory;
|
|
260
|
+
this.xfaLayerFactory = options.xfaLayerFactory;
|
|
261
|
+
this.structTreeLayerFactory = options.structTreeLayerFactory;
|
|
262
|
+
this.renderer = options.renderer || RendererType.CANVAS;
|
|
263
|
+
this.l10n = options.l10n || NullL10n;
|
|
264
|
+
this.paintTask = null;
|
|
265
|
+
this.paintedViewportMap = new WeakMap();
|
|
266
|
+
this.renderingState = RenderingStates.INITIAL;
|
|
267
|
+
this.resume = null;
|
|
268
|
+
this._renderError = null;
|
|
269
|
+
this.annotationLayer = null;
|
|
270
|
+
this.textLayer = null;
|
|
271
|
+
this.zoomLayer = null;
|
|
272
|
+
this.xfaLayer = null;
|
|
273
|
+
this.structTreeLayer = null;
|
|
274
|
+
const div = document.createElement("div");
|
|
275
|
+
div.className = "page";
|
|
276
|
+
div.style.width = Math.floor(this.viewport.width) + "px";
|
|
277
|
+
div.style.height = Math.floor(this.viewport.height) + "px";
|
|
278
|
+
div.setAttribute("data-page-number", this.id);
|
|
279
|
+
div.setAttribute("role", "region");
|
|
280
|
+
this.l10n.get("page_landmark", {
|
|
281
|
+
page: this.id
|
|
282
|
+
}).then(msg => {
|
|
283
|
+
div.setAttribute("aria-label", msg);
|
|
284
|
+
});
|
|
285
|
+
this.div = div;
|
|
286
|
+
container.appendChild(div);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
setPdfPage(pdfPage) {
|
|
290
|
+
this.pdfPage = pdfPage;
|
|
291
|
+
this.pdfPageRotate = pdfPage.rotate;
|
|
292
|
+
const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
|
|
293
|
+
this.viewport = pdfPage.getViewport({
|
|
294
|
+
scale: this.scale * CSS_UNITS,
|
|
295
|
+
rotation: totalRotation
|
|
296
|
+
});
|
|
297
|
+
this.reset();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
destroy() {
|
|
301
|
+
this.reset();
|
|
302
|
+
|
|
303
|
+
if (this.pdfPage) {
|
|
304
|
+
this.pdfPage.cleanup();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* @private
|
|
309
|
+
*/
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
async _renderAnnotationLayer() {
|
|
313
|
+
let error = null;
|
|
314
|
+
|
|
315
|
+
try {
|
|
316
|
+
await this.annotationLayer.render(this.viewport, "display");
|
|
317
|
+
} catch (ex) {
|
|
318
|
+
error = ex;
|
|
319
|
+
} finally {
|
|
320
|
+
this.eventBus.dispatch("annotationlayerrendered", {
|
|
321
|
+
source: this,
|
|
322
|
+
pageNumber: this.id,
|
|
323
|
+
error
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* @private
|
|
329
|
+
*/
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
async _renderXfaLayer() {
|
|
333
|
+
let error = null;
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
await this.xfaLayer.render(this.viewport, "display");
|
|
337
|
+
} catch (ex) {
|
|
338
|
+
error = ex;
|
|
339
|
+
} finally {
|
|
340
|
+
this.eventBus.dispatch("xfalayerrendered", {
|
|
341
|
+
source: this,
|
|
342
|
+
pageNumber: this.id,
|
|
343
|
+
error
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* @private
|
|
349
|
+
*/
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
_resetZoomLayer(removeFromDOM = false) {
|
|
353
|
+
if (!this.zoomLayer) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const zoomLayerCanvas = this.zoomLayer.firstChild;
|
|
358
|
+
this.paintedViewportMap.delete(zoomLayerCanvas); // Zeroing the width and height causes Firefox to release graphics
|
|
359
|
+
// resources immediately, which can greatly reduce memory consumption.
|
|
360
|
+
|
|
361
|
+
zoomLayerCanvas.width = 0;
|
|
362
|
+
zoomLayerCanvas.height = 0;
|
|
363
|
+
|
|
364
|
+
if (removeFromDOM) {
|
|
365
|
+
// Note: `ChildNode.remove` doesn't throw if the parent node is undefined.
|
|
366
|
+
this.zoomLayer.remove();
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
this.zoomLayer = null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
reset({
|
|
373
|
+
keepZoomLayer = false,
|
|
374
|
+
keepAnnotationLayer = false,
|
|
375
|
+
keepXfaLayer = false
|
|
376
|
+
} = {}) {
|
|
377
|
+
var _this$annotationLayer, _this$xfaLayer;
|
|
378
|
+
|
|
379
|
+
this.cancelRendering({
|
|
380
|
+
keepAnnotationLayer,
|
|
381
|
+
keepXfaLayer
|
|
382
|
+
});
|
|
383
|
+
this.renderingState = RenderingStates.INITIAL;
|
|
384
|
+
const div = this.div;
|
|
385
|
+
div.style.width = Math.floor(this.viewport.width) + "px";
|
|
386
|
+
div.style.height = Math.floor(this.viewport.height) + "px";
|
|
387
|
+
const childNodes = div.childNodes,
|
|
388
|
+
zoomLayerNode = keepZoomLayer && this.zoomLayer || null,
|
|
389
|
+
annotationLayerNode = keepAnnotationLayer && ((_this$annotationLayer = this.annotationLayer) === null || _this$annotationLayer === void 0 ? void 0 : _this$annotationLayer.div) || null,
|
|
390
|
+
xfaLayerNode = keepXfaLayer && ((_this$xfaLayer = this.xfaLayer) === null || _this$xfaLayer === void 0 ? void 0 : _this$xfaLayer.div) || null;
|
|
391
|
+
|
|
392
|
+
for (let i = childNodes.length - 1; i >= 0; i--) {
|
|
393
|
+
const node = childNodes[i];
|
|
394
|
+
|
|
395
|
+
switch (node) {
|
|
396
|
+
case zoomLayerNode:
|
|
397
|
+
case annotationLayerNode:
|
|
398
|
+
case xfaLayerNode:
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
div.removeChild(node);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
div.removeAttribute("data-loaded");
|
|
406
|
+
|
|
407
|
+
if (annotationLayerNode) {
|
|
408
|
+
// Hide the annotation layer until all elements are resized
|
|
409
|
+
// so they are not displayed on the already resized page.
|
|
410
|
+
this.annotationLayer.hide();
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (xfaLayerNode) {
|
|
414
|
+
// Hide the XFA layer until all elements are resized
|
|
415
|
+
// so they are not displayed on the already resized page.
|
|
416
|
+
this.xfaLayer.hide();
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (!zoomLayerNode) {
|
|
420
|
+
if (this.canvas) {
|
|
421
|
+
this.paintedViewportMap.delete(this.canvas); // Zeroing the width and height causes Firefox to release graphics
|
|
422
|
+
// resources immediately, which can greatly reduce memory consumption.
|
|
423
|
+
|
|
424
|
+
this.canvas.width = 0;
|
|
425
|
+
this.canvas.height = 0;
|
|
426
|
+
delete this.canvas;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
this._resetZoomLayer();
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (this.svg) {
|
|
433
|
+
this.paintedViewportMap.delete(this.svg);
|
|
434
|
+
delete this.svg;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
this.loadingIconDiv = document.createElement("div");
|
|
438
|
+
this.loadingIconDiv.className = "loadingIcon";
|
|
439
|
+
this.loadingIconDiv.setAttribute("role", "img");
|
|
440
|
+
this.l10n.get("loading").then(msg => {
|
|
441
|
+
var _this$loadingIconDiv;
|
|
442
|
+
|
|
443
|
+
(_this$loadingIconDiv = this.loadingIconDiv) === null || _this$loadingIconDiv === void 0 ? void 0 : _this$loadingIconDiv.setAttribute("aria-label", msg);
|
|
444
|
+
});
|
|
445
|
+
div.appendChild(this.loadingIconDiv);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
update(scale, rotation, optionalContentConfigPromise = null) {
|
|
449
|
+
this.scale = scale || this.scale; // The rotation may be zero.
|
|
450
|
+
|
|
451
|
+
if (typeof rotation !== "undefined") {
|
|
452
|
+
this.rotation = rotation;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (optionalContentConfigPromise instanceof Promise) {
|
|
456
|
+
this._optionalContentConfigPromise = optionalContentConfigPromise;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
|
|
460
|
+
this.viewport = this.viewport.clone({
|
|
461
|
+
scale: this.scale * CSS_UNITS,
|
|
462
|
+
rotation: totalRotation
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
if (this.svg) {
|
|
466
|
+
this.cssTransform({
|
|
467
|
+
target: this.svg,
|
|
468
|
+
redrawAnnotationLayer: true,
|
|
469
|
+
redrawXfaLayer: true
|
|
470
|
+
});
|
|
471
|
+
this.eventBus.dispatch("pagerendered", {
|
|
472
|
+
source: this,
|
|
473
|
+
pageNumber: this.id,
|
|
474
|
+
cssTransform: true,
|
|
475
|
+
timestamp: performance.now(),
|
|
476
|
+
error: this._renderError
|
|
477
|
+
});
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
let isScalingRestricted = false;
|
|
482
|
+
|
|
483
|
+
if (this.canvas && this.maxCanvasPixels > 0) {
|
|
484
|
+
const outputScale = this.outputScale;
|
|
485
|
+
|
|
486
|
+
if ((Math.floor(this.viewport.width) * outputScale.sx | 0) * (Math.floor(this.viewport.height) * outputScale.sy | 0) > this.maxCanvasPixels) {
|
|
487
|
+
isScalingRestricted = true;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (this.canvas) {
|
|
492
|
+
if (this.useOnlyCssZoom || this.hasRestrictedScaling && isScalingRestricted) {
|
|
493
|
+
this.cssTransform({
|
|
494
|
+
target: this.canvas,
|
|
495
|
+
redrawAnnotationLayer: true,
|
|
496
|
+
redrawXfaLayer: true
|
|
497
|
+
});
|
|
498
|
+
this.eventBus.dispatch("pagerendered", {
|
|
499
|
+
source: this,
|
|
500
|
+
pageNumber: this.id,
|
|
501
|
+
cssTransform: true,
|
|
502
|
+
timestamp: performance.now(),
|
|
503
|
+
error: this._renderError
|
|
504
|
+
});
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (!this.zoomLayer && !this.canvas.hidden) {
|
|
509
|
+
this.zoomLayer = this.canvas.parentNode;
|
|
510
|
+
this.zoomLayer.style.position = "absolute";
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (this.zoomLayer) {
|
|
515
|
+
this.cssTransform({
|
|
516
|
+
target: this.zoomLayer.firstChild
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
this.reset({
|
|
521
|
+
keepZoomLayer: true,
|
|
522
|
+
keepAnnotationLayer: true,
|
|
523
|
+
keepXfaLayer: true
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* PLEASE NOTE: Most likely you want to use the `this.reset()` method,
|
|
528
|
+
* rather than calling this one directly.
|
|
529
|
+
*/
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
cancelRendering({
|
|
533
|
+
keepAnnotationLayer = false,
|
|
534
|
+
keepXfaLayer = false
|
|
535
|
+
} = {}) {
|
|
536
|
+
if (this.paintTask) {
|
|
537
|
+
this.paintTask.cancel();
|
|
538
|
+
this.paintTask = null;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
this.resume = null;
|
|
542
|
+
|
|
543
|
+
if (this.textLayer) {
|
|
544
|
+
this.textLayer.cancel();
|
|
545
|
+
this.textLayer = null;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (this.annotationLayer && (!keepAnnotationLayer || !this.annotationLayer.div)) {
|
|
549
|
+
this.annotationLayer.cancel();
|
|
550
|
+
this.annotationLayer = null;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (this.xfaLayer && (!keepXfaLayer || !this.xfaLayer.div)) {
|
|
554
|
+
this.xfaLayer.cancel();
|
|
555
|
+
this.xfaLayer = null;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (this._onTextLayerRendered) {
|
|
559
|
+
this.eventBus._off("textlayerrendered", this._onTextLayerRendered);
|
|
560
|
+
|
|
561
|
+
this._onTextLayerRendered = null;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
cssTransform({
|
|
566
|
+
target,
|
|
567
|
+
redrawAnnotationLayer = false,
|
|
568
|
+
redrawXfaLayer = false
|
|
569
|
+
}) {
|
|
570
|
+
// Scale target (canvas or svg), its wrapper and page container.
|
|
571
|
+
const width = this.viewport.width;
|
|
572
|
+
const height = this.viewport.height;
|
|
573
|
+
const div = this.div;
|
|
574
|
+
target.style.width = target.parentNode.style.width = div.style.width = Math.floor(width) + "px";
|
|
575
|
+
target.style.height = target.parentNode.style.height = div.style.height = Math.floor(height) + "px"; // The canvas may have been originally rotated; rotate relative to that.
|
|
576
|
+
|
|
577
|
+
const relativeRotation = this.viewport.rotation - this.paintedViewportMap.get(target).rotation;
|
|
578
|
+
const absRotation = Math.abs(relativeRotation);
|
|
579
|
+
let scaleX = 1,
|
|
580
|
+
scaleY = 1;
|
|
581
|
+
|
|
582
|
+
if (absRotation === 90 || absRotation === 270) {
|
|
583
|
+
// Scale x and y because of the rotation.
|
|
584
|
+
scaleX = height / width;
|
|
585
|
+
scaleY = width / height;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
target.style.transform = `rotate(${relativeRotation}deg) scale(${scaleX}, ${scaleY})`;
|
|
589
|
+
|
|
590
|
+
if (this.textLayer) {
|
|
591
|
+
// Rotating the text layer is more complicated since the divs inside the
|
|
592
|
+
// the text layer are rotated.
|
|
593
|
+
// TODO: This could probably be simplified by drawing the text layer in
|
|
594
|
+
// one orientation and then rotating overall.
|
|
595
|
+
const textLayerViewport = this.textLayer.viewport;
|
|
596
|
+
const textRelativeRotation = this.viewport.rotation - textLayerViewport.rotation;
|
|
597
|
+
const textAbsRotation = Math.abs(textRelativeRotation);
|
|
598
|
+
let scale = width / textLayerViewport.width;
|
|
599
|
+
|
|
600
|
+
if (textAbsRotation === 90 || textAbsRotation === 270) {
|
|
601
|
+
scale = width / textLayerViewport.height;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const textLayerDiv = this.textLayer.textLayerDiv;
|
|
605
|
+
let transX, transY;
|
|
606
|
+
|
|
607
|
+
switch (textAbsRotation) {
|
|
608
|
+
case 0:
|
|
609
|
+
transX = transY = 0;
|
|
610
|
+
break;
|
|
611
|
+
|
|
612
|
+
case 90:
|
|
613
|
+
transX = 0;
|
|
614
|
+
transY = "-" + textLayerDiv.style.height;
|
|
615
|
+
break;
|
|
616
|
+
|
|
617
|
+
case 180:
|
|
618
|
+
transX = "-" + textLayerDiv.style.width;
|
|
619
|
+
transY = "-" + textLayerDiv.style.height;
|
|
620
|
+
break;
|
|
621
|
+
|
|
622
|
+
case 270:
|
|
623
|
+
transX = "-" + textLayerDiv.style.width;
|
|
624
|
+
transY = 0;
|
|
625
|
+
break;
|
|
626
|
+
|
|
627
|
+
default:
|
|
628
|
+
console.error("Bad rotation value.");
|
|
629
|
+
break;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
textLayerDiv.style.transform = `rotate(${textAbsRotation}deg) ` + `scale(${scale}) ` + `translate(${transX}, ${transY})`;
|
|
633
|
+
textLayerDiv.style.transformOrigin = "0% 0%";
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (redrawAnnotationLayer && this.annotationLayer) {
|
|
637
|
+
this._renderAnnotationLayer();
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (redrawXfaLayer && this.xfaLayer) {
|
|
641
|
+
this._renderXfaLayer();
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
get width() {
|
|
646
|
+
return this.viewport.width;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
get height() {
|
|
650
|
+
return this.viewport.height;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
getPagePoint(x, y) {
|
|
654
|
+
return this.viewport.convertToPdfPoint(x, y);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
draw() {
|
|
658
|
+
var _this$annotationLayer2, _this$xfaLayer2;
|
|
659
|
+
|
|
660
|
+
if (this.renderingState !== RenderingStates.INITIAL) {
|
|
661
|
+
console.error("Must be in new state before drawing");
|
|
662
|
+
this.reset(); // Ensure that we reset all state to prevent issues.
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const {
|
|
666
|
+
div,
|
|
667
|
+
pdfPage
|
|
668
|
+
} = this;
|
|
669
|
+
|
|
670
|
+
if (!pdfPage) {
|
|
671
|
+
this.renderingState = RenderingStates.FINISHED;
|
|
672
|
+
|
|
673
|
+
if (this.loadingIconDiv) {
|
|
674
|
+
div.removeChild(this.loadingIconDiv);
|
|
675
|
+
delete this.loadingIconDiv;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return Promise.reject(new Error("pdfPage is not loaded"));
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
this.renderingState = RenderingStates.RUNNING; // Wrap the canvas so that if it has a CSS transform for high DPI the
|
|
682
|
+
// overflow will be hidden in Firefox.
|
|
683
|
+
|
|
684
|
+
const canvasWrapper = document.createElement("div");
|
|
685
|
+
canvasWrapper.style.width = div.style.width;
|
|
686
|
+
canvasWrapper.style.height = div.style.height;
|
|
687
|
+
canvasWrapper.classList.add("canvasWrapper");
|
|
688
|
+
|
|
689
|
+
if ((_this$annotationLayer2 = this.annotationLayer) !== null && _this$annotationLayer2 !== void 0 && _this$annotationLayer2.div) {
|
|
690
|
+
// The annotation layer needs to stay on top.
|
|
691
|
+
div.insertBefore(canvasWrapper, this.annotationLayer.div);
|
|
692
|
+
} else {
|
|
693
|
+
div.appendChild(canvasWrapper);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
let textLayer = null;
|
|
697
|
+
|
|
698
|
+
if (this.textLayerMode !== TextLayerMode.DISABLE && this.textLayerFactory) {
|
|
699
|
+
var _this$annotationLayer3;
|
|
700
|
+
|
|
701
|
+
const textLayerDiv = document.createElement("div");
|
|
702
|
+
textLayerDiv.className = "textLayer";
|
|
703
|
+
textLayerDiv.style.width = canvasWrapper.style.width;
|
|
704
|
+
textLayerDiv.style.height = canvasWrapper.style.height;
|
|
705
|
+
|
|
706
|
+
if ((_this$annotationLayer3 = this.annotationLayer) !== null && _this$annotationLayer3 !== void 0 && _this$annotationLayer3.div) {
|
|
707
|
+
// The annotation layer needs to stay on top.
|
|
708
|
+
div.insertBefore(textLayerDiv, this.annotationLayer.div);
|
|
709
|
+
} else {
|
|
710
|
+
div.appendChild(textLayerDiv);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
textLayer = this.textLayerFactory.createTextLayerBuilder(textLayerDiv, this.id - 1, this.viewport, this.textLayerMode === TextLayerMode.ENABLE_ENHANCE, this.eventBus);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
this.textLayer = textLayer;
|
|
717
|
+
|
|
718
|
+
if ((_this$xfaLayer2 = this.xfaLayer) !== null && _this$xfaLayer2 !== void 0 && _this$xfaLayer2.div) {
|
|
719
|
+
// The xfa layer needs to stay on top.
|
|
720
|
+
div.appendChild(this.xfaLayer.div);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
let renderContinueCallback = null;
|
|
724
|
+
|
|
725
|
+
if (this.renderingQueue) {
|
|
726
|
+
renderContinueCallback = cont => {
|
|
727
|
+
if (!this.renderingQueue.isHighestPriority(this)) {
|
|
728
|
+
this.renderingState = RenderingStates.PAUSED;
|
|
729
|
+
|
|
730
|
+
this.resume = () => {
|
|
731
|
+
this.renderingState = RenderingStates.RUNNING;
|
|
732
|
+
cont();
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
cont();
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const finishPaintTask = async (error = null) => {
|
|
743
|
+
// The paintTask may have been replaced by a new one, so only remove
|
|
744
|
+
// the reference to the paintTask if it matches the one that is
|
|
745
|
+
// triggering this callback.
|
|
746
|
+
if (paintTask === this.paintTask) {
|
|
747
|
+
this.paintTask = null;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (error instanceof RenderingCancelledException) {
|
|
751
|
+
this._renderError = null;
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
this._renderError = error;
|
|
756
|
+
this.renderingState = RenderingStates.FINISHED;
|
|
757
|
+
|
|
758
|
+
if (this.loadingIconDiv) {
|
|
759
|
+
div.removeChild(this.loadingIconDiv);
|
|
760
|
+
delete this.loadingIconDiv;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
this._resetZoomLayer(
|
|
764
|
+
/* removeFromDOM = */
|
|
765
|
+
true);
|
|
766
|
+
|
|
767
|
+
this.eventBus.dispatch("pagerendered", {
|
|
768
|
+
source: this,
|
|
769
|
+
pageNumber: this.id,
|
|
770
|
+
cssTransform: false,
|
|
771
|
+
timestamp: performance.now(),
|
|
772
|
+
error: this._renderError
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
if (error) {
|
|
776
|
+
throw error;
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
const paintTask = this.renderer === RendererType.SVG ? this.paintOnSvg(canvasWrapper) : this.paintOnCanvas(canvasWrapper);
|
|
781
|
+
paintTask.onRenderContinue = renderContinueCallback;
|
|
782
|
+
this.paintTask = paintTask;
|
|
783
|
+
const resultPromise = paintTask.promise.then(() => {
|
|
784
|
+
return finishPaintTask(null).then(() => {
|
|
785
|
+
if (textLayer) {
|
|
786
|
+
const readableStream = pdfPage.streamTextContent({
|
|
787
|
+
normalizeWhitespace: true,
|
|
788
|
+
includeMarkedContent: true
|
|
789
|
+
});
|
|
790
|
+
textLayer.setTextContentStream(readableStream);
|
|
791
|
+
textLayer.render();
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
}, function (reason) {
|
|
795
|
+
return finishPaintTask(reason);
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
if (this.annotationLayerFactory) {
|
|
799
|
+
if (!this.annotationLayer) {
|
|
800
|
+
this.annotationLayer = this.annotationLayerFactory.createAnnotationLayerBuilder(div, pdfPage,
|
|
801
|
+
/* annotationStorage = */
|
|
802
|
+
null, this.imageResourcesPath, this.renderInteractiveForms, this.l10n,
|
|
803
|
+
/* enableScripting */
|
|
804
|
+
null,
|
|
805
|
+
/* hasJSActionsPromise = */
|
|
806
|
+
null,
|
|
807
|
+
/* mouseState = */
|
|
808
|
+
null);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
this._renderAnnotationLayer();
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
if (this.xfaLayerFactory) {
|
|
815
|
+
if (!this.xfaLayer) {
|
|
816
|
+
this.xfaLayer = this.xfaLayerFactory.createXfaLayerBuilder(div, pdfPage,
|
|
817
|
+
/* annotationStorage = */
|
|
818
|
+
null);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
this._renderXfaLayer();
|
|
822
|
+
} // The structure tree is currently only supported when the text layer is
|
|
823
|
+
// enabled and a canvas is used for rendering.
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
if (this.structTreeLayerFactory && this.textLayer && this.canvas) {
|
|
827
|
+
// The structure tree must be generated after the text layer for the
|
|
828
|
+
// aria-owns to work.
|
|
829
|
+
this._onTextLayerRendered = event => {
|
|
830
|
+
if (event.pageNumber !== this.id) {
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
this.eventBus._off("textlayerrendered", this._onTextLayerRendered);
|
|
835
|
+
|
|
836
|
+
this._onTextLayerRendered = null;
|
|
837
|
+
|
|
838
|
+
if (!this.canvas) {
|
|
839
|
+
return; // The canvas was removed, prevent errors below.
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
this.pdfPage.getStructTree().then(tree => {
|
|
843
|
+
if (!tree) {
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
if (!this.canvas) {
|
|
848
|
+
return; // The canvas was removed, prevent errors below.
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const treeDom = this.structTreeLayer.render(tree);
|
|
852
|
+
treeDom.classList.add("structTree");
|
|
853
|
+
this.canvas.appendChild(treeDom);
|
|
854
|
+
});
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
this.eventBus._on("textlayerrendered", this._onTextLayerRendered);
|
|
858
|
+
|
|
859
|
+
this.structTreeLayer = this.structTreeLayerFactory.createStructTreeLayerBuilder(pdfPage);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
div.setAttribute("data-loaded", true);
|
|
863
|
+
this.eventBus.dispatch("pagerender", {
|
|
864
|
+
source: this,
|
|
865
|
+
pageNumber: this.id
|
|
866
|
+
});
|
|
867
|
+
return resultPromise;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
paintOnCanvas(canvasWrapper) {
|
|
871
|
+
const renderCapability = createPromiseCapability();
|
|
872
|
+
const result = {
|
|
873
|
+
promise: renderCapability.promise,
|
|
874
|
+
|
|
875
|
+
onRenderContinue(cont) {
|
|
876
|
+
cont();
|
|
877
|
+
},
|
|
878
|
+
|
|
879
|
+
cancel() {
|
|
880
|
+
renderTask.cancel();
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
};
|
|
884
|
+
const viewport = this.viewport;
|
|
885
|
+
const canvas = document.createElement("canvas"); // Keep the canvas hidden until the first draw callback, or until drawing
|
|
886
|
+
// is complete when `!this.renderingQueue`, to prevent black flickering.
|
|
887
|
+
|
|
888
|
+
canvas.hidden = true;
|
|
889
|
+
let isCanvasHidden = true;
|
|
890
|
+
|
|
891
|
+
const showCanvas = function () {
|
|
892
|
+
if (isCanvasHidden) {
|
|
893
|
+
canvas.hidden = false;
|
|
894
|
+
isCanvasHidden = false;
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
canvasWrapper.appendChild(canvas);
|
|
899
|
+
this.canvas = canvas;
|
|
900
|
+
|
|
901
|
+
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL || GENERIC")) {
|
|
902
|
+
canvas.mozOpaque = true;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
const ctx = canvas.getContext("2d", {
|
|
906
|
+
alpha: false
|
|
907
|
+
});
|
|
908
|
+
const outputScale = getOutputScale(ctx);
|
|
909
|
+
this.outputScale = outputScale;
|
|
910
|
+
|
|
911
|
+
if (this.useOnlyCssZoom) {
|
|
912
|
+
const actualSizeViewport = viewport.clone({
|
|
913
|
+
scale: CSS_UNITS
|
|
914
|
+
}); // Use a scale that makes the canvas have the originally intended size
|
|
915
|
+
// of the page.
|
|
916
|
+
|
|
917
|
+
outputScale.sx *= actualSizeViewport.width / viewport.width;
|
|
918
|
+
outputScale.sy *= actualSizeViewport.height / viewport.height;
|
|
919
|
+
outputScale.scaled = true;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
if (this.maxCanvasPixels > 0) {
|
|
923
|
+
const pixelsInViewport = viewport.width * viewport.height;
|
|
924
|
+
const maxScale = Math.sqrt(this.maxCanvasPixels / pixelsInViewport);
|
|
925
|
+
|
|
926
|
+
if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
|
|
927
|
+
outputScale.sx = maxScale;
|
|
928
|
+
outputScale.sy = maxScale;
|
|
929
|
+
outputScale.scaled = true;
|
|
930
|
+
this.hasRestrictedScaling = true;
|
|
931
|
+
} else {
|
|
932
|
+
this.hasRestrictedScaling = false;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
const sfx = approximateFraction(outputScale.sx);
|
|
937
|
+
const sfy = approximateFraction(outputScale.sy);
|
|
938
|
+
canvas.width = roundToDivide(viewport.width * outputScale.sx, sfx[0]);
|
|
939
|
+
canvas.height = roundToDivide(viewport.height * outputScale.sy, sfy[0]);
|
|
940
|
+
canvas.style.width = roundToDivide(viewport.width, sfx[1]) + "px";
|
|
941
|
+
canvas.style.height = roundToDivide(viewport.height, sfy[1]) + "px"; // Add the viewport so it's known what it was originally drawn with.
|
|
942
|
+
|
|
943
|
+
this.paintedViewportMap.set(canvas, viewport); // Rendering area
|
|
944
|
+
|
|
945
|
+
const transform = !outputScale.scaled ? null : [outputScale.sx, 0, 0, outputScale.sy, 0, 0];
|
|
946
|
+
const renderContext = {
|
|
947
|
+
canvasContext: ctx,
|
|
948
|
+
transform,
|
|
949
|
+
viewport: this.viewport,
|
|
950
|
+
renderInteractiveForms: this.renderInteractiveForms,
|
|
951
|
+
optionalContentConfigPromise: this._optionalContentConfigPromise
|
|
952
|
+
};
|
|
953
|
+
const renderTask = this.pdfPage.render(renderContext);
|
|
954
|
+
|
|
955
|
+
renderTask.onContinue = function (cont) {
|
|
956
|
+
showCanvas();
|
|
957
|
+
|
|
958
|
+
if (result.onRenderContinue) {
|
|
959
|
+
result.onRenderContinue(cont);
|
|
960
|
+
} else {
|
|
961
|
+
cont();
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
renderTask.promise.then(function () {
|
|
966
|
+
showCanvas();
|
|
967
|
+
renderCapability.resolve(undefined);
|
|
968
|
+
}, function (error) {
|
|
969
|
+
showCanvas();
|
|
970
|
+
renderCapability.reject(error);
|
|
971
|
+
});
|
|
972
|
+
return result;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
paintOnSvg(wrapper) {
|
|
976
|
+
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL || CHROME")) {
|
|
977
|
+
// Return a mock object, to prevent errors such as e.g.
|
|
978
|
+
// "TypeError: paintTask.promise is undefined".
|
|
979
|
+
return {
|
|
980
|
+
promise: Promise.reject(new Error("SVG rendering is not supported.")),
|
|
981
|
+
|
|
982
|
+
onRenderContinue(cont) {},
|
|
983
|
+
|
|
984
|
+
cancel() {}
|
|
985
|
+
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
let cancelled = false;
|
|
990
|
+
|
|
991
|
+
const ensureNotCancelled = () => {
|
|
992
|
+
if (cancelled) {
|
|
993
|
+
throw new RenderingCancelledException(`Rendering cancelled, page ${this.id}`, "svg");
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
const pdfPage = this.pdfPage;
|
|
998
|
+
const actualSizeViewport = this.viewport.clone({
|
|
999
|
+
scale: CSS_UNITS
|
|
1000
|
+
});
|
|
1001
|
+
const promise = pdfPage.getOperatorList().then(opList => {
|
|
1002
|
+
ensureNotCancelled();
|
|
1003
|
+
const svgGfx = new SVGGraphics(pdfPage.commonObjs, pdfPage.objs,
|
|
1004
|
+
/* forceDataSchema = */
|
|
1005
|
+
viewerCompatibilityParams.disableCreateObjectURL);
|
|
1006
|
+
return svgGfx.getSVG(opList, actualSizeViewport).then(svg => {
|
|
1007
|
+
ensureNotCancelled();
|
|
1008
|
+
this.svg = svg;
|
|
1009
|
+
this.paintedViewportMap.set(svg, actualSizeViewport);
|
|
1010
|
+
svg.style.width = wrapper.style.width;
|
|
1011
|
+
svg.style.height = wrapper.style.height;
|
|
1012
|
+
this.renderingState = RenderingStates.FINISHED;
|
|
1013
|
+
wrapper.appendChild(svg);
|
|
1014
|
+
});
|
|
1015
|
+
});
|
|
1016
|
+
return {
|
|
1017
|
+
promise,
|
|
1018
|
+
|
|
1019
|
+
onRenderContinue(cont) {
|
|
1020
|
+
cont();
|
|
1021
|
+
},
|
|
1022
|
+
|
|
1023
|
+
cancel() {
|
|
1024
|
+
cancelled = true;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* @param {string|null} label
|
|
1031
|
+
*/
|
|
1032
|
+
|
|
1033
|
+
|
|
1034
|
+
setPageLabel(label) {
|
|
1035
|
+
this.pageLabel = typeof label === "string" ? label : null;
|
|
1036
|
+
|
|
1037
|
+
if (this.pageLabel !== null) {
|
|
1038
|
+
this.div.setAttribute("data-page-label", this.pageLabel);
|
|
1039
|
+
} else {
|
|
1040
|
+
this.div.removeAttribute("data-page-label");
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/* Copyright 2021 Mozilla Foundation
|
|
1047
|
+
*
|
|
1048
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1049
|
+
* you may not use this file except in compliance with the License.
|
|
1050
|
+
* You may obtain a copy of the License at
|
|
1051
|
+
*
|
|
1052
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1053
|
+
*
|
|
1054
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1055
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1056
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1057
|
+
* See the License for the specific language governing permissions and
|
|
1058
|
+
* limitations under the License.
|
|
1059
|
+
*/
|
|
1060
|
+
const PDF_ROLE_TO_HTML_ROLE = {
|
|
1061
|
+
// Document level structure types
|
|
1062
|
+
Document: null,
|
|
1063
|
+
// There's a "document" role, but it doesn't make sense here.
|
|
1064
|
+
DocumentFragment: null,
|
|
1065
|
+
// Grouping level structure types
|
|
1066
|
+
Part: "group",
|
|
1067
|
+
Sect: "group",
|
|
1068
|
+
// XXX: There's a "section" role, but it's abstract.
|
|
1069
|
+
Div: "group",
|
|
1070
|
+
Aside: "note",
|
|
1071
|
+
NonStruct: "none",
|
|
1072
|
+
// Block level structure types
|
|
1073
|
+
P: null,
|
|
1074
|
+
// H<n>,
|
|
1075
|
+
H: "heading",
|
|
1076
|
+
Title: null,
|
|
1077
|
+
FENote: "note",
|
|
1078
|
+
// Sub-block level structure type
|
|
1079
|
+
Sub: "group",
|
|
1080
|
+
// General inline level structure types
|
|
1081
|
+
Lbl: null,
|
|
1082
|
+
Span: null,
|
|
1083
|
+
Em: null,
|
|
1084
|
+
Strong: null,
|
|
1085
|
+
Link: "link",
|
|
1086
|
+
Annot: "note",
|
|
1087
|
+
Form: "form",
|
|
1088
|
+
// Ruby and Warichu structure types
|
|
1089
|
+
Ruby: null,
|
|
1090
|
+
RB: null,
|
|
1091
|
+
RT: null,
|
|
1092
|
+
RP: null,
|
|
1093
|
+
Warichu: null,
|
|
1094
|
+
WT: null,
|
|
1095
|
+
WP: null,
|
|
1096
|
+
// List standard structure types
|
|
1097
|
+
L: "list",
|
|
1098
|
+
LI: "listitem",
|
|
1099
|
+
LBody: null,
|
|
1100
|
+
// Table standard structure types
|
|
1101
|
+
Table: "table",
|
|
1102
|
+
TR: "row",
|
|
1103
|
+
TH: "columnheader",
|
|
1104
|
+
TD: "cell",
|
|
1105
|
+
THead: "columnheader",
|
|
1106
|
+
TBody: null,
|
|
1107
|
+
TFoot: null,
|
|
1108
|
+
// Standard structure type Caption
|
|
1109
|
+
Caption: null,
|
|
1110
|
+
// Standard structure type Figure
|
|
1111
|
+
Figure: "figure",
|
|
1112
|
+
// Standard structure type Formula
|
|
1113
|
+
Formula: null,
|
|
1114
|
+
// standard structure type Artifact
|
|
1115
|
+
Artifact: null
|
|
1116
|
+
};
|
|
1117
|
+
const HEADING_PATTERN = /^H(\d+)$/;
|
|
1118
|
+
/**
|
|
1119
|
+
* @typedef {Object} StructTreeLayerBuilderOptions
|
|
1120
|
+
* @property {PDFPage} pdfPage
|
|
1121
|
+
*/
|
|
1122
|
+
|
|
1123
|
+
class StructTreeLayerBuilder {
|
|
1124
|
+
/**
|
|
1125
|
+
* @param {StructTreeLayerBuilderOptions} options
|
|
1126
|
+
*/
|
|
1127
|
+
constructor({
|
|
1128
|
+
pdfPage
|
|
1129
|
+
}) {
|
|
1130
|
+
this.pdfPage = pdfPage;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
render(structTree) {
|
|
1134
|
+
return this._walk(structTree);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
_setAttributes(structElement, htmlElement) {
|
|
1138
|
+
if (structElement.alt !== undefined) {
|
|
1139
|
+
htmlElement.setAttribute("aria-label", structElement.alt);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
if (structElement.id !== undefined) {
|
|
1143
|
+
htmlElement.setAttribute("aria-owns", structElement.id);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
_walk(node) {
|
|
1148
|
+
if (!node) {
|
|
1149
|
+
return null;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
const element = document.createElement("span");
|
|
1153
|
+
|
|
1154
|
+
if ("role" in node) {
|
|
1155
|
+
const {
|
|
1156
|
+
role
|
|
1157
|
+
} = node;
|
|
1158
|
+
const match = role.match(HEADING_PATTERN);
|
|
1159
|
+
|
|
1160
|
+
if (match) {
|
|
1161
|
+
element.setAttribute("role", "heading");
|
|
1162
|
+
element.setAttribute("aria-level", match[1]);
|
|
1163
|
+
} else if (PDF_ROLE_TO_HTML_ROLE[role]) {
|
|
1164
|
+
element.setAttribute("role", PDF_ROLE_TO_HTML_ROLE[role]);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
this._setAttributes(node, element);
|
|
1169
|
+
|
|
1170
|
+
if (node.children) {
|
|
1171
|
+
if (node.children.length === 1 && "id" in node.children[0]) {
|
|
1172
|
+
// Often there is only one content node so just set the values on the
|
|
1173
|
+
// parent node to avoid creating an extra span.
|
|
1174
|
+
this._setAttributes(node.children[0], element);
|
|
1175
|
+
} else {
|
|
1176
|
+
for (const kid of node.children) {
|
|
1177
|
+
element.appendChild(this._walk(kid));
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
return element;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
/* Copyright 2012 Mozilla Foundation
|
|
1188
|
+
*
|
|
1189
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1190
|
+
* you may not use this file except in compliance with the License.
|
|
1191
|
+
* You may obtain a copy of the License at
|
|
1192
|
+
*
|
|
1193
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1194
|
+
*
|
|
1195
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1196
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1197
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1198
|
+
* See the License for the specific language governing permissions and
|
|
1199
|
+
* limitations under the License.
|
|
1200
|
+
*/
|
|
1201
|
+
const EXPAND_DIVS_TIMEOUT = 300; // ms
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* @typedef {Object} TextLayerBuilderOptions
|
|
1205
|
+
* @property {HTMLDivElement} textLayerDiv - The text layer container.
|
|
1206
|
+
* @property {EventBus} eventBus - The application event bus.
|
|
1207
|
+
* @property {number} pageIndex - The page index.
|
|
1208
|
+
* @property {PageViewport} viewport - The viewport of the text layer.
|
|
1209
|
+
* @property {PDFFindController} findController
|
|
1210
|
+
* @property {boolean} enhanceTextSelection - Option to turn on improved
|
|
1211
|
+
* text selection.
|
|
1212
|
+
*/
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* The text layer builder provides text selection functionality for the PDF.
|
|
1216
|
+
* It does this by creating overlay divs over the PDF's text. These divs
|
|
1217
|
+
* contain text that matches the PDF text they are overlaying. This object
|
|
1218
|
+
* also provides a way to highlight text that is being searched for.
|
|
1219
|
+
*/
|
|
1220
|
+
|
|
1221
|
+
class TextLayerBuilder {
|
|
1222
|
+
constructor({
|
|
1223
|
+
textLayerDiv,
|
|
1224
|
+
eventBus,
|
|
1225
|
+
pageIndex,
|
|
1226
|
+
viewport,
|
|
1227
|
+
findController = null,
|
|
1228
|
+
enhanceTextSelection = false
|
|
1229
|
+
}) {
|
|
1230
|
+
this.textLayerDiv = textLayerDiv;
|
|
1231
|
+
this.eventBus = eventBus;
|
|
1232
|
+
this.textContent = null;
|
|
1233
|
+
this.textContentItemsStr = [];
|
|
1234
|
+
this.textContentStream = null;
|
|
1235
|
+
this.renderingDone = false;
|
|
1236
|
+
this.pageIdx = pageIndex;
|
|
1237
|
+
this.pageNumber = this.pageIdx + 1;
|
|
1238
|
+
this.matches = [];
|
|
1239
|
+
this.viewport = viewport;
|
|
1240
|
+
this.textDivs = [];
|
|
1241
|
+
this.findController = findController;
|
|
1242
|
+
this.textLayerRenderTask = null;
|
|
1243
|
+
this.enhanceTextSelection = enhanceTextSelection;
|
|
1244
|
+
this._onUpdateTextLayerMatches = null;
|
|
1245
|
+
|
|
1246
|
+
this._bindMouse();
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* @private
|
|
1250
|
+
*/
|
|
1251
|
+
|
|
1252
|
+
|
|
1253
|
+
_finishRendering() {
|
|
1254
|
+
this.renderingDone = true;
|
|
1255
|
+
|
|
1256
|
+
if (!this.enhanceTextSelection) {
|
|
1257
|
+
const endOfContent = document.createElement("div");
|
|
1258
|
+
endOfContent.className = "endOfContent";
|
|
1259
|
+
this.textLayerDiv.appendChild(endOfContent);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
this.eventBus.dispatch("textlayerrendered", {
|
|
1263
|
+
source: this,
|
|
1264
|
+
pageNumber: this.pageNumber,
|
|
1265
|
+
numTextDivs: this.textDivs.length
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* Renders the text layer.
|
|
1270
|
+
*
|
|
1271
|
+
* @param {number} [timeout] - Wait for a specified amount of milliseconds
|
|
1272
|
+
* before rendering.
|
|
1273
|
+
*/
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
render(timeout = 0) {
|
|
1277
|
+
if (!(this.textContent || this.textContentStream) || this.renderingDone) {
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
this.cancel();
|
|
1282
|
+
this.textDivs = [];
|
|
1283
|
+
const textLayerFrag = document.createDocumentFragment();
|
|
1284
|
+
this.textLayerRenderTask = renderTextLayer({
|
|
1285
|
+
textContent: this.textContent,
|
|
1286
|
+
textContentStream: this.textContentStream,
|
|
1287
|
+
container: textLayerFrag,
|
|
1288
|
+
viewport: this.viewport,
|
|
1289
|
+
textDivs: this.textDivs,
|
|
1290
|
+
textContentItemsStr: this.textContentItemsStr,
|
|
1291
|
+
timeout,
|
|
1292
|
+
enhanceTextSelection: this.enhanceTextSelection
|
|
1293
|
+
});
|
|
1294
|
+
this.textLayerRenderTask.promise.then(() => {
|
|
1295
|
+
this.textLayerDiv.appendChild(textLayerFrag);
|
|
1296
|
+
|
|
1297
|
+
this._finishRendering();
|
|
1298
|
+
|
|
1299
|
+
this._updateMatches();
|
|
1300
|
+
}, function (reason) {// Cancelled or failed to render text layer; skipping errors.
|
|
1301
|
+
});
|
|
1302
|
+
|
|
1303
|
+
if (!this._onUpdateTextLayerMatches) {
|
|
1304
|
+
this._onUpdateTextLayerMatches = evt => {
|
|
1305
|
+
if (evt.pageIndex === this.pageIdx || evt.pageIndex === -1) {
|
|
1306
|
+
this._updateMatches();
|
|
1307
|
+
}
|
|
1308
|
+
};
|
|
1309
|
+
|
|
1310
|
+
this.eventBus._on("updatetextlayermatches", this._onUpdateTextLayerMatches);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Cancel rendering of the text layer.
|
|
1315
|
+
*/
|
|
1316
|
+
|
|
1317
|
+
|
|
1318
|
+
cancel() {
|
|
1319
|
+
if (this.textLayerRenderTask) {
|
|
1320
|
+
this.textLayerRenderTask.cancel();
|
|
1321
|
+
this.textLayerRenderTask = null;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
if (this._onUpdateTextLayerMatches) {
|
|
1325
|
+
this.eventBus._off("updatetextlayermatches", this._onUpdateTextLayerMatches);
|
|
1326
|
+
|
|
1327
|
+
this._onUpdateTextLayerMatches = null;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
setTextContentStream(readableStream) {
|
|
1332
|
+
this.cancel();
|
|
1333
|
+
this.textContentStream = readableStream;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
setTextContent(textContent) {
|
|
1337
|
+
this.cancel();
|
|
1338
|
+
this.textContent = textContent;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
_convertMatches(matches, matchesLength) {
|
|
1342
|
+
// Early exit if there is nothing to convert.
|
|
1343
|
+
if (!matches) {
|
|
1344
|
+
return [];
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
const {
|
|
1348
|
+
textContentItemsStr
|
|
1349
|
+
} = this;
|
|
1350
|
+
let i = 0,
|
|
1351
|
+
iIndex = 0;
|
|
1352
|
+
const end = textContentItemsStr.length - 1;
|
|
1353
|
+
const result = [];
|
|
1354
|
+
|
|
1355
|
+
for (let m = 0, mm = matches.length; m < mm; m++) {
|
|
1356
|
+
// Calculate the start position.
|
|
1357
|
+
let matchIdx = matches[m]; // Loop over the divIdxs.
|
|
1358
|
+
|
|
1359
|
+
while (i !== end && matchIdx >= iIndex + textContentItemsStr[i].length) {
|
|
1360
|
+
iIndex += textContentItemsStr[i].length;
|
|
1361
|
+
i++;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
if (i === textContentItemsStr.length) {
|
|
1365
|
+
console.error("Could not find a matching mapping");
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
const match = {
|
|
1369
|
+
begin: {
|
|
1370
|
+
divIdx: i,
|
|
1371
|
+
offset: matchIdx - iIndex
|
|
1372
|
+
}
|
|
1373
|
+
}; // Calculate the end position.
|
|
1374
|
+
|
|
1375
|
+
matchIdx += matchesLength[m]; // Somewhat the same array as above, but use > instead of >= to get
|
|
1376
|
+
// the end position right.
|
|
1377
|
+
|
|
1378
|
+
while (i !== end && matchIdx > iIndex + textContentItemsStr[i].length) {
|
|
1379
|
+
iIndex += textContentItemsStr[i].length;
|
|
1380
|
+
i++;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
match.end = {
|
|
1384
|
+
divIdx: i,
|
|
1385
|
+
offset: matchIdx - iIndex
|
|
1386
|
+
};
|
|
1387
|
+
result.push(match);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
return result;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
_renderMatches(matches) {
|
|
1394
|
+
// Early exit if there is nothing to render.
|
|
1395
|
+
if (matches.length === 0) {
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
const {
|
|
1400
|
+
findController,
|
|
1401
|
+
pageIdx,
|
|
1402
|
+
textContentItemsStr,
|
|
1403
|
+
textDivs
|
|
1404
|
+
} = this;
|
|
1405
|
+
const isSelectedPage = pageIdx === findController.selected.pageIdx;
|
|
1406
|
+
const selectedMatchIdx = findController.selected.matchIdx;
|
|
1407
|
+
const highlightAll = findController.state.highlightAll;
|
|
1408
|
+
let prevEnd = null;
|
|
1409
|
+
const infinity = {
|
|
1410
|
+
divIdx: -1,
|
|
1411
|
+
offset: undefined
|
|
1412
|
+
};
|
|
1413
|
+
|
|
1414
|
+
function beginText(begin, className) {
|
|
1415
|
+
const divIdx = begin.divIdx;
|
|
1416
|
+
textDivs[divIdx].textContent = "";
|
|
1417
|
+
return appendTextToDiv(divIdx, 0, begin.offset, className);
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
|
|
1421
|
+
const div = textDivs[divIdx];
|
|
1422
|
+
const content = textContentItemsStr[divIdx].substring(fromOffset, toOffset);
|
|
1423
|
+
const node = document.createTextNode(content);
|
|
1424
|
+
|
|
1425
|
+
if (className) {
|
|
1426
|
+
const span = document.createElement("span");
|
|
1427
|
+
span.className = `${className} appended`;
|
|
1428
|
+
span.appendChild(node);
|
|
1429
|
+
div.appendChild(span);
|
|
1430
|
+
return className.includes("selected") ? span.offsetLeft : 0;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
div.appendChild(node);
|
|
1434
|
+
return 0;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
let i0 = selectedMatchIdx,
|
|
1438
|
+
i1 = i0 + 1;
|
|
1439
|
+
|
|
1440
|
+
if (highlightAll) {
|
|
1441
|
+
i0 = 0;
|
|
1442
|
+
i1 = matches.length;
|
|
1443
|
+
} else if (!isSelectedPage) {
|
|
1444
|
+
// Not highlighting all and this isn't the selected page, so do nothing.
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
for (let i = i0; i < i1; i++) {
|
|
1449
|
+
const match = matches[i];
|
|
1450
|
+
const begin = match.begin;
|
|
1451
|
+
const end = match.end;
|
|
1452
|
+
const isSelected = isSelectedPage && i === selectedMatchIdx;
|
|
1453
|
+
const highlightSuffix = isSelected ? " selected" : "";
|
|
1454
|
+
let selectedLeft = 0; // Match inside new div.
|
|
1455
|
+
|
|
1456
|
+
if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
|
|
1457
|
+
// If there was a previous div, then add the text at the end.
|
|
1458
|
+
if (prevEnd !== null) {
|
|
1459
|
+
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
|
|
1460
|
+
} // Clear the divs and set the content until the starting point.
|
|
1461
|
+
|
|
1462
|
+
|
|
1463
|
+
beginText(begin);
|
|
1464
|
+
} else {
|
|
1465
|
+
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
if (begin.divIdx === end.divIdx) {
|
|
1469
|
+
selectedLeft = appendTextToDiv(begin.divIdx, begin.offset, end.offset, "highlight" + highlightSuffix);
|
|
1470
|
+
} else {
|
|
1471
|
+
selectedLeft = appendTextToDiv(begin.divIdx, begin.offset, infinity.offset, "highlight begin" + highlightSuffix);
|
|
1472
|
+
|
|
1473
|
+
for (let n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
|
|
1474
|
+
textDivs[n0].className = "highlight middle" + highlightSuffix;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
beginText(end, "highlight end" + highlightSuffix);
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
prevEnd = end;
|
|
1481
|
+
|
|
1482
|
+
if (isSelected) {
|
|
1483
|
+
// Attempt to scroll the selected match into view.
|
|
1484
|
+
findController.scrollMatchIntoView({
|
|
1485
|
+
element: textDivs[begin.divIdx],
|
|
1486
|
+
selectedLeft,
|
|
1487
|
+
pageIndex: pageIdx,
|
|
1488
|
+
matchIndex: selectedMatchIdx
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
if (prevEnd) {
|
|
1494
|
+
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
_updateMatches() {
|
|
1499
|
+
// Only show matches when all rendering is done.
|
|
1500
|
+
if (!this.renderingDone) {
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
const {
|
|
1505
|
+
findController,
|
|
1506
|
+
matches,
|
|
1507
|
+
pageIdx,
|
|
1508
|
+
textContentItemsStr,
|
|
1509
|
+
textDivs
|
|
1510
|
+
} = this;
|
|
1511
|
+
let clearedUntilDivIdx = -1; // Clear all current matches.
|
|
1512
|
+
|
|
1513
|
+
for (let i = 0, ii = matches.length; i < ii; i++) {
|
|
1514
|
+
const match = matches[i];
|
|
1515
|
+
const begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
|
|
1516
|
+
|
|
1517
|
+
for (let n = begin, end = match.end.divIdx; n <= end; n++) {
|
|
1518
|
+
const div = textDivs[n];
|
|
1519
|
+
div.textContent = textContentItemsStr[n];
|
|
1520
|
+
div.className = "";
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
clearedUntilDivIdx = match.end.divIdx + 1;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
if (!(findController !== null && findController !== void 0 && findController.highlightMatches)) {
|
|
1527
|
+
return;
|
|
1528
|
+
} // Convert the matches on the `findController` into the match format
|
|
1529
|
+
// used for the textLayer.
|
|
1530
|
+
|
|
1531
|
+
|
|
1532
|
+
const pageMatches = findController.pageMatches[pageIdx] || null;
|
|
1533
|
+
const pageMatchesLength = findController.pageMatchesLength[pageIdx] || null;
|
|
1534
|
+
this.matches = this._convertMatches(pageMatches, pageMatchesLength);
|
|
1535
|
+
|
|
1536
|
+
this._renderMatches(this.matches);
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Improves text selection by adding an additional div where the mouse was
|
|
1540
|
+
* clicked. This reduces flickering of the content if the mouse is slowly
|
|
1541
|
+
* dragged up or down.
|
|
1542
|
+
*
|
|
1543
|
+
* @private
|
|
1544
|
+
*/
|
|
1545
|
+
|
|
1546
|
+
|
|
1547
|
+
_bindMouse() {
|
|
1548
|
+
const div = this.textLayerDiv;
|
|
1549
|
+
let expandDivsTimer = null;
|
|
1550
|
+
div.addEventListener("mousedown", evt => {
|
|
1551
|
+
if (this.enhanceTextSelection && this.textLayerRenderTask) {
|
|
1552
|
+
this.textLayerRenderTask.expandTextDivs(true);
|
|
1553
|
+
|
|
1554
|
+
if ((typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) && expandDivsTimer) {
|
|
1555
|
+
clearTimeout(expandDivsTimer);
|
|
1556
|
+
expandDivsTimer = null;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
const end = div.querySelector(".endOfContent");
|
|
1563
|
+
|
|
1564
|
+
if (!end) {
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) {
|
|
1569
|
+
// On non-Firefox browsers, the selection will feel better if the height
|
|
1570
|
+
// of the `endOfContent` div is adjusted to start at mouse click
|
|
1571
|
+
// location. This avoids flickering when the selection moves up.
|
|
1572
|
+
// However it does not work when selection is started on empty space.
|
|
1573
|
+
let adjustTop = evt.target !== div;
|
|
1574
|
+
|
|
1575
|
+
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
|
1576
|
+
adjustTop = adjustTop && window.getComputedStyle(end).getPropertyValue("-moz-user-select") !== "none";
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
if (adjustTop) {
|
|
1580
|
+
const divBounds = div.getBoundingClientRect();
|
|
1581
|
+
const r = Math.max(0, (evt.pageY - divBounds.top) / divBounds.height);
|
|
1582
|
+
end.style.top = (r * 100).toFixed(2) + "%";
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
end.classList.add("active");
|
|
1587
|
+
});
|
|
1588
|
+
div.addEventListener("mouseup", () => {
|
|
1589
|
+
if (this.enhanceTextSelection && this.textLayerRenderTask) {
|
|
1590
|
+
if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) {
|
|
1591
|
+
expandDivsTimer = setTimeout(() => {
|
|
1592
|
+
if (this.textLayerRenderTask) {
|
|
1593
|
+
this.textLayerRenderTask.expandTextDivs(false);
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
expandDivsTimer = null;
|
|
1597
|
+
}, EXPAND_DIVS_TIMEOUT);
|
|
1598
|
+
} else {
|
|
1599
|
+
this.textLayerRenderTask.expandTextDivs(false);
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
const end = div.querySelector(".endOfContent");
|
|
1606
|
+
|
|
1607
|
+
if (!end) {
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) {
|
|
1612
|
+
end.style.top = "";
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
end.classList.remove("active");
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
/* Copyright 2021 Mozilla Foundation
|
|
1622
|
+
*
|
|
1623
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1624
|
+
* you may not use this file except in compliance with the License.
|
|
1625
|
+
* You may obtain a copy of the License at
|
|
1626
|
+
*
|
|
1627
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1628
|
+
*
|
|
1629
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1630
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1631
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1632
|
+
* See the License for the specific language governing permissions and
|
|
1633
|
+
* limitations under the License.
|
|
1634
|
+
*/
|
|
1635
|
+
/**
|
|
1636
|
+
* @typedef {Object} XfaLayerBuilderOptions
|
|
1637
|
+
* @property {HTMLDivElement} pageDiv
|
|
1638
|
+
* @property {PDFPage} pdfPage
|
|
1639
|
+
* @property {AnnotationStorage} [annotationStorage]
|
|
1640
|
+
*/
|
|
1641
|
+
|
|
1642
|
+
class XfaLayerBuilder {
|
|
1643
|
+
/**
|
|
1644
|
+
* @param {XfaLayerBuilderOptions} options
|
|
1645
|
+
*/
|
|
1646
|
+
constructor({
|
|
1647
|
+
pageDiv,
|
|
1648
|
+
pdfPage,
|
|
1649
|
+
xfaHtml,
|
|
1650
|
+
annotationStorage
|
|
1651
|
+
}) {
|
|
1652
|
+
this.pageDiv = pageDiv;
|
|
1653
|
+
this.pdfPage = pdfPage;
|
|
1654
|
+
this.xfaHtml = xfaHtml;
|
|
1655
|
+
this.annotationStorage = annotationStorage;
|
|
1656
|
+
this.div = null;
|
|
1657
|
+
this._cancelled = false;
|
|
1658
|
+
}
|
|
1659
|
+
/**
|
|
1660
|
+
* @param {PageViewport} viewport
|
|
1661
|
+
* @param {string} intent (default value is 'display')
|
|
1662
|
+
* @returns {Promise<void>} A promise that is resolved when rendering of the
|
|
1663
|
+
* annotations is complete.
|
|
1664
|
+
*/
|
|
1665
|
+
|
|
1666
|
+
|
|
1667
|
+
render(viewport, intent = "display") {
|
|
1668
|
+
if (intent === "print") {
|
|
1669
|
+
const parameters = {
|
|
1670
|
+
viewport: viewport.clone({
|
|
1671
|
+
dontFlip: true
|
|
1672
|
+
}),
|
|
1673
|
+
div: this.div,
|
|
1674
|
+
xfa: this.xfaHtml,
|
|
1675
|
+
page: null,
|
|
1676
|
+
annotationStorage: this.annotationStorage,
|
|
1677
|
+
intent
|
|
1678
|
+
}; // Create an xfa layer div and render the form
|
|
1679
|
+
|
|
1680
|
+
const div = document.createElement("div");
|
|
1681
|
+
this.pageDiv.appendChild(div);
|
|
1682
|
+
parameters.div = div;
|
|
1683
|
+
XfaLayer.render(parameters);
|
|
1684
|
+
return Promise.resolve();
|
|
1685
|
+
} // intent === "display"
|
|
1686
|
+
|
|
1687
|
+
|
|
1688
|
+
return this.pdfPage.getXfa().then(xfa => {
|
|
1689
|
+
if (this._cancelled) {
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
const parameters = {
|
|
1694
|
+
viewport: viewport.clone({
|
|
1695
|
+
dontFlip: true
|
|
1696
|
+
}),
|
|
1697
|
+
div: this.div,
|
|
1698
|
+
xfa,
|
|
1699
|
+
page: this.pdfPage,
|
|
1700
|
+
annotationStorage: this.annotationStorage,
|
|
1701
|
+
intent
|
|
1702
|
+
};
|
|
1703
|
+
|
|
1704
|
+
if (this.div) {
|
|
1705
|
+
XfaLayer.update(parameters);
|
|
1706
|
+
} else {
|
|
1707
|
+
// Create an xfa layer div and render the form
|
|
1708
|
+
this.div = document.createElement("div");
|
|
1709
|
+
this.pageDiv.appendChild(this.div);
|
|
1710
|
+
parameters.div = this.div;
|
|
1711
|
+
XfaLayer.render(parameters);
|
|
1712
|
+
}
|
|
1713
|
+
}).catch(error => {
|
|
1714
|
+
console.error(error);
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
cancel() {
|
|
1719
|
+
this._cancelled = true;
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
hide() {
|
|
1723
|
+
if (!this.div) {
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
this.div.hidden = true;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
/* Copyright 2014 Mozilla Foundation
|
|
1733
|
+
*
|
|
1734
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1735
|
+
* you may not use this file except in compliance with the License.
|
|
1736
|
+
* You may obtain a copy of the License at
|
|
1737
|
+
*
|
|
1738
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1739
|
+
*
|
|
1740
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1741
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1742
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1743
|
+
* See the License for the specific language governing permissions and
|
|
1744
|
+
* limitations under the License.
|
|
1745
|
+
*/
|
|
1746
|
+
const DEFAULT_CACHE_SIZE = 10;
|
|
1747
|
+
/**
|
|
1748
|
+
* @typedef {Object} PDFViewerOptions
|
|
1749
|
+
* @property {HTMLDivElement} container - The container for the viewer element.
|
|
1750
|
+
* @property {HTMLDivElement} [viewer] - The viewer element.
|
|
1751
|
+
* @property {EventBus} eventBus - The application event bus.
|
|
1752
|
+
* @property {IPDFLinkService} linkService - The navigation/linking service.
|
|
1753
|
+
* @property {DownloadManager} [downloadManager] - The download manager
|
|
1754
|
+
* component.
|
|
1755
|
+
* @property {PDFFindController} [findController] - The find controller
|
|
1756
|
+
* component.
|
|
1757
|
+
* @property {PDFScriptingManager} [scriptingManager] - The scripting manager
|
|
1758
|
+
* component.
|
|
1759
|
+
* @property {PDFRenderingQueue} [renderingQueue] - The rendering queue object.
|
|
1760
|
+
* @property {boolean} [removePageBorders] - Removes the border shadow around
|
|
1761
|
+
* the pages. The default value is `false`.
|
|
1762
|
+
* @property {number} [textLayerMode] - Controls if the text layer used for
|
|
1763
|
+
* selection and searching is created, and if the improved text selection
|
|
1764
|
+
* behaviour is enabled. The constants from {TextLayerMode} should be used.
|
|
1765
|
+
* The default value is `TextLayerMode.ENABLE`.
|
|
1766
|
+
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
|
1767
|
+
* mainly for annotation icons. Include trailing slash.
|
|
1768
|
+
* @property {boolean} [renderInteractiveForms] - Enables rendering of
|
|
1769
|
+
* interactive form elements. The default value is `true`.
|
|
1770
|
+
* @property {boolean} [enablePrintAutoRotate] - Enables automatic rotation of
|
|
1771
|
+
* landscape pages upon printing. The default is `false`.
|
|
1772
|
+
* @property {string} renderer - 'canvas' or 'svg'. The default is 'canvas'.
|
|
1773
|
+
* @property {boolean} [useOnlyCssZoom] - Enables CSS only zooming. The default
|
|
1774
|
+
* value is `false`.
|
|
1775
|
+
* @property {number} [maxCanvasPixels] - The maximum supported canvas size in
|
|
1776
|
+
* total pixels, i.e. width * height. Use -1 for no limit. The default value
|
|
1777
|
+
* is 4096 * 4096 (16 mega-pixels).
|
|
1778
|
+
* @property {IL10n} l10n - Localization service.
|
|
1779
|
+
* @property {boolean} [enableScripting] - Enable embedded script execution
|
|
1780
|
+
* (also requires {scriptingManager} being set). The default value is `false`.
|
|
1781
|
+
*/
|
|
1782
|
+
|
|
1783
|
+
function PDFPageViewBuffer(size) {
|
|
1784
|
+
const data = [];
|
|
1785
|
+
|
|
1786
|
+
this.push = function (view) {
|
|
1787
|
+
const i = data.indexOf(view);
|
|
1788
|
+
|
|
1789
|
+
if (i >= 0) {
|
|
1790
|
+
data.splice(i, 1);
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
data.push(view);
|
|
1794
|
+
|
|
1795
|
+
if (data.length > size) {
|
|
1796
|
+
data.shift().destroy();
|
|
1797
|
+
}
|
|
1798
|
+
};
|
|
1799
|
+
/**
|
|
1800
|
+
* After calling resize, the size of the buffer will be newSize. The optional
|
|
1801
|
+
* parameter pagesToKeep is, if present, an array of pages to push to the back
|
|
1802
|
+
* of the buffer, delaying their destruction. The size of pagesToKeep has no
|
|
1803
|
+
* impact on the final size of the buffer; if pagesToKeep has length larger
|
|
1804
|
+
* than newSize, some of those pages will be destroyed anyway.
|
|
1805
|
+
*/
|
|
1806
|
+
|
|
1807
|
+
|
|
1808
|
+
this.resize = function (newSize, pagesToKeep) {
|
|
1809
|
+
size = newSize;
|
|
1810
|
+
|
|
1811
|
+
if (pagesToKeep) {
|
|
1812
|
+
const pageIdsToKeep = new Set();
|
|
1813
|
+
|
|
1814
|
+
for (let i = 0, iMax = pagesToKeep.length; i < iMax; ++i) {
|
|
1815
|
+
pageIdsToKeep.add(pagesToKeep[i].id);
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
moveToEndOfArray(data, function (page) {
|
|
1819
|
+
return pageIdsToKeep.has(page.id);
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
while (data.length > size) {
|
|
1824
|
+
data.shift().destroy();
|
|
1825
|
+
}
|
|
1826
|
+
};
|
|
1827
|
+
|
|
1828
|
+
this.has = function (view) {
|
|
1829
|
+
return data.includes(view);
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
function isSameScale(oldScale, newScale) {
|
|
1834
|
+
if (newScale === oldScale) {
|
|
1835
|
+
return true;
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
if (Math.abs(newScale - oldScale) < 1e-15) {
|
|
1839
|
+
// Prevent unnecessary re-rendering of all pages when the scale
|
|
1840
|
+
// changes only because of limited numerical precision.
|
|
1841
|
+
return true;
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
return false;
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1847
|
+
* Simple viewer control to display PDF content/pages.
|
|
1848
|
+
* @implements {IRenderableView}
|
|
1849
|
+
*/
|
|
1850
|
+
|
|
1851
|
+
|
|
1852
|
+
class BaseViewer {
|
|
1853
|
+
/**
|
|
1854
|
+
* @param {PDFViewerOptions} options
|
|
1855
|
+
*/
|
|
1856
|
+
constructor(options) {
|
|
1857
|
+
if (this.constructor === BaseViewer) {
|
|
1858
|
+
throw new Error("Cannot initialize BaseViewer.");
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
const viewerVersion = typeof PDFJSDev !== "undefined" ? PDFJSDev.eval("BUNDLE_VERSION") : null;
|
|
1862
|
+
|
|
1863
|
+
if (version !== viewerVersion) {
|
|
1864
|
+
throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`);
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
this._name = this.constructor.name;
|
|
1868
|
+
this.container = options.container;
|
|
1869
|
+
this.viewer = options.viewer || options.container.firstElementChild;
|
|
1870
|
+
|
|
1871
|
+
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("!PRODUCTION || GENERIC")) {
|
|
1872
|
+
var _this$container, _this$viewer;
|
|
1873
|
+
|
|
1874
|
+
if (!(((_this$container = this.container) === null || _this$container === void 0 ? void 0 : _this$container.tagName.toUpperCase()) === "DIV" && ((_this$viewer = this.viewer) === null || _this$viewer === void 0 ? void 0 : _this$viewer.tagName.toUpperCase()) === "DIV")) {
|
|
1875
|
+
throw new Error("Invalid `container` and/or `viewer` option.");
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
if (this.container.offsetParent && getComputedStyle(this.container).position !== "absolute") {
|
|
1879
|
+
throw new Error("The `container` must be absolutely positioned.");
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
this.eventBus = options.eventBus;
|
|
1884
|
+
this.linkService = options.linkService || new SimpleLinkService();
|
|
1885
|
+
this.downloadManager = options.downloadManager || null;
|
|
1886
|
+
this.findController = options.findController || null;
|
|
1887
|
+
this._scriptingManager = options.scriptingManager || null;
|
|
1888
|
+
this.removePageBorders = options.removePageBorders || false;
|
|
1889
|
+
this.textLayerMode = Number.isInteger(options.textLayerMode) ? options.textLayerMode : TextLayerMode.ENABLE;
|
|
1890
|
+
this.imageResourcesPath = options.imageResourcesPath || "";
|
|
1891
|
+
this.renderInteractiveForms = options.renderInteractiveForms !== false;
|
|
1892
|
+
this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
|
|
1893
|
+
this.renderer = options.renderer || RendererType.CANVAS;
|
|
1894
|
+
this.useOnlyCssZoom = options.useOnlyCssZoom || false;
|
|
1895
|
+
this.maxCanvasPixels = options.maxCanvasPixels;
|
|
1896
|
+
this.l10n = options.l10n || NullL10n;
|
|
1897
|
+
this.enableScripting = options.enableScripting === true && !!this._scriptingManager;
|
|
1898
|
+
this.defaultRenderingQueue = !options.renderingQueue;
|
|
1899
|
+
|
|
1900
|
+
if (this.defaultRenderingQueue) {
|
|
1901
|
+
// Custom rendering queue is not specified, using default one
|
|
1902
|
+
this.renderingQueue = new PDFRenderingQueue();
|
|
1903
|
+
this.renderingQueue.setViewer(this);
|
|
1904
|
+
} else {
|
|
1905
|
+
this.renderingQueue = options.renderingQueue;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this));
|
|
1909
|
+
this.presentationModeState = PresentationModeState.UNKNOWN;
|
|
1910
|
+
this._onBeforeDraw = this._onAfterDraw = null;
|
|
1911
|
+
|
|
1912
|
+
this._resetView();
|
|
1913
|
+
|
|
1914
|
+
if (this.removePageBorders) {
|
|
1915
|
+
this.viewer.classList.add("removePageBorders");
|
|
1916
|
+
} // Defer the dispatching of this event, to give other viewer components
|
|
1917
|
+
// time to initialize *and* register 'baseviewerinit' event listeners.
|
|
1918
|
+
|
|
1919
|
+
|
|
1920
|
+
Promise.resolve().then(() => {
|
|
1921
|
+
this.eventBus.dispatch("baseviewerinit", {
|
|
1922
|
+
source: this
|
|
1923
|
+
});
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
get pagesCount() {
|
|
1928
|
+
return this._pages.length;
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
getPageView(index) {
|
|
1932
|
+
return this._pages[index];
|
|
1933
|
+
}
|
|
1934
|
+
/**
|
|
1935
|
+
* @type {boolean} - True if all {PDFPageView} objects are initialized.
|
|
1936
|
+
*/
|
|
1937
|
+
|
|
1938
|
+
|
|
1939
|
+
get pageViewsReady() {
|
|
1940
|
+
if (!this._pagesCapability.settled) {
|
|
1941
|
+
return false;
|
|
1942
|
+
} // Prevent printing errors when 'disableAutoFetch' is set, by ensuring
|
|
1943
|
+
// that *all* pages have in fact been completely loaded.
|
|
1944
|
+
|
|
1945
|
+
|
|
1946
|
+
return this._pages.every(function (pageView) {
|
|
1947
|
+
return pageView === null || pageView === void 0 ? void 0 : pageView.pdfPage;
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* @type {number}
|
|
1952
|
+
*/
|
|
1953
|
+
|
|
1954
|
+
|
|
1955
|
+
get currentPageNumber() {
|
|
1956
|
+
return this._currentPageNumber;
|
|
1957
|
+
}
|
|
1958
|
+
/**
|
|
1959
|
+
* @param {number} val - The page number.
|
|
1960
|
+
*/
|
|
1961
|
+
|
|
1962
|
+
|
|
1963
|
+
set currentPageNumber(val) {
|
|
1964
|
+
if (!Number.isInteger(val)) {
|
|
1965
|
+
throw new Error("Invalid page number.");
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
if (!this.pdfDocument) {
|
|
1969
|
+
return;
|
|
1970
|
+
} // The intent can be to just reset a scroll position and/or scale.
|
|
1971
|
+
|
|
1972
|
+
|
|
1973
|
+
if (!this._setCurrentPageNumber(val,
|
|
1974
|
+
/* resetCurrentPageView = */
|
|
1975
|
+
true)) {
|
|
1976
|
+
console.error(`${this._name}.currentPageNumber: "${val}" is not a valid page.`);
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* @returns {boolean} Whether the pageNumber is valid (within bounds).
|
|
1981
|
+
* @private
|
|
1982
|
+
*/
|
|
1983
|
+
|
|
1984
|
+
|
|
1985
|
+
_setCurrentPageNumber(val, resetCurrentPageView = false) {
|
|
1986
|
+
var _this$_pageLabels, _this$_pageLabels2;
|
|
1987
|
+
|
|
1988
|
+
if (this._currentPageNumber === val) {
|
|
1989
|
+
if (resetCurrentPageView) {
|
|
1990
|
+
this._resetCurrentPageView();
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
return true;
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
if (!(0 < val && val <= this.pagesCount)) {
|
|
1997
|
+
return false;
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
const previous = this._currentPageNumber;
|
|
2001
|
+
this._currentPageNumber = val;
|
|
2002
|
+
this.eventBus.dispatch("pagechanging", {
|
|
2003
|
+
source: this,
|
|
2004
|
+
pageNumber: val,
|
|
2005
|
+
pageLabel: (_this$_pageLabels = (_this$_pageLabels2 = this._pageLabels) === null || _this$_pageLabels2 === void 0 ? void 0 : _this$_pageLabels2[val - 1]) !== null && _this$_pageLabels !== void 0 ? _this$_pageLabels : null,
|
|
2006
|
+
previous
|
|
2007
|
+
});
|
|
2008
|
+
|
|
2009
|
+
if (resetCurrentPageView) {
|
|
2010
|
+
this._resetCurrentPageView();
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
return true;
|
|
2014
|
+
}
|
|
2015
|
+
/**
|
|
2016
|
+
* @type {string|null} Returns the current page label, or `null` if no page
|
|
2017
|
+
* labels exist.
|
|
2018
|
+
*/
|
|
2019
|
+
|
|
2020
|
+
|
|
2021
|
+
get currentPageLabel() {
|
|
2022
|
+
var _this$_pageLabels3, _this$_pageLabels4;
|
|
2023
|
+
|
|
2024
|
+
return (_this$_pageLabels3 = (_this$_pageLabels4 = this._pageLabels) === null || _this$_pageLabels4 === void 0 ? void 0 : _this$_pageLabels4[this._currentPageNumber - 1]) !== null && _this$_pageLabels3 !== void 0 ? _this$_pageLabels3 : null;
|
|
2025
|
+
}
|
|
2026
|
+
/**
|
|
2027
|
+
* @param {string} val - The page label.
|
|
2028
|
+
*/
|
|
2029
|
+
|
|
2030
|
+
|
|
2031
|
+
set currentPageLabel(val) {
|
|
2032
|
+
if (!this.pdfDocument) {
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
let page = val | 0; // Fallback page number.
|
|
2037
|
+
|
|
2038
|
+
if (this._pageLabels) {
|
|
2039
|
+
const i = this._pageLabels.indexOf(val);
|
|
2040
|
+
|
|
2041
|
+
if (i >= 0) {
|
|
2042
|
+
page = i + 1;
|
|
2043
|
+
}
|
|
2044
|
+
} // The intent can be to just reset a scroll position and/or scale.
|
|
2045
|
+
|
|
2046
|
+
|
|
2047
|
+
if (!this._setCurrentPageNumber(page,
|
|
2048
|
+
/* resetCurrentPageView = */
|
|
2049
|
+
true)) {
|
|
2050
|
+
console.error(`${this._name}.currentPageLabel: "${val}" is not a valid page.`);
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
/**
|
|
2054
|
+
* @type {number}
|
|
2055
|
+
*/
|
|
2056
|
+
|
|
2057
|
+
|
|
2058
|
+
get currentScale() {
|
|
2059
|
+
return this._currentScale !== UNKNOWN_SCALE ? this._currentScale : DEFAULT_SCALE;
|
|
2060
|
+
}
|
|
2061
|
+
/**
|
|
2062
|
+
* @param {number} val - Scale of the pages in percents.
|
|
2063
|
+
*/
|
|
2064
|
+
|
|
2065
|
+
|
|
2066
|
+
set currentScale(val) {
|
|
2067
|
+
if (isNaN(val)) {
|
|
2068
|
+
throw new Error("Invalid numeric scale.");
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
if (!this.pdfDocument) {
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
this._setScale(val, false);
|
|
2076
|
+
}
|
|
2077
|
+
/**
|
|
2078
|
+
* @type {string}
|
|
2079
|
+
*/
|
|
2080
|
+
|
|
2081
|
+
|
|
2082
|
+
get currentScaleValue() {
|
|
2083
|
+
return this._currentScaleValue;
|
|
2084
|
+
}
|
|
2085
|
+
/**
|
|
2086
|
+
* @param val - The scale of the pages (in percent or predefined value).
|
|
2087
|
+
*/
|
|
2088
|
+
|
|
2089
|
+
|
|
2090
|
+
set currentScaleValue(val) {
|
|
2091
|
+
if (!this.pdfDocument) {
|
|
2092
|
+
return;
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
this._setScale(val, false);
|
|
2096
|
+
}
|
|
2097
|
+
/**
|
|
2098
|
+
* @type {number}
|
|
2099
|
+
*/
|
|
2100
|
+
|
|
2101
|
+
|
|
2102
|
+
get pagesRotation() {
|
|
2103
|
+
return this._pagesRotation;
|
|
2104
|
+
}
|
|
2105
|
+
/**
|
|
2106
|
+
* @param {number} rotation - The rotation of the pages (0, 90, 180, 270).
|
|
2107
|
+
*/
|
|
2108
|
+
|
|
2109
|
+
|
|
2110
|
+
set pagesRotation(rotation) {
|
|
2111
|
+
if (!isValidRotation(rotation)) {
|
|
2112
|
+
throw new Error("Invalid pages rotation angle.");
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
if (!this.pdfDocument) {
|
|
2116
|
+
return;
|
|
2117
|
+
} // Normalize the rotation, by clamping it to the [0, 360) range.
|
|
2118
|
+
|
|
2119
|
+
|
|
2120
|
+
rotation %= 360;
|
|
2121
|
+
|
|
2122
|
+
if (rotation < 0) {
|
|
2123
|
+
rotation += 360;
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
if (this._pagesRotation === rotation) {
|
|
2127
|
+
return; // The rotation didn't change.
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
this._pagesRotation = rotation;
|
|
2131
|
+
const pageNumber = this._currentPageNumber;
|
|
2132
|
+
|
|
2133
|
+
for (let i = 0, ii = this._pages.length; i < ii; i++) {
|
|
2134
|
+
const pageView = this._pages[i];
|
|
2135
|
+
pageView.update(pageView.scale, rotation);
|
|
2136
|
+
} // Prevent errors in case the rotation changes *before* the scale has been
|
|
2137
|
+
// set to a non-default value.
|
|
2138
|
+
|
|
2139
|
+
|
|
2140
|
+
if (this._currentScaleValue) {
|
|
2141
|
+
this._setScale(this._currentScaleValue, true);
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
this.eventBus.dispatch("rotationchanging", {
|
|
2145
|
+
source: this,
|
|
2146
|
+
pagesRotation: rotation,
|
|
2147
|
+
pageNumber
|
|
2148
|
+
});
|
|
2149
|
+
|
|
2150
|
+
if (this.defaultRenderingQueue) {
|
|
2151
|
+
this.update();
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
get firstPagePromise() {
|
|
2156
|
+
return this.pdfDocument ? this._firstPageCapability.promise : null;
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
get onePageRendered() {
|
|
2160
|
+
return this.pdfDocument ? this._onePageRenderedCapability.promise : null;
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
get pagesPromise() {
|
|
2164
|
+
return this.pdfDocument ? this._pagesCapability.promise : null;
|
|
2165
|
+
}
|
|
2166
|
+
/**
|
|
2167
|
+
* @private
|
|
2168
|
+
*/
|
|
2169
|
+
|
|
2170
|
+
|
|
2171
|
+
get _viewerElement() {
|
|
2172
|
+
// In most viewers, e.g. `PDFViewer`, this should return `this.viewer`.
|
|
2173
|
+
throw new Error("Not implemented: _viewerElement");
|
|
2174
|
+
}
|
|
2175
|
+
/**
|
|
2176
|
+
* @private
|
|
2177
|
+
*/
|
|
2178
|
+
|
|
2179
|
+
|
|
2180
|
+
_onePageRenderedOrForceFetch() {
|
|
2181
|
+
// Unless the viewer *and* its pages are visible, rendering won't start and
|
|
2182
|
+
// `this._onePageRenderedCapability` thus won't be resolved.
|
|
2183
|
+
// To ensure that automatic printing, on document load, still works even in
|
|
2184
|
+
// those cases we force-allow fetching of all pages when:
|
|
2185
|
+
// - The viewer is hidden in the DOM, e.g. in a `display: none` <iframe>
|
|
2186
|
+
// element; fixes bug 1618621.
|
|
2187
|
+
// - The viewer is visible, but none of the pages are (e.g. if the
|
|
2188
|
+
// viewer is very small); fixes bug 1618955.
|
|
2189
|
+
if (!this.container.offsetParent || this._getVisiblePages().views.length === 0) {
|
|
2190
|
+
return Promise.resolve();
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
return this._onePageRenderedCapability.promise;
|
|
2194
|
+
}
|
|
2195
|
+
/**
|
|
2196
|
+
* @param pdfDocument {PDFDocument}
|
|
2197
|
+
*/
|
|
2198
|
+
|
|
2199
|
+
|
|
2200
|
+
setDocument(pdfDocument) {
|
|
2201
|
+
if (this.pdfDocument) {
|
|
2202
|
+
this.eventBus.dispatch("pagesdestroy", {
|
|
2203
|
+
source: this
|
|
2204
|
+
});
|
|
2205
|
+
|
|
2206
|
+
this._cancelRendering();
|
|
2207
|
+
|
|
2208
|
+
this._resetView();
|
|
2209
|
+
|
|
2210
|
+
if (this.findController) {
|
|
2211
|
+
this.findController.setDocument(null);
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
if (this._scriptingManager) {
|
|
2215
|
+
this._scriptingManager.setDocument(null);
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
this.pdfDocument = pdfDocument;
|
|
2220
|
+
|
|
2221
|
+
if (!pdfDocument) {
|
|
2222
|
+
return;
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
const isPureXfa = pdfDocument.isPureXfa;
|
|
2226
|
+
const pagesCount = pdfDocument.numPages;
|
|
2227
|
+
const firstPagePromise = pdfDocument.getPage(1); // Rendering (potentially) depends on this, hence fetching it immediately.
|
|
2228
|
+
|
|
2229
|
+
const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig();
|
|
2230
|
+
|
|
2231
|
+
this._pagesCapability.promise.then(() => {
|
|
2232
|
+
this.eventBus.dispatch("pagesloaded", {
|
|
2233
|
+
source: this,
|
|
2234
|
+
pagesCount
|
|
2235
|
+
});
|
|
2236
|
+
});
|
|
2237
|
+
|
|
2238
|
+
this._onBeforeDraw = evt => {
|
|
2239
|
+
const pageView = this._pages[evt.pageNumber - 1];
|
|
2240
|
+
|
|
2241
|
+
if (!pageView) {
|
|
2242
|
+
return;
|
|
2243
|
+
} // Add the page to the buffer at the start of drawing. That way it can be
|
|
2244
|
+
// evicted from the buffer and destroyed even if we pause its rendering.
|
|
2245
|
+
|
|
2246
|
+
|
|
2247
|
+
this._buffer.push(pageView);
|
|
2248
|
+
};
|
|
2249
|
+
|
|
2250
|
+
this.eventBus._on("pagerender", this._onBeforeDraw);
|
|
2251
|
+
|
|
2252
|
+
this._onAfterDraw = evt => {
|
|
2253
|
+
if (evt.cssTransform || this._onePageRenderedCapability.settled) {
|
|
2254
|
+
return;
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
this._onePageRenderedCapability.resolve();
|
|
2258
|
+
|
|
2259
|
+
this.eventBus._off("pagerendered", this._onAfterDraw);
|
|
2260
|
+
|
|
2261
|
+
this._onAfterDraw = null;
|
|
2262
|
+
};
|
|
2263
|
+
|
|
2264
|
+
this.eventBus._on("pagerendered", this._onAfterDraw); // Fetch a single page so we can get a viewport that will be the default
|
|
2265
|
+
// viewport for all pages
|
|
2266
|
+
|
|
2267
|
+
|
|
2268
|
+
firstPagePromise.then(firstPdfPage => {
|
|
2269
|
+
this._firstPageCapability.resolve(firstPdfPage);
|
|
2270
|
+
|
|
2271
|
+
this._optionalContentConfigPromise = optionalContentConfigPromise;
|
|
2272
|
+
const scale = this.currentScale;
|
|
2273
|
+
const viewport = firstPdfPage.getViewport({
|
|
2274
|
+
scale: scale * CSS_UNITS
|
|
2275
|
+
});
|
|
2276
|
+
const textLayerFactory = this.textLayerMode !== TextLayerMode.DISABLE ? this : null;
|
|
2277
|
+
const xfaLayerFactory = isPureXfa ? this : null;
|
|
2278
|
+
|
|
2279
|
+
for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
|
|
2280
|
+
const pageView = new PDFPageView({
|
|
2281
|
+
container: this._viewerElement,
|
|
2282
|
+
eventBus: this.eventBus,
|
|
2283
|
+
id: pageNum,
|
|
2284
|
+
scale,
|
|
2285
|
+
defaultViewport: viewport.clone(),
|
|
2286
|
+
optionalContentConfigPromise,
|
|
2287
|
+
renderingQueue: this.renderingQueue,
|
|
2288
|
+
textLayerFactory,
|
|
2289
|
+
textLayerMode: this.textLayerMode,
|
|
2290
|
+
annotationLayerFactory: this,
|
|
2291
|
+
xfaLayerFactory,
|
|
2292
|
+
structTreeLayerFactory: this,
|
|
2293
|
+
imageResourcesPath: this.imageResourcesPath,
|
|
2294
|
+
renderInteractiveForms: this.renderInteractiveForms,
|
|
2295
|
+
renderer: this.renderer,
|
|
2296
|
+
useOnlyCssZoom: this.useOnlyCssZoom,
|
|
2297
|
+
maxCanvasPixels: this.maxCanvasPixels,
|
|
2298
|
+
l10n: this.l10n
|
|
2299
|
+
});
|
|
2300
|
+
|
|
2301
|
+
this._pages.push(pageView);
|
|
2302
|
+
} // Set the first `pdfPage` immediately, since it's already loaded,
|
|
2303
|
+
// rather than having to repeat the `PDFDocumentProxy.getPage` call in
|
|
2304
|
+
// the `this._ensurePdfPageLoaded` method before rendering can start.
|
|
2305
|
+
|
|
2306
|
+
|
|
2307
|
+
const firstPageView = this._pages[0];
|
|
2308
|
+
|
|
2309
|
+
if (firstPageView) {
|
|
2310
|
+
firstPageView.setPdfPage(firstPdfPage);
|
|
2311
|
+
this.linkService.cachePageRef(1, firstPdfPage.ref);
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
if (this._spreadMode !== SpreadMode.NONE) {
|
|
2315
|
+
this._updateSpreadMode();
|
|
2316
|
+
} // Fetch all the pages since the viewport is needed before printing
|
|
2317
|
+
// starts to create the correct size canvas. Wait until one page is
|
|
2318
|
+
// rendered so we don't tie up too many resources early on.
|
|
2319
|
+
|
|
2320
|
+
|
|
2321
|
+
this._onePageRenderedOrForceFetch().then(() => {
|
|
2322
|
+
if (this.findController) {
|
|
2323
|
+
this.findController.setDocument(pdfDocument); // Enable searching.
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
if (this.enableScripting) {
|
|
2327
|
+
this._scriptingManager.setDocument(pdfDocument);
|
|
2328
|
+
} // In addition to 'disableAutoFetch' being set, also attempt to reduce
|
|
2329
|
+
// resource usage when loading *very* long/large documents.
|
|
2330
|
+
|
|
2331
|
+
|
|
2332
|
+
if (pdfDocument.loadingParams.disableAutoFetch || pagesCount > 7500) {
|
|
2333
|
+
// XXX: Printing is semi-broken with auto fetch disabled.
|
|
2334
|
+
this._pagesCapability.resolve();
|
|
2335
|
+
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
let getPagesLeft = pagesCount - 1; // The first page was already loaded.
|
|
2340
|
+
|
|
2341
|
+
if (getPagesLeft <= 0) {
|
|
2342
|
+
this._pagesCapability.resolve();
|
|
2343
|
+
|
|
2344
|
+
return;
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
for (let pageNum = 2; pageNum <= pagesCount; ++pageNum) {
|
|
2348
|
+
pdfDocument.getPage(pageNum).then(pdfPage => {
|
|
2349
|
+
const pageView = this._pages[pageNum - 1];
|
|
2350
|
+
|
|
2351
|
+
if (!pageView.pdfPage) {
|
|
2352
|
+
pageView.setPdfPage(pdfPage);
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
this.linkService.cachePageRef(pageNum, pdfPage.ref);
|
|
2356
|
+
|
|
2357
|
+
if (--getPagesLeft === 0) {
|
|
2358
|
+
this._pagesCapability.resolve();
|
|
2359
|
+
}
|
|
2360
|
+
}, reason => {
|
|
2361
|
+
console.error(`Unable to get page ${pageNum} to initialize viewer`, reason);
|
|
2362
|
+
|
|
2363
|
+
if (--getPagesLeft === 0) {
|
|
2364
|
+
this._pagesCapability.resolve();
|
|
2365
|
+
}
|
|
2366
|
+
});
|
|
2367
|
+
}
|
|
2368
|
+
});
|
|
2369
|
+
|
|
2370
|
+
this.eventBus.dispatch("pagesinit", {
|
|
2371
|
+
source: this
|
|
2372
|
+
});
|
|
2373
|
+
|
|
2374
|
+
if (this.defaultRenderingQueue) {
|
|
2375
|
+
this.update();
|
|
2376
|
+
}
|
|
2377
|
+
}).catch(reason => {
|
|
2378
|
+
console.error("Unable to initialize viewer", reason);
|
|
2379
|
+
});
|
|
2380
|
+
}
|
|
2381
|
+
/**
|
|
2382
|
+
* @param {Array|null} labels
|
|
2383
|
+
*/
|
|
2384
|
+
|
|
2385
|
+
|
|
2386
|
+
setPageLabels(labels) {
|
|
2387
|
+
if (!this.pdfDocument) {
|
|
2388
|
+
return;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
if (!labels) {
|
|
2392
|
+
this._pageLabels = null;
|
|
2393
|
+
} else if (!(Array.isArray(labels) && this.pdfDocument.numPages === labels.length)) {
|
|
2394
|
+
this._pageLabels = null;
|
|
2395
|
+
console.error(`${this._name}.setPageLabels: Invalid page labels.`);
|
|
2396
|
+
} else {
|
|
2397
|
+
this._pageLabels = labels;
|
|
2398
|
+
} // Update all the `PDFPageView` instances.
|
|
2399
|
+
|
|
2400
|
+
|
|
2401
|
+
for (let i = 0, ii = this._pages.length; i < ii; i++) {
|
|
2402
|
+
var _this$_pageLabels$i, _this$_pageLabels5;
|
|
2403
|
+
|
|
2404
|
+
this._pages[i].setPageLabel((_this$_pageLabels$i = (_this$_pageLabels5 = this._pageLabels) === null || _this$_pageLabels5 === void 0 ? void 0 : _this$_pageLabels5[i]) !== null && _this$_pageLabels$i !== void 0 ? _this$_pageLabels$i : null);
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
_resetView() {
|
|
2409
|
+
this._pages = [];
|
|
2410
|
+
this._currentPageNumber = 1;
|
|
2411
|
+
this._currentScale = UNKNOWN_SCALE;
|
|
2412
|
+
this._currentScaleValue = null;
|
|
2413
|
+
this._pageLabels = null;
|
|
2414
|
+
this._buffer = new PDFPageViewBuffer(DEFAULT_CACHE_SIZE);
|
|
2415
|
+
this._location = null;
|
|
2416
|
+
this._pagesRotation = 0;
|
|
2417
|
+
this._optionalContentConfigPromise = null;
|
|
2418
|
+
this._pagesRequests = new WeakMap();
|
|
2419
|
+
this._firstPageCapability = createPromiseCapability();
|
|
2420
|
+
this._onePageRenderedCapability = createPromiseCapability();
|
|
2421
|
+
this._pagesCapability = createPromiseCapability();
|
|
2422
|
+
this._scrollMode = ScrollMode.VERTICAL;
|
|
2423
|
+
this._spreadMode = SpreadMode.NONE;
|
|
2424
|
+
|
|
2425
|
+
if (this._onBeforeDraw) {
|
|
2426
|
+
this.eventBus._off("pagerender", this._onBeforeDraw);
|
|
2427
|
+
|
|
2428
|
+
this._onBeforeDraw = null;
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
if (this._onAfterDraw) {
|
|
2432
|
+
this.eventBus._off("pagerendered", this._onAfterDraw);
|
|
2433
|
+
|
|
2434
|
+
this._onAfterDraw = null;
|
|
2435
|
+
} // Remove the pages from the DOM...
|
|
2436
|
+
|
|
2437
|
+
|
|
2438
|
+
this.viewer.textContent = ""; // ... and reset the Scroll mode CSS class(es) afterwards.
|
|
2439
|
+
|
|
2440
|
+
this._updateScrollMode();
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
_scrollUpdate() {
|
|
2444
|
+
if (this.pagesCount === 0) {
|
|
2445
|
+
return;
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2448
|
+
this.update();
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
_scrollIntoView({
|
|
2452
|
+
pageDiv,
|
|
2453
|
+
pageSpot = null,
|
|
2454
|
+
pageNumber = null
|
|
2455
|
+
}) {
|
|
2456
|
+
scrollIntoView(pageDiv, pageSpot);
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
_setScaleUpdatePages(newScale, newValue, noScroll = false, preset = false) {
|
|
2460
|
+
this._currentScaleValue = newValue.toString();
|
|
2461
|
+
|
|
2462
|
+
if (isSameScale(this._currentScale, newScale)) {
|
|
2463
|
+
if (preset) {
|
|
2464
|
+
this.eventBus.dispatch("scalechanging", {
|
|
2465
|
+
source: this,
|
|
2466
|
+
scale: newScale,
|
|
2467
|
+
presetValue: newValue
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
return;
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
for (let i = 0, ii = this._pages.length; i < ii; i++) {
|
|
2475
|
+
this._pages[i].update(newScale);
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
this._currentScale = newScale;
|
|
2479
|
+
|
|
2480
|
+
if (!noScroll) {
|
|
2481
|
+
let page = this._currentPageNumber,
|
|
2482
|
+
dest;
|
|
2483
|
+
|
|
2484
|
+
if (this._location && !(this.isInPresentationMode || this.isChangingPresentationMode)) {
|
|
2485
|
+
page = this._location.pageNumber;
|
|
2486
|
+
dest = [null, {
|
|
2487
|
+
name: "XYZ"
|
|
2488
|
+
}, this._location.left, this._location.top, null];
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
this.scrollPageIntoView({
|
|
2492
|
+
pageNumber: page,
|
|
2493
|
+
destArray: dest,
|
|
2494
|
+
allowNegativeOffset: true
|
|
2495
|
+
});
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
this.eventBus.dispatch("scalechanging", {
|
|
2499
|
+
source: this,
|
|
2500
|
+
scale: newScale,
|
|
2501
|
+
presetValue: preset ? newValue : undefined
|
|
2502
|
+
});
|
|
2503
|
+
|
|
2504
|
+
if (this.defaultRenderingQueue) {
|
|
2505
|
+
this.update();
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
/**
|
|
2509
|
+
* @private
|
|
2510
|
+
*/
|
|
2511
|
+
|
|
2512
|
+
|
|
2513
|
+
get _pageWidthScaleFactor() {
|
|
2514
|
+
if (this._spreadMode !== SpreadMode.NONE && this._scrollMode !== ScrollMode.HORIZONTAL && !this.isInPresentationMode) {
|
|
2515
|
+
return 2;
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
return 1;
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
_setScale(value, noScroll = false) {
|
|
2522
|
+
let scale = parseFloat(value);
|
|
2523
|
+
|
|
2524
|
+
if (scale > 0) {
|
|
2525
|
+
this._setScaleUpdatePages(scale, value, noScroll,
|
|
2526
|
+
/* preset = */
|
|
2527
|
+
false);
|
|
2528
|
+
} else {
|
|
2529
|
+
const currentPage = this._pages[this._currentPageNumber - 1];
|
|
2530
|
+
|
|
2531
|
+
if (!currentPage) {
|
|
2532
|
+
return;
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
const noPadding = this.isInPresentationMode || this.removePageBorders;
|
|
2536
|
+
let hPadding = noPadding ? 0 : SCROLLBAR_PADDING;
|
|
2537
|
+
let vPadding = noPadding ? 0 : VERTICAL_PADDING;
|
|
2538
|
+
|
|
2539
|
+
if (!noPadding && this._isScrollModeHorizontal) {
|
|
2540
|
+
[hPadding, vPadding] = [vPadding, hPadding]; // Swap the padding values.
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
const pageWidthScale = (this.container.clientWidth - hPadding) / currentPage.width * currentPage.scale / this._pageWidthScaleFactor;
|
|
2544
|
+
const pageHeightScale = (this.container.clientHeight - vPadding) / currentPage.height * currentPage.scale;
|
|
2545
|
+
|
|
2546
|
+
switch (value) {
|
|
2547
|
+
case "page-actual":
|
|
2548
|
+
scale = 1;
|
|
2549
|
+
break;
|
|
2550
|
+
|
|
2551
|
+
case "page-width":
|
|
2552
|
+
scale = pageWidthScale;
|
|
2553
|
+
break;
|
|
2554
|
+
|
|
2555
|
+
case "page-height":
|
|
2556
|
+
scale = pageHeightScale;
|
|
2557
|
+
break;
|
|
2558
|
+
|
|
2559
|
+
case "page-fit":
|
|
2560
|
+
scale = Math.min(pageWidthScale, pageHeightScale);
|
|
2561
|
+
break;
|
|
2562
|
+
|
|
2563
|
+
case "auto":
|
|
2564
|
+
// For pages in landscape mode, fit the page height to the viewer
|
|
2565
|
+
// *unless* the page would thus become too wide to fit horizontally.
|
|
2566
|
+
const horizontalScale = isPortraitOrientation(currentPage) ? pageWidthScale : Math.min(pageHeightScale, pageWidthScale);
|
|
2567
|
+
scale = Math.min(MAX_AUTO_SCALE, horizontalScale);
|
|
2568
|
+
break;
|
|
2569
|
+
|
|
2570
|
+
default:
|
|
2571
|
+
console.error(`${this._name}._setScale: "${value}" is an unknown zoom value.`);
|
|
2572
|
+
return;
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
this._setScaleUpdatePages(scale, value, noScroll,
|
|
2576
|
+
/* preset = */
|
|
2577
|
+
true);
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
/**
|
|
2581
|
+
* Refreshes page view: scrolls to the current page and updates the scale.
|
|
2582
|
+
* @private
|
|
2583
|
+
*/
|
|
2584
|
+
|
|
2585
|
+
|
|
2586
|
+
_resetCurrentPageView() {
|
|
2587
|
+
if (this.isInPresentationMode) {
|
|
2588
|
+
// Fixes the case when PDF has different page sizes.
|
|
2589
|
+
this._setScale(this._currentScaleValue, true);
|
|
2590
|
+
}
|
|
2591
|
+
|
|
2592
|
+
const pageView = this._pages[this._currentPageNumber - 1];
|
|
2593
|
+
|
|
2594
|
+
this._scrollIntoView({
|
|
2595
|
+
pageDiv: pageView.div
|
|
2596
|
+
});
|
|
2597
|
+
}
|
|
2598
|
+
/**
|
|
2599
|
+
* @param {string} label - The page label.
|
|
2600
|
+
* @returns {number|null} The page number corresponding to the page label,
|
|
2601
|
+
* or `null` when no page labels exist and/or the input is invalid.
|
|
2602
|
+
*/
|
|
2603
|
+
|
|
2604
|
+
|
|
2605
|
+
pageLabelToPageNumber(label) {
|
|
2606
|
+
if (!this._pageLabels) {
|
|
2607
|
+
return null;
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
const i = this._pageLabels.indexOf(label);
|
|
2611
|
+
|
|
2612
|
+
if (i < 0) {
|
|
2613
|
+
return null;
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
return i + 1;
|
|
2617
|
+
}
|
|
2618
|
+
/**
|
|
2619
|
+
* @typedef ScrollPageIntoViewParameters
|
|
2620
|
+
* @property {number} pageNumber - The page number.
|
|
2621
|
+
* @property {Array} [destArray] - The original PDF destination array, in the
|
|
2622
|
+
* format: <page-ref> </XYZ|/FitXXX> <args..>
|
|
2623
|
+
* @property {boolean} [allowNegativeOffset] - Allow negative page offsets.
|
|
2624
|
+
* The default value is `false`.
|
|
2625
|
+
* @property {boolean} [ignoreDestinationZoom] - Ignore the zoom argument in
|
|
2626
|
+
* the destination array. The default value is `false`.
|
|
2627
|
+
*/
|
|
2628
|
+
|
|
2629
|
+
/**
|
|
2630
|
+
* Scrolls page into view.
|
|
2631
|
+
* @param {ScrollPageIntoViewParameters} params
|
|
2632
|
+
*/
|
|
2633
|
+
|
|
2634
|
+
|
|
2635
|
+
scrollPageIntoView({
|
|
2636
|
+
pageNumber,
|
|
2637
|
+
destArray = null,
|
|
2638
|
+
allowNegativeOffset = false,
|
|
2639
|
+
ignoreDestinationZoom = false
|
|
2640
|
+
}) {
|
|
2641
|
+
if (!this.pdfDocument) {
|
|
2642
|
+
return;
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
const pageView = Number.isInteger(pageNumber) && this._pages[pageNumber - 1];
|
|
2646
|
+
|
|
2647
|
+
if (!pageView) {
|
|
2648
|
+
console.error(`${this._name}.scrollPageIntoView: ` + `"${pageNumber}" is not a valid pageNumber parameter.`);
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
if (this.isInPresentationMode || !destArray) {
|
|
2653
|
+
this._setCurrentPageNumber(pageNumber,
|
|
2654
|
+
/* resetCurrentPageView = */
|
|
2655
|
+
true);
|
|
2656
|
+
|
|
2657
|
+
return;
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
let x = 0,
|
|
2661
|
+
y = 0;
|
|
2662
|
+
let width = 0,
|
|
2663
|
+
height = 0,
|
|
2664
|
+
widthScale,
|
|
2665
|
+
heightScale;
|
|
2666
|
+
const changeOrientation = pageView.rotation % 180 !== 0;
|
|
2667
|
+
const pageWidth = (changeOrientation ? pageView.height : pageView.width) / pageView.scale / CSS_UNITS;
|
|
2668
|
+
const pageHeight = (changeOrientation ? pageView.width : pageView.height) / pageView.scale / CSS_UNITS;
|
|
2669
|
+
let scale = 0;
|
|
2670
|
+
|
|
2671
|
+
switch (destArray[1].name) {
|
|
2672
|
+
case "XYZ":
|
|
2673
|
+
x = destArray[2];
|
|
2674
|
+
y = destArray[3];
|
|
2675
|
+
scale = destArray[4]; // If x and/or y coordinates are not supplied, default to
|
|
2676
|
+
// _top_ left of the page (not the obvious bottom left,
|
|
2677
|
+
// since aligning the bottom of the intended page with the
|
|
2678
|
+
// top of the window is rarely helpful).
|
|
2679
|
+
|
|
2680
|
+
x = x !== null ? x : 0;
|
|
2681
|
+
y = y !== null ? y : pageHeight;
|
|
2682
|
+
break;
|
|
2683
|
+
|
|
2684
|
+
case "Fit":
|
|
2685
|
+
case "FitB":
|
|
2686
|
+
scale = "page-fit";
|
|
2687
|
+
break;
|
|
2688
|
+
|
|
2689
|
+
case "FitH":
|
|
2690
|
+
case "FitBH":
|
|
2691
|
+
y = destArray[2];
|
|
2692
|
+
scale = "page-width"; // According to the PDF spec, section 12.3.2.2, a `null` value in the
|
|
2693
|
+
// parameter should maintain the position relative to the new page.
|
|
2694
|
+
|
|
2695
|
+
if (y === null && this._location) {
|
|
2696
|
+
x = this._location.left;
|
|
2697
|
+
y = this._location.top;
|
|
2698
|
+
} else if (typeof y !== "number") {
|
|
2699
|
+
// The "top" value isn't optional, according to the spec, however some
|
|
2700
|
+
// bad PDF generators will pretend that it is (fixes bug 1663390).
|
|
2701
|
+
y = pageHeight;
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2704
|
+
break;
|
|
2705
|
+
|
|
2706
|
+
case "FitV":
|
|
2707
|
+
case "FitBV":
|
|
2708
|
+
x = destArray[2];
|
|
2709
|
+
width = pageWidth;
|
|
2710
|
+
height = pageHeight;
|
|
2711
|
+
scale = "page-height";
|
|
2712
|
+
break;
|
|
2713
|
+
|
|
2714
|
+
case "FitR":
|
|
2715
|
+
x = destArray[2];
|
|
2716
|
+
y = destArray[3];
|
|
2717
|
+
width = destArray[4] - x;
|
|
2718
|
+
height = destArray[5] - y;
|
|
2719
|
+
const hPadding = this.removePageBorders ? 0 : SCROLLBAR_PADDING;
|
|
2720
|
+
const vPadding = this.removePageBorders ? 0 : VERTICAL_PADDING;
|
|
2721
|
+
widthScale = (this.container.clientWidth - hPadding) / width / CSS_UNITS;
|
|
2722
|
+
heightScale = (this.container.clientHeight - vPadding) / height / CSS_UNITS;
|
|
2723
|
+
scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
|
|
2724
|
+
break;
|
|
2725
|
+
|
|
2726
|
+
default:
|
|
2727
|
+
console.error(`${this._name}.scrollPageIntoView: ` + `"${destArray[1].name}" is not a valid destination type.`);
|
|
2728
|
+
return;
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
if (!ignoreDestinationZoom) {
|
|
2732
|
+
if (scale && scale !== this._currentScale) {
|
|
2733
|
+
this.currentScaleValue = scale;
|
|
2734
|
+
} else if (this._currentScale === UNKNOWN_SCALE) {
|
|
2735
|
+
this.currentScaleValue = DEFAULT_SCALE_VALUE;
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
if (scale === "page-fit" && !destArray[4]) {
|
|
2740
|
+
this._scrollIntoView({
|
|
2741
|
+
pageDiv: pageView.div,
|
|
2742
|
+
pageNumber
|
|
2743
|
+
});
|
|
2744
|
+
|
|
2745
|
+
return;
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
const boundingRect = [pageView.viewport.convertToViewportPoint(x, y), pageView.viewport.convertToViewportPoint(x + width, y + height)];
|
|
2749
|
+
let left = Math.min(boundingRect[0][0], boundingRect[1][0]);
|
|
2750
|
+
let top = Math.min(boundingRect[0][1], boundingRect[1][1]);
|
|
2751
|
+
|
|
2752
|
+
if (!allowNegativeOffset) {
|
|
2753
|
+
// Some bad PDF generators will create destinations with e.g. top values
|
|
2754
|
+
// that exceeds the page height. Ensure that offsets are not negative,
|
|
2755
|
+
// to prevent a previous page from becoming visible (fixes bug 874482).
|
|
2756
|
+
left = Math.max(left, 0);
|
|
2757
|
+
top = Math.max(top, 0);
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
this._scrollIntoView({
|
|
2761
|
+
pageDiv: pageView.div,
|
|
2762
|
+
pageSpot: {
|
|
2763
|
+
left,
|
|
2764
|
+
top
|
|
2765
|
+
},
|
|
2766
|
+
pageNumber
|
|
2767
|
+
});
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
_updateLocation(firstPage) {
|
|
2771
|
+
const currentScale = this._currentScale;
|
|
2772
|
+
const currentScaleValue = this._currentScaleValue;
|
|
2773
|
+
const normalizedScaleValue = parseFloat(currentScaleValue) === currentScale ? Math.round(currentScale * 10000) / 100 : currentScaleValue;
|
|
2774
|
+
const pageNumber = firstPage.id;
|
|
2775
|
+
let pdfOpenParams = "#page=" + pageNumber;
|
|
2776
|
+
pdfOpenParams += "&zoom=" + normalizedScaleValue;
|
|
2777
|
+
const currentPageView = this._pages[pageNumber - 1];
|
|
2778
|
+
const container = this.container;
|
|
2779
|
+
const topLeft = currentPageView.getPagePoint(container.scrollLeft - firstPage.x, container.scrollTop - firstPage.y);
|
|
2780
|
+
const intLeft = Math.round(topLeft[0]);
|
|
2781
|
+
const intTop = Math.round(topLeft[1]);
|
|
2782
|
+
pdfOpenParams += "," + intLeft + "," + intTop;
|
|
2783
|
+
this._location = {
|
|
2784
|
+
pageNumber,
|
|
2785
|
+
scale: normalizedScaleValue,
|
|
2786
|
+
top: intTop,
|
|
2787
|
+
left: intLeft,
|
|
2788
|
+
rotation: this._pagesRotation,
|
|
2789
|
+
pdfOpenParams
|
|
2790
|
+
};
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2793
|
+
_updateHelper(visiblePages) {
|
|
2794
|
+
throw new Error("Not implemented: _updateHelper");
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
update() {
|
|
2798
|
+
const visible = this._getVisiblePages();
|
|
2799
|
+
|
|
2800
|
+
const visiblePages = visible.views,
|
|
2801
|
+
numVisiblePages = visiblePages.length;
|
|
2802
|
+
|
|
2803
|
+
if (numVisiblePages === 0) {
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
const newCacheSize = Math.max(DEFAULT_CACHE_SIZE, 2 * numVisiblePages + 1);
|
|
2808
|
+
|
|
2809
|
+
this._buffer.resize(newCacheSize, visiblePages);
|
|
2810
|
+
|
|
2811
|
+
this.renderingQueue.renderHighestPriority(visible);
|
|
2812
|
+
|
|
2813
|
+
this._updateHelper(visiblePages); // Run any class-specific update code.
|
|
2814
|
+
|
|
2815
|
+
|
|
2816
|
+
this._updateLocation(visible.first);
|
|
2817
|
+
|
|
2818
|
+
this.eventBus.dispatch("updateviewarea", {
|
|
2819
|
+
source: this,
|
|
2820
|
+
location: this._location
|
|
2821
|
+
});
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
containsElement(element) {
|
|
2825
|
+
return this.container.contains(element);
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
focus() {
|
|
2829
|
+
this.container.focus();
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
get _isScrollModeHorizontal() {
|
|
2833
|
+
// Used to ensure that pre-rendering of the next/previous page works
|
|
2834
|
+
// correctly, since Scroll/Spread modes are ignored in Presentation Mode.
|
|
2835
|
+
return this.isInPresentationMode ? false : this._scrollMode === ScrollMode.HORIZONTAL;
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2838
|
+
get _isContainerRtl() {
|
|
2839
|
+
return getComputedStyle(this.container).direction === "rtl";
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
get isInPresentationMode() {
|
|
2843
|
+
return this.presentationModeState === PresentationModeState.FULLSCREEN;
|
|
2844
|
+
}
|
|
2845
|
+
|
|
2846
|
+
get isChangingPresentationMode() {
|
|
2847
|
+
return this.presentationModeState === PresentationModeState.CHANGING;
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
get isHorizontalScrollbarEnabled() {
|
|
2851
|
+
return this.isInPresentationMode ? false : this.container.scrollWidth > this.container.clientWidth;
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
get isVerticalScrollbarEnabled() {
|
|
2855
|
+
return this.isInPresentationMode ? false : this.container.scrollHeight > this.container.clientHeight;
|
|
2856
|
+
}
|
|
2857
|
+
/**
|
|
2858
|
+
* Helper method for `this._getVisiblePages`. Should only ever be used when
|
|
2859
|
+
* the viewer can only display a single page at a time, for example in:
|
|
2860
|
+
* - `PDFSinglePageViewer`.
|
|
2861
|
+
* - `PDFViewer` with Presentation Mode active.
|
|
2862
|
+
*/
|
|
2863
|
+
|
|
2864
|
+
|
|
2865
|
+
_getCurrentVisiblePage() {
|
|
2866
|
+
if (!this.pagesCount) {
|
|
2867
|
+
return {
|
|
2868
|
+
views: []
|
|
2869
|
+
};
|
|
2870
|
+
}
|
|
2871
|
+
|
|
2872
|
+
const pageView = this._pages[this._currentPageNumber - 1]; // NOTE: Compute the `x` and `y` properties of the current view,
|
|
2873
|
+
// since `this._updateLocation` depends of them being available.
|
|
2874
|
+
|
|
2875
|
+
const element = pageView.div;
|
|
2876
|
+
const view = {
|
|
2877
|
+
id: pageView.id,
|
|
2878
|
+
x: element.offsetLeft + element.clientLeft,
|
|
2879
|
+
y: element.offsetTop + element.clientTop,
|
|
2880
|
+
view: pageView
|
|
2881
|
+
};
|
|
2882
|
+
return {
|
|
2883
|
+
first: view,
|
|
2884
|
+
last: view,
|
|
2885
|
+
views: [view]
|
|
2886
|
+
};
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
_getVisiblePages() {
|
|
2890
|
+
return getVisibleElements({
|
|
2891
|
+
scrollEl: this.container,
|
|
2892
|
+
views: this._pages,
|
|
2893
|
+
sortByVisibility: true,
|
|
2894
|
+
horizontal: this._isScrollModeHorizontal,
|
|
2895
|
+
rtl: this._isScrollModeHorizontal && this._isContainerRtl
|
|
2896
|
+
});
|
|
2897
|
+
}
|
|
2898
|
+
/**
|
|
2899
|
+
* @param {number} pageNumber
|
|
2900
|
+
*/
|
|
2901
|
+
|
|
2902
|
+
|
|
2903
|
+
isPageVisible(pageNumber) {
|
|
2904
|
+
if (!this.pdfDocument) {
|
|
2905
|
+
return false;
|
|
2906
|
+
}
|
|
2907
|
+
|
|
2908
|
+
if (!(Number.isInteger(pageNumber) && pageNumber > 0 && pageNumber <= this.pagesCount)) {
|
|
2909
|
+
console.error(`${this._name}.isPageVisible: "${pageNumber}" is not a valid page.`);
|
|
2910
|
+
return false;
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
return this._getVisiblePages().views.some(function (view) {
|
|
2914
|
+
return view.id === pageNumber;
|
|
2915
|
+
});
|
|
2916
|
+
}
|
|
2917
|
+
/**
|
|
2918
|
+
* @param {number} pageNumber
|
|
2919
|
+
*/
|
|
2920
|
+
|
|
2921
|
+
|
|
2922
|
+
isPageCached(pageNumber) {
|
|
2923
|
+
if (!this.pdfDocument || !this._buffer) {
|
|
2924
|
+
return false;
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
if (!(Number.isInteger(pageNumber) && pageNumber > 0 && pageNumber <= this.pagesCount)) {
|
|
2928
|
+
console.error(`${this._name}.isPageCached: "${pageNumber}" is not a valid page.`);
|
|
2929
|
+
return false;
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
const pageView = this._pages[pageNumber - 1];
|
|
2933
|
+
|
|
2934
|
+
if (!pageView) {
|
|
2935
|
+
return false;
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
return this._buffer.has(pageView);
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2941
|
+
cleanup() {
|
|
2942
|
+
for (let i = 0, ii = this._pages.length; i < ii; i++) {
|
|
2943
|
+
if (this._pages[i] && this._pages[i].renderingState !== RenderingStates.FINISHED) {
|
|
2944
|
+
this._pages[i].reset();
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
/**
|
|
2949
|
+
* @private
|
|
2950
|
+
*/
|
|
2951
|
+
|
|
2952
|
+
|
|
2953
|
+
_cancelRendering() {
|
|
2954
|
+
for (let i = 0, ii = this._pages.length; i < ii; i++) {
|
|
2955
|
+
if (this._pages[i]) {
|
|
2956
|
+
this._pages[i].cancelRendering();
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
/**
|
|
2961
|
+
* @param {PDFPageView} pageView
|
|
2962
|
+
* @returns {Promise} Returns a promise containing a {PDFPageProxy} object.
|
|
2963
|
+
* @private
|
|
2964
|
+
*/
|
|
2965
|
+
|
|
2966
|
+
|
|
2967
|
+
_ensurePdfPageLoaded(pageView) {
|
|
2968
|
+
if (pageView.pdfPage) {
|
|
2969
|
+
return Promise.resolve(pageView.pdfPage);
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
if (this._pagesRequests.has(pageView)) {
|
|
2973
|
+
return this._pagesRequests.get(pageView);
|
|
2974
|
+
}
|
|
2975
|
+
|
|
2976
|
+
const promise = this.pdfDocument.getPage(pageView.id).then(pdfPage => {
|
|
2977
|
+
if (!pageView.pdfPage) {
|
|
2978
|
+
pageView.setPdfPage(pdfPage);
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2981
|
+
this._pagesRequests.delete(pageView);
|
|
2982
|
+
|
|
2983
|
+
return pdfPage;
|
|
2984
|
+
}).catch(reason => {
|
|
2985
|
+
console.error("Unable to get page for page view", reason); // Page error -- there is nothing that can be done.
|
|
2986
|
+
|
|
2987
|
+
this._pagesRequests.delete(pageView);
|
|
2988
|
+
});
|
|
2989
|
+
|
|
2990
|
+
this._pagesRequests.set(pageView, promise);
|
|
2991
|
+
|
|
2992
|
+
return promise;
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
forceRendering(currentlyVisiblePages) {
|
|
2996
|
+
const visiblePages = currentlyVisiblePages || this._getVisiblePages();
|
|
2997
|
+
|
|
2998
|
+
const scrollAhead = this._isScrollModeHorizontal ? this.scroll.right : this.scroll.down;
|
|
2999
|
+
const pageView = this.renderingQueue.getHighestPriority(visiblePages, this._pages, scrollAhead);
|
|
3000
|
+
|
|
3001
|
+
if (pageView) {
|
|
3002
|
+
this._ensurePdfPageLoaded(pageView).then(() => {
|
|
3003
|
+
this.renderingQueue.renderView(pageView);
|
|
3004
|
+
});
|
|
3005
|
+
|
|
3006
|
+
return true;
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
return false;
|
|
3010
|
+
}
|
|
3011
|
+
/**
|
|
3012
|
+
* @param {HTMLDivElement} textLayerDiv
|
|
3013
|
+
* @param {number} pageIndex
|
|
3014
|
+
* @param {PageViewport} viewport
|
|
3015
|
+
* @param {boolean} enhanceTextSelection
|
|
3016
|
+
* @param {EventBus} eventBus
|
|
3017
|
+
* @returns {TextLayerBuilder}
|
|
3018
|
+
*/
|
|
3019
|
+
|
|
3020
|
+
|
|
3021
|
+
createTextLayerBuilder(textLayerDiv, pageIndex, viewport, enhanceTextSelection = false, eventBus) {
|
|
3022
|
+
return new TextLayerBuilder({
|
|
3023
|
+
textLayerDiv,
|
|
3024
|
+
eventBus,
|
|
3025
|
+
pageIndex,
|
|
3026
|
+
viewport,
|
|
3027
|
+
findController: this.isInPresentationMode ? null : this.findController,
|
|
3028
|
+
enhanceTextSelection: this.isInPresentationMode ? false : enhanceTextSelection
|
|
3029
|
+
});
|
|
3030
|
+
}
|
|
3031
|
+
/**
|
|
3032
|
+
* @param {HTMLDivElement} pageDiv
|
|
3033
|
+
* @param {PDFPage} pdfPage
|
|
3034
|
+
* @param {AnnotationStorage} [annotationStorage] - Storage for annotation
|
|
3035
|
+
* data in forms.
|
|
3036
|
+
* @param {string} [imageResourcesPath] - Path for image resources, mainly
|
|
3037
|
+
* for annotation icons. Include trailing slash.
|
|
3038
|
+
* @param {boolean} renderInteractiveForms
|
|
3039
|
+
* @param {IL10n} l10n
|
|
3040
|
+
* @param {boolean} [enableScripting]
|
|
3041
|
+
* @param {Promise<boolean>} [hasJSActionsPromise]
|
|
3042
|
+
* @param {Object} [mouseState]
|
|
3043
|
+
* @returns {AnnotationLayerBuilder}
|
|
3044
|
+
*/
|
|
3045
|
+
|
|
3046
|
+
|
|
3047
|
+
createAnnotationLayerBuilder(pageDiv, pdfPage, annotationStorage = null, imageResourcesPath = "", renderInteractiveForms = false, l10n = NullL10n, enableScripting = null, hasJSActionsPromise = null, mouseState = null) {
|
|
3048
|
+
var _this$pdfDocument, _this$pdfDocument2, _this$_scriptingManag;
|
|
3049
|
+
|
|
3050
|
+
return new AnnotationLayerBuilder({
|
|
3051
|
+
pageDiv,
|
|
3052
|
+
pdfPage,
|
|
3053
|
+
annotationStorage: annotationStorage || ((_this$pdfDocument = this.pdfDocument) === null || _this$pdfDocument === void 0 ? void 0 : _this$pdfDocument.annotationStorage),
|
|
3054
|
+
imageResourcesPath,
|
|
3055
|
+
renderInteractiveForms,
|
|
3056
|
+
linkService: this.linkService,
|
|
3057
|
+
downloadManager: this.downloadManager,
|
|
3058
|
+
l10n,
|
|
3059
|
+
enableScripting: enableScripting !== null && enableScripting !== void 0 ? enableScripting : this.enableScripting,
|
|
3060
|
+
hasJSActionsPromise: hasJSActionsPromise || ((_this$pdfDocument2 = this.pdfDocument) === null || _this$pdfDocument2 === void 0 ? void 0 : _this$pdfDocument2.hasJSActions()),
|
|
3061
|
+
mouseState: mouseState || ((_this$_scriptingManag = this._scriptingManager) === null || _this$_scriptingManag === void 0 ? void 0 : _this$_scriptingManag.mouseState)
|
|
3062
|
+
});
|
|
3063
|
+
}
|
|
3064
|
+
/**
|
|
3065
|
+
* @param {HTMLDivElement} pageDiv
|
|
3066
|
+
* @param {PDFPage} pdfPage
|
|
3067
|
+
* @param {AnnotationStorage} [annotationStorage] - Storage for annotation
|
|
3068
|
+
* data in forms.
|
|
3069
|
+
* @returns {XfaLayerBuilder}
|
|
3070
|
+
*/
|
|
3071
|
+
|
|
3072
|
+
|
|
3073
|
+
createXfaLayerBuilder(pageDiv, pdfPage, annotationStorage = null) {
|
|
3074
|
+
var _this$pdfDocument3;
|
|
3075
|
+
|
|
3076
|
+
return new XfaLayerBuilder({
|
|
3077
|
+
pageDiv,
|
|
3078
|
+
pdfPage,
|
|
3079
|
+
annotationStorage: annotationStorage || ((_this$pdfDocument3 = this.pdfDocument) === null || _this$pdfDocument3 === void 0 ? void 0 : _this$pdfDocument3.annotationStorage)
|
|
3080
|
+
});
|
|
3081
|
+
}
|
|
3082
|
+
/**
|
|
3083
|
+
* @param {PDFPage} pdfPage
|
|
3084
|
+
* @returns {StructTreeLayerBuilder}
|
|
3085
|
+
*/
|
|
3086
|
+
|
|
3087
|
+
|
|
3088
|
+
createStructTreeLayerBuilder(pdfPage) {
|
|
3089
|
+
return new StructTreeLayerBuilder({
|
|
3090
|
+
pdfPage
|
|
3091
|
+
});
|
|
3092
|
+
}
|
|
3093
|
+
/**
|
|
3094
|
+
* @type {boolean} Whether all pages of the PDF document have identical
|
|
3095
|
+
* widths and heights.
|
|
3096
|
+
*/
|
|
3097
|
+
|
|
3098
|
+
|
|
3099
|
+
get hasEqualPageSizes() {
|
|
3100
|
+
const firstPageView = this._pages[0];
|
|
3101
|
+
|
|
3102
|
+
for (let i = 1, ii = this._pages.length; i < ii; ++i) {
|
|
3103
|
+
const pageView = this._pages[i];
|
|
3104
|
+
|
|
3105
|
+
if (pageView.width !== firstPageView.width || pageView.height !== firstPageView.height) {
|
|
3106
|
+
return false;
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
|
|
3110
|
+
return true;
|
|
3111
|
+
}
|
|
3112
|
+
/**
|
|
3113
|
+
* Returns sizes of the pages.
|
|
3114
|
+
* @returns {Array} Array of objects with width/height/rotation fields.
|
|
3115
|
+
*/
|
|
3116
|
+
|
|
3117
|
+
|
|
3118
|
+
getPagesOverview() {
|
|
3119
|
+
return this._pages.map(pageView => {
|
|
3120
|
+
const viewport = pageView.pdfPage.getViewport({
|
|
3121
|
+
scale: 1
|
|
3122
|
+
});
|
|
3123
|
+
|
|
3124
|
+
if (!this.enablePrintAutoRotate || isPortraitOrientation(viewport)) {
|
|
3125
|
+
return {
|
|
3126
|
+
width: viewport.width,
|
|
3127
|
+
height: viewport.height,
|
|
3128
|
+
rotation: viewport.rotation
|
|
3129
|
+
};
|
|
3130
|
+
} // Landscape orientation.
|
|
3131
|
+
|
|
3132
|
+
|
|
3133
|
+
return {
|
|
3134
|
+
width: viewport.height,
|
|
3135
|
+
height: viewport.width,
|
|
3136
|
+
rotation: (viewport.rotation - 90) % 360
|
|
3137
|
+
};
|
|
3138
|
+
});
|
|
3139
|
+
}
|
|
3140
|
+
/**
|
|
3141
|
+
* @type {Promise<OptionalContentConfig | null>}
|
|
3142
|
+
*/
|
|
3143
|
+
|
|
3144
|
+
|
|
3145
|
+
get optionalContentConfigPromise() {
|
|
3146
|
+
if (!this.pdfDocument) {
|
|
3147
|
+
return Promise.resolve(null);
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
if (!this._optionalContentConfigPromise) {
|
|
3151
|
+
// Prevent issues if the getter is accessed *before* the `onePageRendered`
|
|
3152
|
+
// promise has resolved; won't (normally) happen in the default viewer.
|
|
3153
|
+
return this.pdfDocument.getOptionalContentConfig();
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
return this._optionalContentConfigPromise;
|
|
3157
|
+
}
|
|
3158
|
+
/**
|
|
3159
|
+
* @param {Promise<OptionalContentConfig>} promise - A promise that is
|
|
3160
|
+
* resolved with an {@link OptionalContentConfig} instance.
|
|
3161
|
+
*/
|
|
3162
|
+
|
|
3163
|
+
|
|
3164
|
+
set optionalContentConfigPromise(promise) {
|
|
3165
|
+
if (!(promise instanceof Promise)) {
|
|
3166
|
+
throw new Error(`Invalid optionalContentConfigPromise: ${promise}`);
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
if (!this.pdfDocument) {
|
|
3170
|
+
return;
|
|
3171
|
+
}
|
|
3172
|
+
|
|
3173
|
+
if (!this._optionalContentConfigPromise) {
|
|
3174
|
+
// Ignore the setter *before* the `onePageRendered` promise has resolved,
|
|
3175
|
+
// since it'll be overwritten anyway; won't happen in the default viewer.
|
|
3176
|
+
return;
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
this._optionalContentConfigPromise = promise;
|
|
3180
|
+
|
|
3181
|
+
for (const pageView of this._pages) {
|
|
3182
|
+
pageView.update(pageView.scale, pageView.rotation, promise);
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
this.update();
|
|
3186
|
+
this.eventBus.dispatch("optionalcontentconfigchanged", {
|
|
3187
|
+
source: this,
|
|
3188
|
+
promise
|
|
3189
|
+
});
|
|
3190
|
+
}
|
|
3191
|
+
/**
|
|
3192
|
+
* @type {number} One of the values in {ScrollMode}.
|
|
3193
|
+
*/
|
|
3194
|
+
|
|
3195
|
+
|
|
3196
|
+
get scrollMode() {
|
|
3197
|
+
return this._scrollMode;
|
|
3198
|
+
}
|
|
3199
|
+
/**
|
|
3200
|
+
* @param {number} mode - The direction in which the document pages should be
|
|
3201
|
+
* laid out within the scrolling container.
|
|
3202
|
+
* The constants from {ScrollMode} should be used.
|
|
3203
|
+
*/
|
|
3204
|
+
|
|
3205
|
+
|
|
3206
|
+
set scrollMode(mode) {
|
|
3207
|
+
if (this._scrollMode === mode) {
|
|
3208
|
+
return; // The Scroll mode didn't change.
|
|
3209
|
+
}
|
|
3210
|
+
|
|
3211
|
+
if (!isValidScrollMode(mode)) {
|
|
3212
|
+
throw new Error(`Invalid scroll mode: ${mode}`);
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3215
|
+
this._scrollMode = mode;
|
|
3216
|
+
this.eventBus.dispatch("scrollmodechanged", {
|
|
3217
|
+
source: this,
|
|
3218
|
+
mode
|
|
3219
|
+
});
|
|
3220
|
+
|
|
3221
|
+
this._updateScrollMode(
|
|
3222
|
+
/* pageNumber = */
|
|
3223
|
+
this._currentPageNumber);
|
|
3224
|
+
}
|
|
3225
|
+
|
|
3226
|
+
_updateScrollMode(pageNumber = null) {
|
|
3227
|
+
const scrollMode = this._scrollMode,
|
|
3228
|
+
viewer = this.viewer;
|
|
3229
|
+
viewer.classList.toggle("scrollHorizontal", scrollMode === ScrollMode.HORIZONTAL);
|
|
3230
|
+
viewer.classList.toggle("scrollWrapped", scrollMode === ScrollMode.WRAPPED);
|
|
3231
|
+
|
|
3232
|
+
if (!this.pdfDocument || !pageNumber) {
|
|
3233
|
+
return;
|
|
3234
|
+
} // Non-numeric scale values can be sensitive to the scroll orientation.
|
|
3235
|
+
// Call this before re-scrolling to the current page, to ensure that any
|
|
3236
|
+
// changes in scale don't move the current page.
|
|
3237
|
+
|
|
3238
|
+
|
|
3239
|
+
if (this._currentScaleValue && isNaN(this._currentScaleValue)) {
|
|
3240
|
+
this._setScale(this._currentScaleValue, true);
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3243
|
+
this._setCurrentPageNumber(pageNumber,
|
|
3244
|
+
/* resetCurrentPageView = */
|
|
3245
|
+
true);
|
|
3246
|
+
|
|
3247
|
+
this.update();
|
|
3248
|
+
}
|
|
3249
|
+
/**
|
|
3250
|
+
* @type {number} One of the values in {SpreadMode}.
|
|
3251
|
+
*/
|
|
3252
|
+
|
|
3253
|
+
|
|
3254
|
+
get spreadMode() {
|
|
3255
|
+
return this._spreadMode;
|
|
3256
|
+
}
|
|
3257
|
+
/**
|
|
3258
|
+
* @param {number} mode - Group the pages in spreads, starting with odd- or
|
|
3259
|
+
* even-number pages (unless `SpreadMode.NONE` is used).
|
|
3260
|
+
* The constants from {SpreadMode} should be used.
|
|
3261
|
+
*/
|
|
3262
|
+
|
|
3263
|
+
|
|
3264
|
+
set spreadMode(mode) {
|
|
3265
|
+
if (this._spreadMode === mode) {
|
|
3266
|
+
return; // The Spread mode didn't change.
|
|
3267
|
+
}
|
|
3268
|
+
|
|
3269
|
+
if (!isValidSpreadMode(mode)) {
|
|
3270
|
+
throw new Error(`Invalid spread mode: ${mode}`);
|
|
3271
|
+
}
|
|
3272
|
+
|
|
3273
|
+
this._spreadMode = mode;
|
|
3274
|
+
this.eventBus.dispatch("spreadmodechanged", {
|
|
3275
|
+
source: this,
|
|
3276
|
+
mode
|
|
3277
|
+
});
|
|
3278
|
+
|
|
3279
|
+
this._updateSpreadMode(
|
|
3280
|
+
/* pageNumber = */
|
|
3281
|
+
this._currentPageNumber);
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3284
|
+
_updateSpreadMode(pageNumber = null) {
|
|
3285
|
+
if (!this.pdfDocument) {
|
|
3286
|
+
return;
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
const viewer = this.viewer,
|
|
3290
|
+
pages = this._pages; // Temporarily remove all the pages from the DOM.
|
|
3291
|
+
|
|
3292
|
+
viewer.textContent = "";
|
|
3293
|
+
|
|
3294
|
+
if (this._spreadMode === SpreadMode.NONE) {
|
|
3295
|
+
for (let i = 0, iMax = pages.length; i < iMax; ++i) {
|
|
3296
|
+
viewer.appendChild(pages[i].div);
|
|
3297
|
+
}
|
|
3298
|
+
} else {
|
|
3299
|
+
const parity = this._spreadMode - 1;
|
|
3300
|
+
let spread = null;
|
|
3301
|
+
|
|
3302
|
+
for (let i = 0, iMax = pages.length; i < iMax; ++i) {
|
|
3303
|
+
if (spread === null) {
|
|
3304
|
+
spread = document.createElement("div");
|
|
3305
|
+
spread.className = "spread";
|
|
3306
|
+
viewer.appendChild(spread);
|
|
3307
|
+
} else if (i % 2 === parity) {
|
|
3308
|
+
spread = spread.cloneNode(false);
|
|
3309
|
+
viewer.appendChild(spread);
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
spread.appendChild(pages[i].div);
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
|
|
3316
|
+
if (!pageNumber) {
|
|
3317
|
+
return;
|
|
3318
|
+
}
|
|
3319
|
+
|
|
3320
|
+
if (this._currentScaleValue && isNaN(this._currentScaleValue)) {
|
|
3321
|
+
this._setScale(this._currentScaleValue, true);
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3324
|
+
this._setCurrentPageNumber(pageNumber,
|
|
3325
|
+
/* resetCurrentPageView = */
|
|
3326
|
+
true);
|
|
3327
|
+
|
|
3328
|
+
this.update();
|
|
3329
|
+
}
|
|
3330
|
+
/**
|
|
3331
|
+
* @private
|
|
3332
|
+
*/
|
|
3333
|
+
|
|
3334
|
+
|
|
3335
|
+
_getPageAdvance(currentPageNumber, previous = false) {
|
|
3336
|
+
if (this.isInPresentationMode) {
|
|
3337
|
+
return 1;
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
switch (this._scrollMode) {
|
|
3341
|
+
case ScrollMode.WRAPPED:
|
|
3342
|
+
{
|
|
3343
|
+
const {
|
|
3344
|
+
views
|
|
3345
|
+
} = this._getVisiblePages(),
|
|
3346
|
+
pageLayout = new Map(); // Determine the current (visible) page layout.
|
|
3347
|
+
|
|
3348
|
+
|
|
3349
|
+
for (const {
|
|
3350
|
+
id,
|
|
3351
|
+
y,
|
|
3352
|
+
percent,
|
|
3353
|
+
widthPercent
|
|
3354
|
+
} of views) {
|
|
3355
|
+
if (percent === 0 || widthPercent < 100) {
|
|
3356
|
+
continue;
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3359
|
+
let yArray = pageLayout.get(y);
|
|
3360
|
+
|
|
3361
|
+
if (!yArray) {
|
|
3362
|
+
pageLayout.set(y, yArray || (yArray = []));
|
|
3363
|
+
}
|
|
3364
|
+
|
|
3365
|
+
yArray.push(id);
|
|
3366
|
+
} // Find the row of the current page.
|
|
3367
|
+
|
|
3368
|
+
|
|
3369
|
+
for (const yArray of pageLayout.values()) {
|
|
3370
|
+
const currentIndex = yArray.indexOf(currentPageNumber);
|
|
3371
|
+
|
|
3372
|
+
if (currentIndex === -1) {
|
|
3373
|
+
continue;
|
|
3374
|
+
}
|
|
3375
|
+
|
|
3376
|
+
const numPages = yArray.length;
|
|
3377
|
+
|
|
3378
|
+
if (numPages === 1) {
|
|
3379
|
+
break;
|
|
3380
|
+
} // Handle documents with varying page sizes.
|
|
3381
|
+
|
|
3382
|
+
|
|
3383
|
+
if (previous) {
|
|
3384
|
+
for (let i = currentIndex - 1, ii = 0; i >= ii; i--) {
|
|
3385
|
+
const currentId = yArray[i],
|
|
3386
|
+
expectedId = yArray[i + 1] - 1;
|
|
3387
|
+
|
|
3388
|
+
if (currentId < expectedId) {
|
|
3389
|
+
return currentPageNumber - expectedId;
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
} else {
|
|
3393
|
+
for (let i = currentIndex + 1, ii = numPages; i < ii; i++) {
|
|
3394
|
+
const currentId = yArray[i],
|
|
3395
|
+
expectedId = yArray[i - 1] + 1;
|
|
3396
|
+
|
|
3397
|
+
if (currentId > expectedId) {
|
|
3398
|
+
return expectedId - currentPageNumber;
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
} // The current row is "complete", advance to the previous/next one.
|
|
3402
|
+
|
|
3403
|
+
|
|
3404
|
+
if (previous) {
|
|
3405
|
+
const firstId = yArray[0];
|
|
3406
|
+
|
|
3407
|
+
if (firstId < currentPageNumber) {
|
|
3408
|
+
return currentPageNumber - firstId + 1;
|
|
3409
|
+
}
|
|
3410
|
+
} else {
|
|
3411
|
+
const lastId = yArray[numPages - 1];
|
|
3412
|
+
|
|
3413
|
+
if (lastId > currentPageNumber) {
|
|
3414
|
+
return lastId - currentPageNumber + 1;
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
|
|
3418
|
+
break;
|
|
3419
|
+
}
|
|
3420
|
+
|
|
3421
|
+
break;
|
|
3422
|
+
}
|
|
3423
|
+
|
|
3424
|
+
case ScrollMode.HORIZONTAL:
|
|
3425
|
+
{
|
|
3426
|
+
break;
|
|
3427
|
+
}
|
|
3428
|
+
|
|
3429
|
+
case ScrollMode.VERTICAL:
|
|
3430
|
+
{
|
|
3431
|
+
if (this._spreadMode === SpreadMode.NONE) {
|
|
3432
|
+
break; // Normal vertical scrolling.
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3435
|
+
const parity = this._spreadMode - 1;
|
|
3436
|
+
|
|
3437
|
+
if (previous && currentPageNumber % 2 !== parity) {
|
|
3438
|
+
break; // Left-hand side page.
|
|
3439
|
+
} else if (!previous && currentPageNumber % 2 === parity) {
|
|
3440
|
+
break; // Right-hand side page.
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3443
|
+
const {
|
|
3444
|
+
views
|
|
3445
|
+
} = this._getVisiblePages(),
|
|
3446
|
+
expectedId = previous ? currentPageNumber - 1 : currentPageNumber + 1;
|
|
3447
|
+
|
|
3448
|
+
for (const {
|
|
3449
|
+
id,
|
|
3450
|
+
percent,
|
|
3451
|
+
widthPercent
|
|
3452
|
+
} of views) {
|
|
3453
|
+
if (id !== expectedId) {
|
|
3454
|
+
continue;
|
|
3455
|
+
}
|
|
3456
|
+
|
|
3457
|
+
if (percent > 0 && widthPercent === 100) {
|
|
3458
|
+
return 2;
|
|
3459
|
+
}
|
|
3460
|
+
|
|
3461
|
+
break;
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3464
|
+
break;
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
|
|
3468
|
+
return 1;
|
|
3469
|
+
}
|
|
3470
|
+
/**
|
|
3471
|
+
* Go to the next page, taking scroll/spread-modes into account.
|
|
3472
|
+
* @returns {boolean} Whether navigation occured.
|
|
3473
|
+
*/
|
|
3474
|
+
|
|
3475
|
+
|
|
3476
|
+
nextPage() {
|
|
3477
|
+
const currentPageNumber = this._currentPageNumber,
|
|
3478
|
+
pagesCount = this.pagesCount;
|
|
3479
|
+
|
|
3480
|
+
if (currentPageNumber >= pagesCount) {
|
|
3481
|
+
return false;
|
|
3482
|
+
}
|
|
3483
|
+
|
|
3484
|
+
const advance = this._getPageAdvance(currentPageNumber,
|
|
3485
|
+
/* previous = */
|
|
3486
|
+
false) || 1;
|
|
3487
|
+
this.currentPageNumber = Math.min(currentPageNumber + advance, pagesCount);
|
|
3488
|
+
return true;
|
|
3489
|
+
}
|
|
3490
|
+
/**
|
|
3491
|
+
* Go to the previous page, taking scroll/spread-modes into account.
|
|
3492
|
+
* @returns {boolean} Whether navigation occured.
|
|
3493
|
+
*/
|
|
3494
|
+
|
|
3495
|
+
|
|
3496
|
+
previousPage() {
|
|
3497
|
+
const currentPageNumber = this._currentPageNumber;
|
|
3498
|
+
|
|
3499
|
+
if (currentPageNumber <= 1) {
|
|
3500
|
+
return false;
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3503
|
+
const advance = this._getPageAdvance(currentPageNumber,
|
|
3504
|
+
/* previous = */
|
|
3505
|
+
true) || 1;
|
|
3506
|
+
this.currentPageNumber = Math.max(currentPageNumber - advance, 1);
|
|
3507
|
+
return true;
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
}
|
|
3511
|
+
|
|
3512
|
+
/* Copyright 2014 Mozilla Foundation
|
|
3513
|
+
*
|
|
3514
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3515
|
+
* you may not use this file except in compliance with the License.
|
|
3516
|
+
* You may obtain a copy of the License at
|
|
3517
|
+
*
|
|
3518
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3519
|
+
*
|
|
3520
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
3521
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3522
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3523
|
+
* See the License for the specific language governing permissions and
|
|
3524
|
+
* limitations under the License.
|
|
3525
|
+
*/
|
|
3526
|
+
|
|
3527
|
+
class PDFViewer extends BaseViewer {
|
|
3528
|
+
get _viewerElement() {
|
|
3529
|
+
return shadow(this, "_viewerElement", this.viewer);
|
|
3530
|
+
}
|
|
3531
|
+
|
|
3532
|
+
_scrollIntoView({
|
|
3533
|
+
pageDiv,
|
|
3534
|
+
pageSpot = null,
|
|
3535
|
+
pageNumber = null
|
|
3536
|
+
}) {
|
|
3537
|
+
if (!pageSpot && !this.isInPresentationMode) {
|
|
3538
|
+
const left = pageDiv.offsetLeft + pageDiv.clientLeft;
|
|
3539
|
+
const right = left + pageDiv.clientWidth;
|
|
3540
|
+
const {
|
|
3541
|
+
scrollLeft,
|
|
3542
|
+
clientWidth
|
|
3543
|
+
} = this.container;
|
|
3544
|
+
|
|
3545
|
+
if (this._isScrollModeHorizontal || left < scrollLeft || right > scrollLeft + clientWidth) {
|
|
3546
|
+
pageSpot = {
|
|
3547
|
+
left: 0,
|
|
3548
|
+
top: 0
|
|
3549
|
+
};
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3553
|
+
super._scrollIntoView({
|
|
3554
|
+
pageDiv,
|
|
3555
|
+
pageSpot,
|
|
3556
|
+
pageNumber
|
|
3557
|
+
});
|
|
3558
|
+
}
|
|
3559
|
+
|
|
3560
|
+
_getVisiblePages() {
|
|
3561
|
+
if (this.isInPresentationMode) {
|
|
3562
|
+
// The algorithm in `getVisibleElements` doesn't work in all browsers and
|
|
3563
|
+
// configurations (e.g. Chrome) when Presentation Mode is active.
|
|
3564
|
+
return this._getCurrentVisiblePage();
|
|
3565
|
+
}
|
|
3566
|
+
|
|
3567
|
+
return super._getVisiblePages();
|
|
3568
|
+
}
|
|
3569
|
+
|
|
3570
|
+
_updateHelper(visiblePages) {
|
|
3571
|
+
if (this.isInPresentationMode) {
|
|
3572
|
+
return;
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3575
|
+
let currentId = this._currentPageNumber;
|
|
3576
|
+
let stillFullyVisible = false;
|
|
3577
|
+
|
|
3578
|
+
for (const page of visiblePages) {
|
|
3579
|
+
if (page.percent < 100) {
|
|
3580
|
+
break;
|
|
3581
|
+
}
|
|
3582
|
+
|
|
3583
|
+
if (page.id === currentId && this._scrollMode === ScrollMode.VERTICAL && this._spreadMode === SpreadMode.NONE) {
|
|
3584
|
+
stillFullyVisible = true;
|
|
3585
|
+
break;
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3589
|
+
if (!stillFullyVisible) {
|
|
3590
|
+
currentId = visiblePages[0].id;
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3593
|
+
this._setCurrentPageNumber(currentId);
|
|
3594
|
+
}
|
|
3595
|
+
|
|
3596
|
+
}
|
|
3597
|
+
|
|
3598
|
+
export { PDFViewer };
|