2013-09-20 16:25:41 +09:00
|
|
|
/* Copyright 2012 Mozilla Foundation
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2021-12-15 07:59:17 +09:00
|
|
|
// eslint-disable-next-line max-len
|
|
|
|
/** @typedef {import("../src/display/display_utils").PageViewport} PageViewport */
|
|
|
|
// eslint-disable-next-line max-len
|
|
|
|
/** @typedef {import("../src/display/optional_content_config").OptionalContentConfig} OptionalContentConfig */
|
2021-12-15 21:54:29 +09:00
|
|
|
/** @typedef {import("./event_utils").EventBus} EventBus */
|
2021-12-15 07:59:17 +09:00
|
|
|
/** @typedef {import("./interfaces").IL10n} IL10n */
|
Fix Viewer API definitions and include in CI
The Viewer API definitions do not compile because of missing imports and
anonymous objects are typed as `Object`. These issues were not caught
during CI because the test project was not compiling anything from the
Viewer API.
As an example of the first problem:
```
/**
* @implements MyInterface
*/
export class MyClass {
...
}
```
will generate a broken definition that doesn’t import MyInterface:
```
/**
* @implements MyInterface
*/
export class MyClass implements MyInterface {
...
}
```
This can be fixed by adding a typedef jsdoc to specify the import:
```
/** @typedef {import("./otherFile").MyInterface} MyInterface */
```
See https://github.com/jsdoc/jsdoc/issues/1537 and
https://github.com/microsoft/TypeScript/issues/22160 for more details.
As an example of the second problem:
```
/**
* Gets the size of the specified page, converted from PDF units to inches.
* @param {Object} An Object containing the properties: {Array} `view`,
* {number} `userUnit`, and {number} `rotate`.
*/
function getPageSizeInches({ view, userUnit, rotate }) {
...
}
```
generates the broken definition:
```
function getPageSizeInches({ view, userUnit, rotate }: Object) {
...
}
```
The jsdoc should specify the type of each nested property:
```
/**
* Gets the size of the specified page, converted from PDF units to inches.
* @param {Object} options An object containing the properties: {Array} `view`,
* {number} `userUnit`, and {number} `rotate`.
* @param {number[]} options.view
* @param {number} options.userUnit
* @param {number} options.rotate
*/
```
2021-08-26 07:44:06 +09:00
|
|
|
/** @typedef {import("./interfaces").IRenderableView} IRenderableView */
|
2021-12-15 07:59:17 +09:00
|
|
|
// eslint-disable-next-line max-len
|
|
|
|
/** @typedef {import("./pdf_rendering_queue").PDFRenderingQueue} PDFRenderingQueue */
|
Fix Viewer API definitions and include in CI
The Viewer API definitions do not compile because of missing imports and
anonymous objects are typed as `Object`. These issues were not caught
during CI because the test project was not compiling anything from the
Viewer API.
As an example of the first problem:
```
/**
* @implements MyInterface
*/
export class MyClass {
...
}
```
will generate a broken definition that doesn’t import MyInterface:
```
/**
* @implements MyInterface
*/
export class MyClass implements MyInterface {
...
}
```
This can be fixed by adding a typedef jsdoc to specify the import:
```
/** @typedef {import("./otherFile").MyInterface} MyInterface */
```
See https://github.com/jsdoc/jsdoc/issues/1537 and
https://github.com/microsoft/TypeScript/issues/22160 for more details.
As an example of the second problem:
```
/**
* Gets the size of the specified page, converted from PDF units to inches.
* @param {Object} An Object containing the properties: {Array} `view`,
* {number} `userUnit`, and {number} `rotate`.
*/
function getPageSizeInches({ view, userUnit, rotate }) {
...
}
```
generates the broken definition:
```
function getPageSizeInches({ view, userUnit, rotate }: Object) {
...
}
```
The jsdoc should specify the type of each nested property:
```
/**
* Gets the size of the specified page, converted from PDF units to inches.
* @param {Object} options An object containing the properties: {Array} `view`,
* {number} `userUnit`, and {number} `rotate`.
* @param {number[]} options.view
* @param {number} options.userUnit
* @param {number} options.rotate
*/
```
2021-08-26 07:44:06 +09:00
|
|
|
|
[api-minor] Introduce a new `annotationMode`-option, in `PDFPageProxy.{render, getOperatorList}`
*This is a follow-up to PRs 13867 and 13899.*
This patch is tagged `api-minor` for the following reasons:
- It replaces the `renderInteractiveForms`/`includeAnnotationStorage`-options, in the `PDFPageProxy.render`-method, with the single `annotationMode`-option that controls which annotations are being rendered and how. Note that the old options were mutually exclusive, and setting both to `true` would result in undefined behaviour.
- For improved consistency in the API, the `annotationMode`-option will also work together with the `PDFPageProxy.getOperatorList`-method.
- It's now also possible to disable *all* annotation rendering in both the API and the Viewer, since the other changes meant that this could now be supported with a single added line on the worker-thread[1]; fixes 7282.
---
[1] Please note that in order to simplify the overall implementation, we'll purposely only support disabling of *all* annotations and that the option is being shared between the API and the Viewer. For any more "specialized" use-cases, where e.g. only some annotation-types are being rendered and/or the API and Viewer render different sets of annotations, that'll have to be handled in third-party implementations/forks of the PDF.js code-base.
2021-08-08 21:36:28 +09:00
|
|
|
import {
|
2022-11-22 01:15:39 +09:00
|
|
|
AbortException,
|
[api-minor] Introduce a new `annotationMode`-option, in `PDFPageProxy.{render, getOperatorList}`
*This is a follow-up to PRs 13867 and 13899.*
This patch is tagged `api-minor` for the following reasons:
- It replaces the `renderInteractiveForms`/`includeAnnotationStorage`-options, in the `PDFPageProxy.render`-method, with the single `annotationMode`-option that controls which annotations are being rendered and how. Note that the old options were mutually exclusive, and setting both to `true` would result in undefined behaviour.
- For improved consistency in the API, the `annotationMode`-option will also work together with the `PDFPageProxy.getOperatorList`-method.
- It's now also possible to disable *all* annotation rendering in both the API and the Viewer, since the other changes meant that this could now be supported with a single added line on the worker-thread[1]; fixes 7282.
---
[1] Please note that in order to simplify the overall implementation, we'll purposely only support disabling of *all* annotations and that the option is being shared between the API and the Viewer. For any more "specialized" use-cases, where e.g. only some annotation-types are being rendered and/or the API and Viewer render different sets of annotations, that'll have to be handled in third-party implementations/forks of the PDF.js code-base.
2021-08-08 21:36:28 +09:00
|
|
|
AnnotationMode,
|
2021-09-20 17:10:57 +09:00
|
|
|
PixelsPerInch,
|
[api-minor] Introduce a new `annotationMode`-option, in `PDFPageProxy.{render, getOperatorList}`
*This is a follow-up to PRs 13867 and 13899.*
This patch is tagged `api-minor` for the following reasons:
- It replaces the `renderInteractiveForms`/`includeAnnotationStorage`-options, in the `PDFPageProxy.render`-method, with the single `annotationMode`-option that controls which annotations are being rendered and how. Note that the old options were mutually exclusive, and setting both to `true` would result in undefined behaviour.
- For improved consistency in the API, the `annotationMode`-option will also work together with the `PDFPageProxy.getOperatorList`-method.
- It's now also possible to disable *all* annotation rendering in both the API and the Viewer, since the other changes meant that this could now be supported with a single added line on the worker-thread[1]; fixes 7282.
---
[1] Please note that in order to simplify the overall implementation, we'll purposely only support disabling of *all* annotations and that the option is being shared between the API and the Viewer. For any more "specialized" use-cases, where e.g. only some annotation-types are being rendered and/or the API and Viewer render different sets of annotations, that'll have to be handled in third-party implementations/forks of the PDF.js code-base.
2021-08-08 21:36:28 +09:00
|
|
|
RenderingCancelledException,
|
2022-11-22 02:48:37 +09:00
|
|
|
setLayerDimensions,
|
2022-12-14 21:19:41 +09:00
|
|
|
shadow,
|
[api-minor] Introduce a new `annotationMode`-option, in `PDFPageProxy.{render, getOperatorList}`
*This is a follow-up to PRs 13867 and 13899.*
This patch is tagged `api-minor` for the following reasons:
- It replaces the `renderInteractiveForms`/`includeAnnotationStorage`-options, in the `PDFPageProxy.render`-method, with the single `annotationMode`-option that controls which annotations are being rendered and how. Note that the old options were mutually exclusive, and setting both to `true` would result in undefined behaviour.
- For improved consistency in the API, the `annotationMode`-option will also work together with the `PDFPageProxy.getOperatorList`-method.
- It's now also possible to disable *all* annotation rendering in both the API and the Viewer, since the other changes meant that this could now be supported with a single added line on the worker-thread[1]; fixes 7282.
---
[1] Please note that in order to simplify the overall implementation, we'll purposely only support disabling of *all* annotations and that the option is being shared between the API and the Viewer. For any more "specialized" use-cases, where e.g. only some annotation-types are being rendered and/or the API and Viewer render different sets of annotations, that'll have to be handled in third-party implementations/forks of the PDF.js code-base.
2021-08-08 21:36:28 +09:00
|
|
|
} from "pdfjs-lib";
|
2017-03-28 08:07:27 +09:00
|
|
|
import {
|
2018-09-21 03:51:50 +09:00
|
|
|
approximateFraction,
|
|
|
|
DEFAULT_SCALE,
|
2022-02-19 00:38:25 +09:00
|
|
|
OutputScale,
|
2021-12-15 21:54:29 +09:00
|
|
|
RenderingStates,
|
2018-09-21 03:51:50 +09:00
|
|
|
roundToDivide,
|
|
|
|
TextLayerMode,
|
2020-01-02 20:00:16 +09:00
|
|
|
} from "./ui_utils.js";
|
[api-minor] Remove the `annotationEditorLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `annotationEditorLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-06 22:21:51 +09:00
|
|
|
import { AnnotationEditorLayerBuilder } from "./annotation_editor_layer_builder.js";
|
[api-minor] Remove the `annotationLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `annotationLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 07:34:55 +09:00
|
|
|
import { AnnotationLayerBuilder } from "./annotation_layer_builder.js";
|
2024-02-20 18:50:28 +09:00
|
|
|
import { AppOptions } from "./app_options.js";
|
2023-11-23 03:02:42 +09:00
|
|
|
import { DrawLayerBuilder } from "./draw_layer_builder.js";
|
2024-01-31 02:32:35 +09:00
|
|
|
import { GenericL10n } from "web-null_l10n";
|
2023-02-06 00:13:18 +09:00
|
|
|
import { SimpleLinkService } from "./pdf_link_service.js";
|
[api-minor] Remove the `structTreeLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `structTreeLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 07:43:09 +09:00
|
|
|
import { StructTreeLayerBuilder } from "./struct_tree_layer_builder.js";
|
2022-07-29 00:59:03 +09:00
|
|
|
import { TextAccessibilityManager } from "./text_accessibility.js";
|
[api-minor] Remove the `textHighlighterFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `textHighlighterFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 07:55:23 +09:00
|
|
|
import { TextHighlighter } from "./text_highlighter.js";
|
[api-minor] Remove the `textLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `textLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 08:09:14 +09:00
|
|
|
import { TextLayerBuilder } from "./text_layer_builder.js";
|
[api-minor] Remove the `xfaLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `xfaLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 08:13:44 +09:00
|
|
|
import { XfaLayerBuilder } from "./xfa_layer_builder.js";
|
2016-04-09 02:34:27 +09:00
|
|
|
|
2014-09-21 02:21:49 +09:00
|
|
|
/**
|
2014-09-28 03:03:28 +09:00
|
|
|
* @typedef {Object} PDFPageViewOptions
|
2021-10-28 18:57:27 +09:00
|
|
|
* @property {HTMLDivElement} [container] - The viewer element.
|
2016-04-26 07:57:15 +09:00
|
|
|
* @property {EventBus} eventBus - The application event bus.
|
2014-09-28 03:03:28 +09:00
|
|
|
* @property {number} id - The page unique ID (normally its number).
|
2022-08-26 01:36:05 +09:00
|
|
|
* @property {number} [scale] - The page scale display.
|
2014-09-28 03:03:28 +09:00
|
|
|
* @property {PageViewport} defaultViewport - The page viewport.
|
[api-minor] Add support for toggling of Optional Content in the viewer (issue 12096)
*Besides, obviously, adding viewer support:* This patch attempts to improve the general API for Optional Content Groups slightly, by adding a couple of new methods for interacting with the (more complex) data structures of `OptionalContentConfig`-instances. (Thus allowing us to mark some of the data as "private", given that it probably shouldn't be manipulated directly.)
By utilizing not just the "raw" Optional Content Groups, but the data from the `/Order` array when available, we can thus display the Layers in a proper tree-structure with collapsible headings for PDF documents that utilizes that feature.
Note that it's possible to reset all Optional Content Groups to their default visibility state, simply by double-clicking on the Layers-button in the sidebar.
(Currently that's indicated in the Layers-button tooltip, which is obviously easy to overlook, however it's probably the best we can do for now without adding more buttons, or even a dropdown-toolbar, to the sidebar.)
Also, the current Layers-button icons are a little rough around the edges, quite literally, but given that the viewer will soon have its UI modernized anyway they hopefully suffice in the meantime.
To give users *full* control of the visibility of the various Optional Content Groups, even those which according to the `/Order` array should not (by default) be toggleable in the UI, this patch will place those under a *custom* heading which:
- Is collapsed by default, and placed at the bottom of the Layers-tree, to be a bit less obtrusive.
- Uses a slightly different formatting, compared to the "regular" headings.
- Is localizable.
Finally, note that the thumbnails are *purposely* always rendered with all Optional Content Groups at their default visibility state, since that seems the most useful and it's also consistent with other viewers.
To ensure that this works as intended, we'll thus disable the `PDFThumbnailView.setImage` functionality when the Optional Content Groups have been changed in the viewer. (This obviously means that we'll re-render thumbnails instead of using the rendered pages. However, this situation ought to be rare enough for this to not really be a problem.)
2020-08-07 04:01:03 +09:00
|
|
|
* @property {Promise<OptionalContentConfig>} [optionalContentConfigPromise] -
|
|
|
|
* A promise that is resolved with an {@link OptionalContentConfig} instance.
|
|
|
|
* The default value is `null`.
|
2022-08-26 01:36:05 +09:00
|
|
|
* @property {PDFRenderingQueue} [renderingQueue] - The rendering queue object.
|
2019-10-12 23:30:32 +09:00
|
|
|
* @property {number} [textLayerMode] - Controls if the text layer used for
|
2022-01-19 02:09:12 +09:00
|
|
|
* selection and searching is created. The constants from {TextLayerMode}
|
|
|
|
* should be used. The default value is `TextLayerMode.ENABLE`.
|
[api-minor] Introduce a new `annotationMode`-option, in `PDFPageProxy.{render, getOperatorList}`
*This is a follow-up to PRs 13867 and 13899.*
This patch is tagged `api-minor` for the following reasons:
- It replaces the `renderInteractiveForms`/`includeAnnotationStorage`-options, in the `PDFPageProxy.render`-method, with the single `annotationMode`-option that controls which annotations are being rendered and how. Note that the old options were mutually exclusive, and setting both to `true` would result in undefined behaviour.
- For improved consistency in the API, the `annotationMode`-option will also work together with the `PDFPageProxy.getOperatorList`-method.
- It's now also possible to disable *all* annotation rendering in both the API and the Viewer, since the other changes meant that this could now be supported with a single added line on the worker-thread[1]; fixes 7282.
---
[1] Please note that in order to simplify the overall implementation, we'll purposely only support disabling of *all* annotations and that the option is being shared between the API and the Viewer. For any more "specialized" use-cases, where e.g. only some annotation-types are being rendered and/or the API and Viewer render different sets of annotations, that'll have to be handled in third-party implementations/forks of the PDF.js code-base.
2021-08-08 21:36:28 +09:00
|
|
|
* @property {number} [annotationMode] - Controls if the annotation layer is
|
|
|
|
* created, and if interactive form elements or `AnnotationStorage`-data are
|
|
|
|
* being rendered. The constants from {@link AnnotationMode} should be used;
|
|
|
|
* see also {@link RenderParameters} and {@link GetOperatorListParameters}.
|
|
|
|
* The default value is `AnnotationMode.ENABLE_FORMS`.
|
2019-10-12 23:30:32 +09:00
|
|
|
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
|
|
|
* for annotation icons. Include trailing slash.
|
|
|
|
* @property {number} [maxCanvasPixels] - The maximum supported canvas size in
|
2023-07-24 17:33:08 +09:00
|
|
|
* total pixels, i.e. width * height. Use `-1` for no limit, or `0` for
|
|
|
|
* CSS-only zooming. The default value is 4096 * 4096 (16 mega-pixels).
|
2022-05-04 22:37:13 +09:00
|
|
|
* @property {Object} [pageColors] - Overwrites background and foreground colors
|
|
|
|
* with user defined ones in order to improve readability in high contrast
|
|
|
|
* mode.
|
2022-08-26 01:36:05 +09:00
|
|
|
* @property {IL10n} [l10n] - Localization service.
|
2023-09-25 17:56:12 +09:00
|
|
|
* @property {Object} [layerProperties] - The object that is used to lookup
|
[api-minor] Remove the `annotationEditorLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `annotationEditorLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-06 22:21:51 +09:00
|
|
|
* the necessary layer-properties.
|
2014-09-28 03:03:28 +09:00
|
|
|
*/
|
|
|
|
|
2023-09-25 17:56:12 +09:00
|
|
|
const DEFAULT_LAYER_PROPERTIES =
|
|
|
|
typeof PDFJSDev === "undefined" || !PDFJSDev.test("COMPONENTS")
|
|
|
|
? null
|
|
|
|
: {
|
|
|
|
annotationEditorUIManager: null,
|
|
|
|
annotationStorage: null,
|
|
|
|
downloadManager: null,
|
|
|
|
enableScripting: false,
|
|
|
|
fieldObjectsPromise: null,
|
|
|
|
findController: null,
|
|
|
|
hasJSActionsPromise: null,
|
|
|
|
get linkService() {
|
|
|
|
return new SimpleLinkService();
|
|
|
|
},
|
|
|
|
};
|
[api-minor] Remove the `annotationEditorLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `annotationEditorLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-06 22:21:51 +09:00
|
|
|
|
2014-09-28 03:03:28 +09:00
|
|
|
/**
|
2014-09-21 02:21:49 +09:00
|
|
|
* @implements {IRenderableView}
|
|
|
|
*/
|
2017-05-29 05:44:00 +09:00
|
|
|
class PDFPageView {
|
2021-12-12 00:53:59 +09:00
|
|
|
#annotationMode = AnnotationMode.ENABLE_FORMS;
|
|
|
|
|
2023-07-24 17:33:08 +09:00
|
|
|
#hasRestrictedScaling = false;
|
|
|
|
|
[api-minor] Remove the `annotationEditorLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `annotationEditorLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-06 22:21:51 +09:00
|
|
|
#layerProperties = null;
|
|
|
|
|
2023-01-30 01:53:01 +09:00
|
|
|
#loadingId = null;
|
|
|
|
|
2022-12-12 22:24:27 +09:00
|
|
|
#previousRotation = null;
|
|
|
|
|
2023-03-29 04:52:07 +09:00
|
|
|
#renderError = null;
|
|
|
|
|
2022-12-26 23:00:22 +09:00
|
|
|
#renderingState = RenderingStates.INITIAL;
|
|
|
|
|
2023-04-22 20:07:07 +09:00
|
|
|
#textLayerMode = TextLayerMode.ENABLE;
|
|
|
|
|
2022-06-27 18:41:37 +09:00
|
|
|
#useThumbnailCanvas = {
|
2023-06-02 01:46:50 +09:00
|
|
|
directDrawing: true,
|
2022-06-27 18:41:37 +09:00
|
|
|
initialOptionalContent: true,
|
|
|
|
regularAnnotations: true,
|
|
|
|
};
|
2022-07-24 20:14:58 +09:00
|
|
|
|
2023-03-29 04:52:07 +09:00
|
|
|
#viewportMap = new WeakMap();
|
|
|
|
|
2014-09-28 03:03:28 +09:00
|
|
|
/**
|
|
|
|
* @param {PDFPageViewOptions} options
|
|
|
|
*/
|
2017-05-29 05:44:00 +09:00
|
|
|
constructor(options) {
|
2019-12-27 08:22:32 +09:00
|
|
|
const container = options.container;
|
|
|
|
const defaultViewport = options.defaultViewport;
|
2017-05-29 05:44:00 +09:00
|
|
|
|
|
|
|
this.id = options.id;
|
|
|
|
this.renderingId = "page" + this.id;
|
[api-minor] Remove the `annotationEditorLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `annotationEditorLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-06 22:21:51 +09:00
|
|
|
this.#layerProperties = options.layerProperties || DEFAULT_LAYER_PROPERTIES;
|
2014-09-28 03:03:28 +09:00
|
|
|
|
2017-08-04 07:24:19 +09:00
|
|
|
this.pdfPage = null;
|
2017-05-29 05:44:00 +09:00
|
|
|
this.pageLabel = null;
|
2014-09-28 03:03:28 +09:00
|
|
|
this.rotation = 0;
|
2017-05-29 05:44:00 +09:00
|
|
|
this.scale = options.scale || DEFAULT_SCALE;
|
2014-09-28 03:03:28 +09:00
|
|
|
this.viewport = defaultViewport;
|
|
|
|
this.pdfPageRotate = defaultViewport.rotation;
|
[api-minor] Add support for toggling of Optional Content in the viewer (issue 12096)
*Besides, obviously, adding viewer support:* This patch attempts to improve the general API for Optional Content Groups slightly, by adding a couple of new methods for interacting with the (more complex) data structures of `OptionalContentConfig`-instances. (Thus allowing us to mark some of the data as "private", given that it probably shouldn't be manipulated directly.)
By utilizing not just the "raw" Optional Content Groups, but the data from the `/Order` array when available, we can thus display the Layers in a proper tree-structure with collapsible headings for PDF documents that utilizes that feature.
Note that it's possible to reset all Optional Content Groups to their default visibility state, simply by double-clicking on the Layers-button in the sidebar.
(Currently that's indicated in the Layers-button tooltip, which is obviously easy to overlook, however it's probably the best we can do for now without adding more buttons, or even a dropdown-toolbar, to the sidebar.)
Also, the current Layers-button icons are a little rough around the edges, quite literally, but given that the viewer will soon have its UI modernized anyway they hopefully suffice in the meantime.
To give users *full* control of the visibility of the various Optional Content Groups, even those which according to the `/Order` array should not (by default) be toggleable in the UI, this patch will place those under a *custom* heading which:
- Is collapsed by default, and placed at the bottom of the Layers-tree, to be a bit less obtrusive.
- Uses a slightly different formatting, compared to the "regular" headings.
- Is localizable.
Finally, note that the thumbnails are *purposely* always rendered with all Optional Content Groups at their default visibility state, since that seems the most useful and it's also consistent with other viewers.
To ensure that this works as intended, we'll thus disable the `PDFThumbnailView.setImage` functionality when the Optional Content Groups have been changed in the viewer. (This obviously means that we'll re-render thumbnails instead of using the rendered pages. However, this situation ought to be rare enough for this to not really be a problem.)
2020-08-07 04:01:03 +09:00
|
|
|
this._optionalContentConfigPromise =
|
|
|
|
options.optionalContentConfigPromise || null;
|
2023-04-22 20:07:07 +09:00
|
|
|
this.#textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE;
|
2021-12-12 00:53:59 +09:00
|
|
|
this.#annotationMode =
|
[api-minor] Introduce a new `annotationMode`-option, in `PDFPageProxy.{render, getOperatorList}`
*This is a follow-up to PRs 13867 and 13899.*
This patch is tagged `api-minor` for the following reasons:
- It replaces the `renderInteractiveForms`/`includeAnnotationStorage`-options, in the `PDFPageProxy.render`-method, with the single `annotationMode`-option that controls which annotations are being rendered and how. Note that the old options were mutually exclusive, and setting both to `true` would result in undefined behaviour.
- For improved consistency in the API, the `annotationMode`-option will also work together with the `PDFPageProxy.getOperatorList`-method.
- It's now also possible to disable *all* annotation rendering in both the API and the Viewer, since the other changes meant that this could now be supported with a single added line on the worker-thread[1]; fixes 7282.
---
[1] Please note that in order to simplify the overall implementation, we'll purposely only support disabling of *all* annotations and that the option is being shared between the API and the Viewer. For any more "specialized" use-cases, where e.g. only some annotation-types are being rendered and/or the API and Viewer render different sets of annotations, that'll have to be handled in third-party implementations/forks of the PDF.js code-base.
2021-08-08 21:36:28 +09:00
|
|
|
options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
|
2018-02-13 21:17:11 +09:00
|
|
|
this.imageResourcesPath = options.imageResourcesPath || "";
|
2024-02-20 18:50:28 +09:00
|
|
|
this.maxCanvasPixels =
|
|
|
|
options.maxCanvasPixels ??
|
|
|
|
(AppOptions.getCompat("maxCanvasPixels") || 16777216);
|
2022-05-04 22:37:13 +09:00
|
|
|
this.pageColors = options.pageColors || null;
|
2014-09-28 03:03:28 +09:00
|
|
|
|
2020-02-27 23:02:03 +09:00
|
|
|
this.eventBus = options.eventBus;
|
2017-05-29 05:44:00 +09:00
|
|
|
this.renderingQueue = options.renderingQueue;
|
2024-01-31 02:32:35 +09:00
|
|
|
this.l10n = options.l10n;
|
|
|
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
|
|
|
this.l10n ||= new GenericL10n();
|
|
|
|
}
|
2014-09-28 03:03:28 +09:00
|
|
|
|
2023-03-29 04:52:07 +09:00
|
|
|
this.renderTask = null;
|
2014-09-28 03:03:28 +09:00
|
|
|
this.resume = null;
|
2023-03-18 20:09:25 +09:00
|
|
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
2022-07-24 22:43:31 +09:00
|
|
|
this._isStandalone = !this.renderingQueue?.hasViewer();
|
2023-06-27 06:12:37 +09:00
|
|
|
this._container = container;
|
2022-07-24 22:43:31 +09:00
|
|
|
}
|
2014-09-28 03:03:28 +09:00
|
|
|
|
2021-11-07 02:36:49 +09:00
|
|
|
this._annotationCanvasMap = null;
|
|
|
|
|
2017-05-29 05:44:00 +09:00
|
|
|
this.annotationLayer = null;
|
2022-06-01 17:38:08 +09:00
|
|
|
this.annotationEditorLayer = null;
|
2014-09-28 03:03:28 +09:00
|
|
|
this.textLayer = null;
|
2013-10-03 01:05:46 +09:00
|
|
|
this.zoomLayer = null;
|
2021-03-19 18:11:40 +09:00
|
|
|
this.xfaLayer = null;
|
2021-04-01 07:07:02 +09:00
|
|
|
this.structTreeLayer = null;
|
2023-11-23 03:02:42 +09:00
|
|
|
this.drawLayer = null;
|
2013-09-20 16:25:41 +09:00
|
|
|
|
2019-12-27 08:22:32 +09:00
|
|
|
const div = document.createElement("div");
|
2014-09-28 03:03:28 +09:00
|
|
|
div.className = "page";
|
2015-03-25 13:34:42 +09:00
|
|
|
div.setAttribute("data-page-number", this.id);
|
2021-03-19 08:10:54 +09:00
|
|
|
div.setAttribute("role", "region");
|
2023-10-19 23:30:57 +09:00
|
|
|
div.setAttribute("data-l10n-id", "pdfjs-page-landmark");
|
|
|
|
div.setAttribute("data-l10n-args", JSON.stringify({ page: this.id }));
|
2014-09-28 03:03:28 +09:00
|
|
|
this.div = div;
|
|
|
|
|
2022-11-22 02:48:37 +09:00
|
|
|
this.#setDimensions();
|
2022-06-12 19:20:25 +09:00
|
|
|
container?.append(div);
|
2022-07-24 20:14:58 +09:00
|
|
|
|
2022-07-24 22:43:31 +09:00
|
|
|
if (
|
2023-03-18 20:09:25 +09:00
|
|
|
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) &&
|
2022-07-24 22:43:31 +09:00
|
|
|
this._isStandalone
|
|
|
|
) {
|
2022-12-09 22:30:12 +09:00
|
|
|
// Ensure that the various layers always get the correct initial size,
|
|
|
|
// see issue 15795.
|
2023-01-17 03:38:33 +09:00
|
|
|
container?.style.setProperty(
|
2022-12-09 22:30:12 +09:00
|
|
|
"--scale-factor",
|
|
|
|
this.scale * PixelsPerInch.PDF_TO_CSS_UNITS
|
|
|
|
);
|
|
|
|
|
2022-07-24 20:14:58 +09:00
|
|
|
const { optionalContentConfigPromise } = options;
|
|
|
|
if (optionalContentConfigPromise) {
|
|
|
|
// Ensure that the thumbnails always display the *initial* document
|
2022-06-27 18:41:37 +09:00
|
|
|
// state, for documents with optional content.
|
2022-07-24 20:14:58 +09:00
|
|
|
optionalContentConfigPromise.then(optionalContentConfig => {
|
|
|
|
if (
|
|
|
|
optionalContentConfigPromise !== this._optionalContentConfigPromise
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
2022-06-27 18:41:37 +09:00
|
|
|
this.#useThumbnailCanvas.initialOptionalContent =
|
|
|
|
optionalContentConfig.hasInitialVisibility;
|
2022-07-24 20:14:58 +09:00
|
|
|
});
|
|
|
|
}
|
[api-minor] Re-factor `NullL10n` and remove the hard-coded l10n strings (PR 17115 follow-up)
*Please note:* These changes only affect the GENERIC build, since `NullL10n` is only a stub elsewhere (see PR 17135).
After the changes in PR 17115, which modernized and improved l10n-handling, the `NullL10n`-implementation is no longer a good fallback for the "proper" `L10n`-classes.
To improve this situation, especially for the *standalone* viewer-components, this patch makes the following changes:
- Let the `NullL10n`-implementation extend an actual `L10n`-class, which is constant and lazily initialized, to ensure that it works *exactly* like the "proper" ones.
- Automatically bundle the "en-US" l10n-strings in the build, via the pre-processor, such that we don't need to remember to manually update them.
- Ensure that the *standalone* viewer-components register their DOM-elements for translation, similar to the default viewer, since this will allow future code improvements by using "data-l10n-id"/"data-l10n-args" in most (if not all) parts of the viewer.
- Remove the `NullL10n` from the `AnnotationLayer`, to avoid affecting bundle size too much.
For third-party users that access the `AnnotationLayer`, as exposed in the main PDF.js library, they'll now need to *manually* register it for translation. (However, the *standalone* viewer-components still works given the point above.)
2023-10-20 05:12:46 +09:00
|
|
|
|
|
|
|
// Ensure that Fluent is connected in e.g. the COMPONENTS build.
|
2024-01-31 02:32:35 +09:00
|
|
|
if (!options.l10n) {
|
[api-minor] Re-factor `NullL10n` and remove the hard-coded l10n strings (PR 17115 follow-up)
*Please note:* These changes only affect the GENERIC build, since `NullL10n` is only a stub elsewhere (see PR 17135).
After the changes in PR 17115, which modernized and improved l10n-handling, the `NullL10n`-implementation is no longer a good fallback for the "proper" `L10n`-classes.
To improve this situation, especially for the *standalone* viewer-components, this patch makes the following changes:
- Let the `NullL10n`-implementation extend an actual `L10n`-class, which is constant and lazily initialized, to ensure that it works *exactly* like the "proper" ones.
- Automatically bundle the "en-US" l10n-strings in the build, via the pre-processor, such that we don't need to remember to manually update them.
- Ensure that the *standalone* viewer-components register their DOM-elements for translation, similar to the default viewer, since this will allow future code improvements by using "data-l10n-id"/"data-l10n-args" in most (if not all) parts of the viewer.
- Remove the `NullL10n` from the `AnnotationLayer`, to avoid affecting bundle size too much.
For third-party users that access the `AnnotationLayer`, as exposed in the main PDF.js library, they'll now need to *manually* register it for translation. (However, the *standalone* viewer-components still works given the point above.)
2023-10-20 05:12:46 +09:00
|
|
|
this.l10n.translate(this.div);
|
|
|
|
}
|
2022-07-24 20:14:58 +09:00
|
|
|
}
|
2014-09-28 03:03:28 +09:00
|
|
|
}
|
|
|
|
|
2022-12-26 23:00:22 +09:00
|
|
|
get renderingState() {
|
|
|
|
return this.#renderingState;
|
|
|
|
}
|
|
|
|
|
|
|
|
set renderingState(state) {
|
2023-01-30 01:53:01 +09:00
|
|
|
if (state === this.#renderingState) {
|
|
|
|
return;
|
|
|
|
}
|
2022-12-26 23:00:22 +09:00
|
|
|
this.#renderingState = state;
|
|
|
|
|
2023-01-30 01:53:01 +09:00
|
|
|
if (this.#loadingId) {
|
|
|
|
clearTimeout(this.#loadingId);
|
|
|
|
this.#loadingId = null;
|
|
|
|
}
|
|
|
|
|
2022-12-26 23:00:22 +09:00
|
|
|
switch (state) {
|
|
|
|
case RenderingStates.PAUSED:
|
2023-01-30 01:53:01 +09:00
|
|
|
this.div.classList.remove("loading");
|
2022-12-26 23:00:22 +09:00
|
|
|
break;
|
|
|
|
case RenderingStates.RUNNING:
|
2023-01-30 01:53:01 +09:00
|
|
|
this.div.classList.add("loadingIcon");
|
|
|
|
this.#loadingId = setTimeout(() => {
|
|
|
|
// Adding the loading class is slightly postponed in order to not have
|
|
|
|
// it with loadingIcon.
|
|
|
|
// If we don't do that the visibility of the background is changed but
|
|
|
|
// the transition isn't triggered.
|
|
|
|
this.div.classList.add("loading");
|
|
|
|
this.#loadingId = null;
|
|
|
|
}, 0);
|
2022-12-26 23:00:22 +09:00
|
|
|
break;
|
2023-02-03 05:17:49 +09:00
|
|
|
case RenderingStates.INITIAL:
|
2022-12-26 23:00:22 +09:00
|
|
|
case RenderingStates.FINISHED:
|
2023-01-30 01:53:01 +09:00
|
|
|
this.div.classList.remove("loadingIcon", "loading");
|
2022-12-26 23:00:22 +09:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-22 02:48:37 +09:00
|
|
|
#setDimensions() {
|
2022-12-12 22:24:27 +09:00
|
|
|
const { viewport } = this;
|
2022-12-27 20:14:42 +09:00
|
|
|
if (this.pdfPage) {
|
|
|
|
if (this.#previousRotation === viewport.rotation) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.#previousRotation = viewport.rotation;
|
2022-12-12 22:24:27 +09:00
|
|
|
}
|
|
|
|
|
2022-11-22 02:48:37 +09:00
|
|
|
setLayerDimensions(
|
2022-12-12 22:24:27 +09:00
|
|
|
this.div,
|
2022-11-22 02:48:37 +09:00
|
|
|
viewport,
|
|
|
|
/* mustFlip = */ true,
|
|
|
|
/* mustRotate = */ false
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-05-29 05:44:00 +09:00
|
|
|
setPdfPage(pdfPage) {
|
2023-06-27 06:12:37 +09:00
|
|
|
if (
|
|
|
|
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) &&
|
|
|
|
this._isStandalone &&
|
|
|
|
(this.pageColors?.foreground === "CanvasText" ||
|
|
|
|
this.pageColors?.background === "Canvas")
|
|
|
|
) {
|
|
|
|
this._container?.style.setProperty(
|
2023-11-13 19:18:35 +09:00
|
|
|
"--hcm-highlight-filter",
|
2023-06-27 06:12:37 +09:00
|
|
|
pdfPage.filterFactory.addHighlightHCMFilter(
|
2024-01-19 02:36:03 +09:00
|
|
|
"highlight",
|
|
|
|
"CanvasText",
|
|
|
|
"Canvas",
|
|
|
|
"HighlightText",
|
|
|
|
"Highlight"
|
|
|
|
)
|
|
|
|
);
|
|
|
|
this._container?.style.setProperty(
|
|
|
|
"--hcm-highlight-selected-filter",
|
|
|
|
pdfPage.filterFactory.addHighlightHCMFilter(
|
|
|
|
"highlight_selected",
|
2023-06-27 06:12:37 +09:00
|
|
|
"CanvasText",
|
|
|
|
"Canvas",
|
|
|
|
"HighlightText",
|
|
|
|
"Highlight"
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2017-05-29 05:44:00 +09:00
|
|
|
this.pdfPage = pdfPage;
|
|
|
|
this.pdfPageRotate = pdfPage.rotate;
|
2013-09-20 16:25:41 +09:00
|
|
|
|
2019-12-27 08:22:32 +09:00
|
|
|
const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
|
2018-12-21 19:47:37 +09:00
|
|
|
this.viewport = pdfPage.getViewport({
|
2021-09-20 17:10:57 +09:00
|
|
|
scale: this.scale * PixelsPerInch.PDF_TO_CSS_UNITS,
|
2018-12-21 19:47:37 +09:00
|
|
|
rotation: totalRotation,
|
|
|
|
});
|
2022-12-12 22:24:27 +09:00
|
|
|
this.#setDimensions();
|
2017-05-29 05:44:00 +09:00
|
|
|
this.reset();
|
|
|
|
}
|
2014-09-28 03:03:28 +09:00
|
|
|
|
2017-05-29 05:44:00 +09:00
|
|
|
destroy() {
|
|
|
|
this.reset();
|
2022-09-05 22:36:04 +09:00
|
|
|
this.pdfPage?.cleanup();
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
2013-09-20 16:25:41 +09:00
|
|
|
|
2022-12-14 21:19:41 +09:00
|
|
|
get _textHighlighter() {
|
|
|
|
return shadow(
|
|
|
|
this,
|
|
|
|
"_textHighlighter",
|
[api-minor] Remove the `textHighlighterFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `textHighlighterFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 07:55:23 +09:00
|
|
|
new TextHighlighter({
|
2022-12-14 21:19:41 +09:00
|
|
|
pageIndex: this.id - 1,
|
|
|
|
eventBus: this.eventBus,
|
2023-09-25 17:56:12 +09:00
|
|
|
findController: this.#layerProperties.findController,
|
2022-12-14 21:19:41 +09:00
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-12-14 21:12:49 +09:00
|
|
|
async #renderAnnotationLayer() {
|
2020-08-18 05:19:03 +09:00
|
|
|
let error = null;
|
|
|
|
try {
|
|
|
|
await this.annotationLayer.render(this.viewport, "display");
|
|
|
|
} catch (ex) {
|
2022-12-14 21:12:49 +09:00
|
|
|
console.error(`#renderAnnotationLayer: "${ex}".`);
|
2020-08-18 05:19:03 +09:00
|
|
|
error = ex;
|
|
|
|
} finally {
|
|
|
|
this.eventBus.dispatch("annotationlayerrendered", {
|
|
|
|
source: this,
|
|
|
|
pageNumber: this.id,
|
|
|
|
error,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-14 21:12:49 +09:00
|
|
|
async #renderAnnotationEditorLayer() {
|
2022-06-01 17:38:08 +09:00
|
|
|
let error = null;
|
|
|
|
try {
|
|
|
|
await this.annotationEditorLayer.render(this.viewport, "display");
|
|
|
|
} catch (ex) {
|
2022-12-14 21:12:49 +09:00
|
|
|
console.error(`#renderAnnotationEditorLayer: "${ex}".`);
|
2022-06-01 17:38:08 +09:00
|
|
|
error = ex;
|
|
|
|
} finally {
|
|
|
|
this.eventBus.dispatch("annotationeditorlayerrendered", {
|
|
|
|
source: this,
|
|
|
|
pageNumber: this.id,
|
|
|
|
error,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-23 03:02:42 +09:00
|
|
|
async #renderDrawLayer() {
|
|
|
|
try {
|
|
|
|
await this.drawLayer.render("display");
|
|
|
|
} catch (ex) {
|
|
|
|
console.error(`#renderDrawLayer: "${ex}".`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-14 21:12:49 +09:00
|
|
|
async #renderXfaLayer() {
|
2021-03-19 18:11:40 +09:00
|
|
|
let error = null;
|
|
|
|
try {
|
2021-08-19 09:02:29 +09:00
|
|
|
const result = await this.xfaLayer.render(this.viewport, "display");
|
2022-12-14 21:19:41 +09:00
|
|
|
if (result?.textDivs && this._textHighlighter) {
|
2023-10-26 03:03:07 +09:00
|
|
|
// Given that the following method fetches the text asynchronously we
|
|
|
|
// can invoke it *before* appending the xfaLayer to the DOM (below),
|
|
|
|
// since a pending search-highlight/scroll operation thus won't run
|
|
|
|
// until after the xfaLayer is available in the viewer.
|
2022-12-14 21:12:49 +09:00
|
|
|
this.#buildXfaTextContentItems(result.textDivs);
|
2021-08-19 09:02:29 +09:00
|
|
|
}
|
2021-03-19 18:11:40 +09:00
|
|
|
} catch (ex) {
|
2022-12-14 21:12:49 +09:00
|
|
|
console.error(`#renderXfaLayer: "${ex}".`);
|
2021-03-19 18:11:40 +09:00
|
|
|
error = ex;
|
|
|
|
} finally {
|
2023-10-26 03:03:07 +09:00
|
|
|
if (this.xfaLayer?.div) {
|
|
|
|
// Pause translation when inserting the xfaLayer in the DOM.
|
|
|
|
this.l10n.pause();
|
|
|
|
this.div.append(this.xfaLayer.div);
|
|
|
|
this.l10n.resume();
|
|
|
|
}
|
|
|
|
|
2021-03-19 18:11:40 +09:00
|
|
|
this.eventBus.dispatch("xfalayerrendered", {
|
|
|
|
source: this,
|
|
|
|
pageNumber: this.id,
|
|
|
|
error,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-22 01:15:39 +09:00
|
|
|
async #renderTextLayer() {
|
|
|
|
const { pdfPage, textLayer, viewport } = this;
|
|
|
|
if (!textLayer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let error = null;
|
|
|
|
try {
|
|
|
|
if (!textLayer.renderingDone) {
|
|
|
|
const readableStream = pdfPage.streamTextContent({
|
|
|
|
includeMarkedContent: true,
|
2023-03-23 18:15:14 +09:00
|
|
|
disableNormalization: true,
|
2022-11-22 01:15:39 +09:00
|
|
|
});
|
2022-12-05 01:42:24 +09:00
|
|
|
textLayer.setTextContentSource(readableStream);
|
2022-11-22 01:15:39 +09:00
|
|
|
}
|
|
|
|
await textLayer.render(viewport);
|
|
|
|
} catch (ex) {
|
|
|
|
if (ex instanceof AbortException) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
console.error(`#renderTextLayer: "${ex}".`);
|
|
|
|
error = ex;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.eventBus.dispatch("textlayerrendered", {
|
|
|
|
source: this,
|
|
|
|
pageNumber: this.id,
|
|
|
|
numTextDivs: textLayer.numTextDivs,
|
|
|
|
error,
|
|
|
|
});
|
2022-12-04 08:27:44 +09:00
|
|
|
|
[api-minor] Remove the `structTreeLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `structTreeLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 07:43:09 +09:00
|
|
|
this.#renderStructTreeLayer();
|
2022-12-04 08:27:44 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The structure tree is currently only supported when the text layer is
|
|
|
|
* enabled and a canvas is used for rendering.
|
|
|
|
*
|
|
|
|
* The structure tree must be generated after the text layer for the
|
|
|
|
* aria-owns to work.
|
|
|
|
*/
|
|
|
|
async #renderStructTreeLayer() {
|
|
|
|
if (!this.textLayer) {
|
|
|
|
return;
|
|
|
|
}
|
[api-minor] Remove the `structTreeLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `structTreeLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 07:43:09 +09:00
|
|
|
this.structTreeLayer ||= new StructTreeLayerBuilder();
|
2022-12-04 08:27:44 +09:00
|
|
|
|
|
|
|
const tree = await (!this.structTreeLayer.renderingDone
|
|
|
|
? this.pdfPage.getStructTree()
|
|
|
|
: null);
|
|
|
|
const treeDom = this.structTreeLayer?.render(tree);
|
|
|
|
if (treeDom) {
|
2023-10-27 21:55:12 +09:00
|
|
|
// Pause translation when inserting the structTree in the DOM.
|
|
|
|
this.l10n.pause();
|
2022-12-04 08:27:44 +09:00
|
|
|
this.canvas?.append(treeDom);
|
2023-10-27 21:55:12 +09:00
|
|
|
this.l10n.resume();
|
2022-12-04 08:27:44 +09:00
|
|
|
}
|
2023-02-07 22:38:32 +09:00
|
|
|
this.structTreeLayer?.show();
|
2022-11-22 01:15:39 +09:00
|
|
|
}
|
|
|
|
|
2022-12-14 21:12:49 +09:00
|
|
|
async #buildXfaTextContentItems(textDivs) {
|
2021-08-19 09:02:29 +09:00
|
|
|
const text = await this.pdfPage.getTextContent();
|
|
|
|
const items = [];
|
|
|
|
for (const item of text.items) {
|
|
|
|
items.push(item.str);
|
|
|
|
}
|
2022-12-14 21:19:41 +09:00
|
|
|
this._textHighlighter.setTextMapping(textDivs, items);
|
|
|
|
this._textHighlighter.enable();
|
2021-08-19 09:02:29 +09:00
|
|
|
}
|
|
|
|
|
2017-05-29 05:44:00 +09:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_resetZoomLayer(removeFromDOM = false) {
|
|
|
|
if (!this.zoomLayer) {
|
|
|
|
return;
|
|
|
|
}
|
2019-12-27 08:22:32 +09:00
|
|
|
const zoomLayerCanvas = this.zoomLayer.firstChild;
|
2023-03-29 04:52:07 +09:00
|
|
|
this.#viewportMap.delete(zoomLayerCanvas);
|
2017-05-29 05:44:00 +09:00
|
|
|
// Zeroing the width and height causes Firefox to release graphics
|
|
|
|
// resources immediately, which can greatly reduce memory consumption.
|
|
|
|
zoomLayerCanvas.width = 0;
|
|
|
|
zoomLayerCanvas.height = 0;
|
|
|
|
|
|
|
|
if (removeFromDOM) {
|
|
|
|
// Note: `ChildNode.remove` doesn't throw if the parent node is undefined.
|
|
|
|
this.zoomLayer.remove();
|
|
|
|
}
|
|
|
|
this.zoomLayer = null;
|
|
|
|
}
|
2013-10-03 01:05:46 +09:00
|
|
|
|
2021-07-23 18:37:38 +09:00
|
|
|
reset({
|
|
|
|
keepZoomLayer = false,
|
|
|
|
keepAnnotationLayer = false,
|
2022-06-01 17:38:08 +09:00
|
|
|
keepAnnotationEditorLayer = false,
|
2021-07-23 18:37:38 +09:00
|
|
|
keepXfaLayer = false,
|
2022-11-22 01:15:39 +09:00
|
|
|
keepTextLayer = false,
|
2021-07-23 18:37:38 +09:00
|
|
|
} = {}) {
|
2022-06-01 17:38:08 +09:00
|
|
|
this.cancelRendering({
|
|
|
|
keepAnnotationLayer,
|
|
|
|
keepAnnotationEditorLayer,
|
|
|
|
keepXfaLayer,
|
2022-11-22 01:15:39 +09:00
|
|
|
keepTextLayer,
|
2022-06-01 17:38:08 +09:00
|
|
|
});
|
2019-02-15 02:47:32 +09:00
|
|
|
this.renderingState = RenderingStates.INITIAL;
|
2013-10-03 01:05:46 +09:00
|
|
|
|
2019-12-27 08:22:32 +09:00
|
|
|
const div = this.div;
|
2013-10-03 01:05:46 +09:00
|
|
|
|
2021-07-23 18:37:38 +09:00
|
|
|
const childNodes = div.childNodes,
|
|
|
|
zoomLayerNode = (keepZoomLayer && this.zoomLayer) || null,
|
|
|
|
annotationLayerNode =
|
|
|
|
(keepAnnotationLayer && this.annotationLayer?.div) || null,
|
2022-06-01 17:38:08 +09:00
|
|
|
annotationEditorLayerNode =
|
|
|
|
(keepAnnotationEditorLayer && this.annotationEditorLayer?.div) || null,
|
2022-11-22 01:15:39 +09:00
|
|
|
xfaLayerNode = (keepXfaLayer && this.xfaLayer?.div) || null,
|
|
|
|
textLayerNode = (keepTextLayer && this.textLayer?.div) || null;
|
2017-05-29 05:44:00 +09:00
|
|
|
for (let i = childNodes.length - 1; i >= 0; i--) {
|
2019-12-27 08:22:32 +09:00
|
|
|
const node = childNodes[i];
|
2021-07-23 18:37:38 +09:00
|
|
|
switch (node) {
|
|
|
|
case zoomLayerNode:
|
|
|
|
case annotationLayerNode:
|
2022-06-01 17:38:08 +09:00
|
|
|
case annotationEditorLayerNode:
|
2021-07-23 18:37:38 +09:00
|
|
|
case xfaLayerNode:
|
2022-11-22 01:15:39 +09:00
|
|
|
case textLayerNode:
|
2021-07-23 18:37:38 +09:00
|
|
|
continue;
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
2021-11-16 20:36:22 +09:00
|
|
|
node.remove();
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
|
|
|
div.removeAttribute("data-loaded");
|
|
|
|
|
2021-07-23 18:37:38 +09:00
|
|
|
if (annotationLayerNode) {
|
2017-05-29 05:44:00 +09:00
|
|
|
// Hide the annotation layer until all elements are resized
|
|
|
|
// so they are not displayed on the already resized page.
|
|
|
|
this.annotationLayer.hide();
|
|
|
|
}
|
2022-06-01 17:38:08 +09:00
|
|
|
if (annotationEditorLayerNode) {
|
|
|
|
this.annotationEditorLayer.hide();
|
|
|
|
}
|
2021-07-23 18:37:38 +09:00
|
|
|
if (xfaLayerNode) {
|
|
|
|
// Hide the XFA layer until all elements are resized
|
|
|
|
// so they are not displayed on the already resized page.
|
|
|
|
this.xfaLayer.hide();
|
|
|
|
}
|
2022-11-22 01:15:39 +09:00
|
|
|
if (textLayerNode) {
|
|
|
|
this.textLayer.hide();
|
|
|
|
}
|
2023-02-07 22:38:32 +09:00
|
|
|
this.structTreeLayer?.hide();
|
2022-11-22 01:15:39 +09:00
|
|
|
|
2021-07-23 18:37:38 +09:00
|
|
|
if (!zoomLayerNode) {
|
2017-05-29 05:44:00 +09:00
|
|
|
if (this.canvas) {
|
2023-03-29 04:52:07 +09:00
|
|
|
this.#viewportMap.delete(this.canvas);
|
2017-05-29 05:44:00 +09:00
|
|
|
// Zeroing the width and height causes Firefox to release graphics
|
|
|
|
// resources immediately, which can greatly reduce memory consumption.
|
|
|
|
this.canvas.width = 0;
|
|
|
|
this.canvas.height = 0;
|
|
|
|
delete this.canvas;
|
2014-05-24 10:43:43 +09:00
|
|
|
}
|
2017-05-29 05:44:00 +09:00
|
|
|
this._resetZoomLayer();
|
|
|
|
}
|
|
|
|
}
|
2013-10-03 01:05:46 +09:00
|
|
|
|
2023-04-28 00:03:03 +09:00
|
|
|
/**
|
|
|
|
* @typedef {Object} PDFPageViewUpdateParameters
|
|
|
|
* @property {number} [scale] The new scale, if specified.
|
|
|
|
* @property {number} [rotation] The new rotation, if specified.
|
|
|
|
* @property {Promise<OptionalContentConfig>} [optionalContentConfigPromise]
|
|
|
|
* A promise that is resolved with an {@link OptionalContentConfig}
|
|
|
|
* instance. The default value is `null`.
|
|
|
|
* @property {number} [drawingDelay]
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update e.g. the scale and/or rotation of the page.
|
2023-08-31 19:01:33 +09:00
|
|
|
* @param {PDFPageViewUpdateParameters} params
|
2023-04-28 00:03:03 +09:00
|
|
|
*/
|
2022-12-12 22:24:27 +09:00
|
|
|
update({
|
|
|
|
scale = 0,
|
|
|
|
rotation = null,
|
|
|
|
optionalContentConfigPromise = null,
|
|
|
|
drawingDelay = -1,
|
|
|
|
}) {
|
2017-05-29 05:44:00 +09:00
|
|
|
this.scale = scale || this.scale;
|
2021-09-01 00:08:43 +09:00
|
|
|
if (typeof rotation === "number") {
|
|
|
|
this.rotation = rotation; // The rotation may be zero.
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
[api-minor] Add support for toggling of Optional Content in the viewer (issue 12096)
*Besides, obviously, adding viewer support:* This patch attempts to improve the general API for Optional Content Groups slightly, by adding a couple of new methods for interacting with the (more complex) data structures of `OptionalContentConfig`-instances. (Thus allowing us to mark some of the data as "private", given that it probably shouldn't be manipulated directly.)
By utilizing not just the "raw" Optional Content Groups, but the data from the `/Order` array when available, we can thus display the Layers in a proper tree-structure with collapsible headings for PDF documents that utilizes that feature.
Note that it's possible to reset all Optional Content Groups to their default visibility state, simply by double-clicking on the Layers-button in the sidebar.
(Currently that's indicated in the Layers-button tooltip, which is obviously easy to overlook, however it's probably the best we can do for now without adding more buttons, or even a dropdown-toolbar, to the sidebar.)
Also, the current Layers-button icons are a little rough around the edges, quite literally, but given that the viewer will soon have its UI modernized anyway they hopefully suffice in the meantime.
To give users *full* control of the visibility of the various Optional Content Groups, even those which according to the `/Order` array should not (by default) be toggleable in the UI, this patch will place those under a *custom* heading which:
- Is collapsed by default, and placed at the bottom of the Layers-tree, to be a bit less obtrusive.
- Uses a slightly different formatting, compared to the "regular" headings.
- Is localizable.
Finally, note that the thumbnails are *purposely* always rendered with all Optional Content Groups at their default visibility state, since that seems the most useful and it's also consistent with other viewers.
To ensure that this works as intended, we'll thus disable the `PDFThumbnailView.setImage` functionality when the Optional Content Groups have been changed in the viewer. (This obviously means that we'll re-render thumbnails instead of using the rendered pages. However, this situation ought to be rare enough for this to not really be a problem.)
2020-08-07 04:01:03 +09:00
|
|
|
if (optionalContentConfigPromise instanceof Promise) {
|
|
|
|
this._optionalContentConfigPromise = optionalContentConfigPromise;
|
2022-07-24 20:14:58 +09:00
|
|
|
|
2022-06-27 18:41:37 +09:00
|
|
|
// Ensure that the thumbnails always display the *initial* document state,
|
|
|
|
// for documents with optional content.
|
2022-07-24 20:14:58 +09:00
|
|
|
optionalContentConfigPromise.then(optionalContentConfig => {
|
|
|
|
if (
|
|
|
|
optionalContentConfigPromise !== this._optionalContentConfigPromise
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
2022-06-27 18:41:37 +09:00
|
|
|
this.#useThumbnailCanvas.initialOptionalContent =
|
|
|
|
optionalContentConfig.hasInitialVisibility;
|
2022-07-24 20:14:58 +09:00
|
|
|
});
|
[api-minor] Add support for toggling of Optional Content in the viewer (issue 12096)
*Besides, obviously, adding viewer support:* This patch attempts to improve the general API for Optional Content Groups slightly, by adding a couple of new methods for interacting with the (more complex) data structures of `OptionalContentConfig`-instances. (Thus allowing us to mark some of the data as "private", given that it probably shouldn't be manipulated directly.)
By utilizing not just the "raw" Optional Content Groups, but the data from the `/Order` array when available, we can thus display the Layers in a proper tree-structure with collapsible headings for PDF documents that utilizes that feature.
Note that it's possible to reset all Optional Content Groups to their default visibility state, simply by double-clicking on the Layers-button in the sidebar.
(Currently that's indicated in the Layers-button tooltip, which is obviously easy to overlook, however it's probably the best we can do for now without adding more buttons, or even a dropdown-toolbar, to the sidebar.)
Also, the current Layers-button icons are a little rough around the edges, quite literally, but given that the viewer will soon have its UI modernized anyway they hopefully suffice in the meantime.
To give users *full* control of the visibility of the various Optional Content Groups, even those which according to the `/Order` array should not (by default) be toggleable in the UI, this patch will place those under a *custom* heading which:
- Is collapsed by default, and placed at the bottom of the Layers-tree, to be a bit less obtrusive.
- Uses a slightly different formatting, compared to the "regular" headings.
- Is localizable.
Finally, note that the thumbnails are *purposely* always rendered with all Optional Content Groups at their default visibility state, since that seems the most useful and it's also consistent with other viewers.
To ensure that this works as intended, we'll thus disable the `PDFThumbnailView.setImage` functionality when the Optional Content Groups have been changed in the viewer. (This obviously means that we'll re-render thumbnails instead of using the rendered pages. However, this situation ought to be rare enough for this to not really be a problem.)
2020-08-07 04:01:03 +09:00
|
|
|
}
|
2023-06-02 01:46:50 +09:00
|
|
|
this.#useThumbnailCanvas.directDrawing = true;
|
2017-05-29 05:44:00 +09:00
|
|
|
|
2019-12-27 08:22:32 +09:00
|
|
|
const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
|
2017-05-29 05:44:00 +09:00
|
|
|
this.viewport = this.viewport.clone({
|
2022-04-14 19:43:44 +09:00
|
|
|
scale: this.scale * PixelsPerInch.PDF_TO_CSS_UNITS,
|
2017-05-29 05:44:00 +09:00
|
|
|
rotation: totalRotation,
|
|
|
|
});
|
2022-12-12 22:24:27 +09:00
|
|
|
this.#setDimensions();
|
2017-05-29 05:44:00 +09:00
|
|
|
|
2022-07-24 22:43:31 +09:00
|
|
|
if (
|
2023-03-18 20:09:25 +09:00
|
|
|
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) &&
|
2022-07-24 22:43:31 +09:00
|
|
|
this._isStandalone
|
|
|
|
) {
|
2023-06-27 06:12:37 +09:00
|
|
|
this._container?.style.setProperty("--scale-factor", this.viewport.scale);
|
2021-11-07 02:36:49 +09:00
|
|
|
}
|
|
|
|
|
2023-07-24 17:33:08 +09:00
|
|
|
if (this.canvas) {
|
|
|
|
let onlyCssZoom = false;
|
|
|
|
if (this.#hasRestrictedScaling) {
|
|
|
|
if (
|
|
|
|
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) &&
|
|
|
|
this.maxCanvasPixels === 0
|
|
|
|
) {
|
|
|
|
onlyCssZoom = true;
|
|
|
|
} else if (this.maxCanvasPixels > 0) {
|
|
|
|
const { width, height } = this.viewport;
|
|
|
|
const { sx, sy } = this.outputScale;
|
|
|
|
onlyCssZoom =
|
|
|
|
((Math.floor(width) * sx) | 0) * ((Math.floor(height) * sy) | 0) >
|
|
|
|
this.maxCanvasPixels;
|
|
|
|
}
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
2023-07-24 17:33:08 +09:00
|
|
|
const postponeDrawing =
|
|
|
|
!onlyCssZoom && drawingDelay >= 0 && drawingDelay < 1000;
|
2022-12-12 22:24:27 +09:00
|
|
|
|
2023-03-29 17:31:54 +09:00
|
|
|
if (postponeDrawing || onlyCssZoom) {
|
2022-12-12 22:24:27 +09:00
|
|
|
if (
|
|
|
|
postponeDrawing &&
|
|
|
|
this.renderingState !== RenderingStates.FINISHED
|
|
|
|
) {
|
|
|
|
this.cancelRendering({
|
|
|
|
keepZoomLayer: true,
|
|
|
|
keepAnnotationLayer: true,
|
|
|
|
keepAnnotationEditorLayer: true,
|
|
|
|
keepXfaLayer: true,
|
|
|
|
keepTextLayer: true,
|
|
|
|
cancelExtraDelay: drawingDelay,
|
|
|
|
});
|
|
|
|
// It isn't really finished, but once we have finished
|
|
|
|
// to postpone, we'll call this.reset(...) which will set
|
|
|
|
// the rendering state to INITIAL, hence the next call to
|
|
|
|
// PDFViewer.update() will trigger a redraw (if it's mandatory).
|
|
|
|
this.renderingState = RenderingStates.FINISHED;
|
2023-06-02 01:46:50 +09:00
|
|
|
// Ensure that the thumbnails won't become partially (or fully) blank,
|
|
|
|
// if the sidebar is opened before the actual rendering is done.
|
|
|
|
this.#useThumbnailCanvas.directDrawing = false;
|
2022-12-12 22:24:27 +09:00
|
|
|
}
|
|
|
|
|
2021-07-23 18:37:38 +09:00
|
|
|
this.cssTransform({
|
|
|
|
target: this.canvas,
|
|
|
|
redrawAnnotationLayer: true,
|
2022-06-01 17:38:08 +09:00
|
|
|
redrawAnnotationEditorLayer: true,
|
2021-07-23 18:37:38 +09:00
|
|
|
redrawXfaLayer: true,
|
2022-12-12 22:24:27 +09:00
|
|
|
redrawTextLayer: !postponeDrawing,
|
|
|
|
hideTextLayer: postponeDrawing,
|
2021-07-23 18:37:38 +09:00
|
|
|
});
|
2016-11-19 04:03:49 +09:00
|
|
|
|
2023-06-02 00:44:14 +09:00
|
|
|
if (postponeDrawing) {
|
|
|
|
// The "pagerendered"-event will be dispatched once the actual
|
|
|
|
// rendering is done, hence don't dispatch it here as well.
|
|
|
|
return;
|
|
|
|
}
|
2016-11-19 04:03:49 +09:00
|
|
|
this.eventBus.dispatch("pagerendered", {
|
|
|
|
source: this,
|
|
|
|
pageNumber: this.id,
|
|
|
|
cssTransform: true,
|
2019-07-13 06:08:23 +09:00
|
|
|
timestamp: performance.now(),
|
2023-03-29 04:52:07 +09:00
|
|
|
error: this.#renderError,
|
2016-11-19 04:03:49 +09:00
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2021-02-08 08:21:49 +09:00
|
|
|
if (!this.zoomLayer && !this.canvas.hidden) {
|
2017-05-29 05:44:00 +09:00
|
|
|
this.zoomLayer = this.canvas.parentNode;
|
|
|
|
this.zoomLayer.style.position = "absolute";
|
2014-09-28 03:03:28 +09:00
|
|
|
}
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
|
|
|
if (this.zoomLayer) {
|
2021-07-23 18:37:38 +09:00
|
|
|
this.cssTransform({ target: this.zoomLayer.firstChild });
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
2021-07-23 18:37:38 +09:00
|
|
|
this.reset({
|
|
|
|
keepZoomLayer: true,
|
|
|
|
keepAnnotationLayer: true,
|
2022-06-01 17:38:08 +09:00
|
|
|
keepAnnotationEditorLayer: true,
|
2021-07-23 18:37:38 +09:00
|
|
|
keepXfaLayer: true,
|
2022-11-22 01:15:39 +09:00
|
|
|
keepTextLayer: true,
|
2021-07-23 18:37:38 +09:00
|
|
|
});
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
2014-09-28 03:03:28 +09:00
|
|
|
|
2019-02-15 02:47:32 +09:00
|
|
|
/**
|
|
|
|
* PLEASE NOTE: Most likely you want to use the `this.reset()` method,
|
|
|
|
* rather than calling this one directly.
|
|
|
|
*/
|
2022-06-01 17:38:08 +09:00
|
|
|
cancelRendering({
|
|
|
|
keepAnnotationLayer = false,
|
|
|
|
keepAnnotationEditorLayer = false,
|
|
|
|
keepXfaLayer = false,
|
2022-11-22 01:15:39 +09:00
|
|
|
keepTextLayer = false,
|
2022-12-12 22:24:27 +09:00
|
|
|
cancelExtraDelay = 0,
|
2022-06-01 17:38:08 +09:00
|
|
|
} = {}) {
|
2023-03-29 04:52:07 +09:00
|
|
|
if (this.renderTask) {
|
|
|
|
this.renderTask.cancel(cancelExtraDelay);
|
|
|
|
this.renderTask = null;
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
|
|
|
this.resume = null;
|
2014-09-28 03:03:28 +09:00
|
|
|
|
2022-11-22 01:15:39 +09:00
|
|
|
if (this.textLayer && (!keepTextLayer || !this.textLayer.div)) {
|
2017-05-29 05:44:00 +09:00
|
|
|
this.textLayer.cancel();
|
|
|
|
this.textLayer = null;
|
|
|
|
}
|
2022-12-04 08:27:44 +09:00
|
|
|
if (this.structTreeLayer && !this.textLayer) {
|
|
|
|
this.structTreeLayer = null;
|
|
|
|
}
|
2021-07-23 21:14:42 +09:00
|
|
|
if (
|
|
|
|
this.annotationLayer &&
|
|
|
|
(!keepAnnotationLayer || !this.annotationLayer.div)
|
|
|
|
) {
|
Prevent the `annotationLayer` from, in some cases, becoming duplicated on the first page when the document loads
I don't know if this is a regression, but I noticed earlier today that depending on the initial scale *and* sidebar state, the `annotationLayer` of the first rendered page may end up duplicated; please see screen-shot below.
[screen-shot]
I can reproduce this reliable with e.g. https://arxiv.org/pdf/1112.0542v1.pdf#zoom=page-width&pagemode=bookmarks.
When the document loads, rendering of the first page begins immediately. When the sidebar is then opened, that forces re-rendering which thus aborts rendering of the first page.
Note that calling `PDFPageView.draw()` will always, provided an `AnnotationLayerFactory` instance exists, call `AnnotationLayerBuilder.render()`. Hence the events described above will result in *two* such calls, where the actual annotation rendering/updating happens asynchronously.
For reasons that I don't (at all) understand, when multiple `pdfPage.getAnnotations()` promises are handled back-to-back (in `AnnotationLayerBuilder.render()`), the `this.div` property seems to not update in time for the subsequent calls.
This thus, at least in Firefox, result in double rendering of all annotations on the first page.
Obviously it'd be good to find out why it breaks, since it *really* shouldn't, but this patch at least provides a (hopefully) acceptable work-around by ignoring `getAnnotations()` calls for `AnnotationLayerBuilder` instances that we're destroying (in `PDFPageView.reset()`).
2017-10-07 00:26:54 +09:00
|
|
|
this.annotationLayer.cancel();
|
|
|
|
this.annotationLayer = null;
|
2021-11-07 02:36:49 +09:00
|
|
|
this._annotationCanvasMap = null;
|
Prevent the `annotationLayer` from, in some cases, becoming duplicated on the first page when the document loads
I don't know if this is a regression, but I noticed earlier today that depending on the initial scale *and* sidebar state, the `annotationLayer` of the first rendered page may end up duplicated; please see screen-shot below.
[screen-shot]
I can reproduce this reliable with e.g. https://arxiv.org/pdf/1112.0542v1.pdf#zoom=page-width&pagemode=bookmarks.
When the document loads, rendering of the first page begins immediately. When the sidebar is then opened, that forces re-rendering which thus aborts rendering of the first page.
Note that calling `PDFPageView.draw()` will always, provided an `AnnotationLayerFactory` instance exists, call `AnnotationLayerBuilder.render()`. Hence the events described above will result in *two* such calls, where the actual annotation rendering/updating happens asynchronously.
For reasons that I don't (at all) understand, when multiple `pdfPage.getAnnotations()` promises are handled back-to-back (in `AnnotationLayerBuilder.render()`), the `this.div` property seems to not update in time for the subsequent calls.
This thus, at least in Firefox, result in double rendering of all annotations on the first page.
Obviously it'd be good to find out why it breaks, since it *really* shouldn't, but this patch at least provides a (hopefully) acceptable work-around by ignoring `getAnnotations()` calls for `AnnotationLayerBuilder` instances that we're destroying (in `PDFPageView.reset()`).
2017-10-07 00:26:54 +09:00
|
|
|
}
|
2022-06-01 17:38:08 +09:00
|
|
|
if (
|
|
|
|
this.annotationEditorLayer &&
|
|
|
|
(!keepAnnotationEditorLayer || !this.annotationEditorLayer.div)
|
|
|
|
) {
|
2023-11-23 03:02:42 +09:00
|
|
|
if (this.drawLayer) {
|
|
|
|
this.drawLayer.cancel();
|
|
|
|
this.drawLayer = null;
|
|
|
|
}
|
2022-06-01 17:38:08 +09:00
|
|
|
this.annotationEditorLayer.cancel();
|
|
|
|
this.annotationEditorLayer = null;
|
|
|
|
}
|
2021-07-23 21:14:42 +09:00
|
|
|
if (this.xfaLayer && (!keepXfaLayer || !this.xfaLayer.div)) {
|
2021-07-23 18:37:38 +09:00
|
|
|
this.xfaLayer.cancel();
|
|
|
|
this.xfaLayer = null;
|
2022-12-14 21:19:41 +09:00
|
|
|
this._textHighlighter?.disable();
|
2021-07-23 18:37:38 +09:00
|
|
|
}
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
2016-07-30 03:51:37 +09:00
|
|
|
|
2021-07-23 18:37:38 +09:00
|
|
|
cssTransform({
|
|
|
|
target,
|
|
|
|
redrawAnnotationLayer = false,
|
2022-06-01 17:38:08 +09:00
|
|
|
redrawAnnotationEditorLayer = false,
|
2021-07-23 18:37:38 +09:00
|
|
|
redrawXfaLayer = false,
|
2022-11-22 01:15:39 +09:00
|
|
|
redrawTextLayer = false,
|
2022-12-12 22:24:27 +09:00
|
|
|
hideTextLayer = false,
|
2021-07-23 18:37:38 +09:00
|
|
|
}) {
|
2023-03-29 04:51:44 +09:00
|
|
|
// Scale target (canvas), its wrapper and page container.
|
|
|
|
if (
|
2023-03-18 20:09:25 +09:00
|
|
|
(typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) &&
|
2023-03-29 04:51:44 +09:00
|
|
|
!(target instanceof HTMLCanvasElement)
|
|
|
|
) {
|
|
|
|
throw new Error("Expected `target` to be a canvas.");
|
|
|
|
}
|
|
|
|
if (!target.hasAttribute("zooming")) {
|
|
|
|
target.setAttribute("zooming", true);
|
|
|
|
const { style } = target;
|
|
|
|
style.width = style.height = "";
|
2022-12-12 22:24:27 +09:00
|
|
|
}
|
|
|
|
|
2023-03-29 04:52:07 +09:00
|
|
|
const originalViewport = this.#viewportMap.get(target);
|
2022-12-12 22:24:27 +09:00
|
|
|
if (this.viewport !== originalViewport) {
|
|
|
|
// The canvas may have been originally rotated; rotate relative to that.
|
|
|
|
const relativeRotation =
|
|
|
|
this.viewport.rotation - originalViewport.rotation;
|
|
|
|
const absRotation = Math.abs(relativeRotation);
|
|
|
|
let scaleX = 1,
|
|
|
|
scaleY = 1;
|
|
|
|
if (absRotation === 90 || absRotation === 270) {
|
|
|
|
const { width, height } = this.viewport;
|
|
|
|
// Scale x and y because of the rotation.
|
|
|
|
scaleX = height / width;
|
|
|
|
scaleY = width / height;
|
|
|
|
}
|
2023-03-29 17:04:20 +09:00
|
|
|
target.style.transform = `rotate(${relativeRotation}deg) scale(${scaleX}, ${scaleY})`;
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
|
|
|
|
2021-07-23 18:37:38 +09:00
|
|
|
if (redrawAnnotationLayer && this.annotationLayer) {
|
2022-12-14 21:12:49 +09:00
|
|
|
this.#renderAnnotationLayer();
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
2022-06-01 17:38:08 +09:00
|
|
|
if (redrawAnnotationEditorLayer && this.annotationEditorLayer) {
|
2023-11-23 03:02:42 +09:00
|
|
|
if (this.drawLayer) {
|
|
|
|
this.#renderDrawLayer();
|
|
|
|
}
|
2022-12-14 21:12:49 +09:00
|
|
|
this.#renderAnnotationEditorLayer();
|
2022-06-01 17:38:08 +09:00
|
|
|
}
|
2021-07-23 18:37:38 +09:00
|
|
|
if (redrawXfaLayer && this.xfaLayer) {
|
2022-12-14 21:12:49 +09:00
|
|
|
this.#renderXfaLayer();
|
2021-03-19 18:11:40 +09:00
|
|
|
}
|
2022-12-12 22:24:27 +09:00
|
|
|
|
|
|
|
if (this.textLayer) {
|
|
|
|
if (hideTextLayer) {
|
|
|
|
this.textLayer.hide();
|
2023-02-07 22:38:32 +09:00
|
|
|
this.structTreeLayer?.hide();
|
2022-12-12 22:24:27 +09:00
|
|
|
} else if (redrawTextLayer) {
|
|
|
|
this.#renderTextLayer();
|
|
|
|
}
|
2022-11-22 01:15:39 +09:00
|
|
|
}
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
2014-09-28 03:03:28 +09:00
|
|
|
|
2017-05-29 05:44:00 +09:00
|
|
|
get width() {
|
|
|
|
return this.viewport.width;
|
|
|
|
}
|
|
|
|
|
|
|
|
get height() {
|
|
|
|
return this.viewport.height;
|
|
|
|
}
|
2014-09-28 03:03:28 +09:00
|
|
|
|
2017-05-29 05:44:00 +09:00
|
|
|
getPagePoint(x, y) {
|
|
|
|
return this.viewport.convertToPdfPoint(x, y);
|
|
|
|
}
|
2014-09-28 03:03:28 +09:00
|
|
|
|
2023-03-29 04:52:07 +09:00
|
|
|
async #finishRenderTask(renderTask, error = null) {
|
|
|
|
// The renderTask may have been replaced by a new one, so only remove
|
|
|
|
// the reference to the renderTask if it matches the one that is
|
2023-03-29 04:51:57 +09:00
|
|
|
// triggering this callback.
|
2023-03-29 04:52:07 +09:00
|
|
|
if (renderTask === this.renderTask) {
|
|
|
|
this.renderTask = null;
|
2023-03-29 04:51:57 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
if (error instanceof RenderingCancelledException) {
|
2023-03-29 04:52:07 +09:00
|
|
|
this.#renderError = null;
|
2023-03-29 04:51:57 +09:00
|
|
|
return;
|
|
|
|
}
|
2023-03-29 04:52:07 +09:00
|
|
|
this.#renderError = error;
|
2023-03-29 04:51:57 +09:00
|
|
|
|
|
|
|
this.renderingState = RenderingStates.FINISHED;
|
|
|
|
this._resetZoomLayer(/* removeFromDOM = */ true);
|
|
|
|
|
|
|
|
// Ensure that the thumbnails won't become partially (or fully) blank,
|
|
|
|
// for documents that contain interactive form elements.
|
2023-03-29 04:52:07 +09:00
|
|
|
this.#useThumbnailCanvas.regularAnnotations = !renderTask.separateAnnots;
|
2023-03-29 04:51:57 +09:00
|
|
|
|
|
|
|
this.eventBus.dispatch("pagerendered", {
|
|
|
|
source: this,
|
|
|
|
pageNumber: this.id,
|
|
|
|
cssTransform: false,
|
|
|
|
timestamp: performance.now(),
|
2023-03-29 04:52:07 +09:00
|
|
|
error: this.#renderError,
|
2023-03-29 04:51:57 +09:00
|
|
|
});
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async draw() {
|
2017-05-29 05:44:00 +09:00
|
|
|
if (this.renderingState !== RenderingStates.INITIAL) {
|
|
|
|
console.error("Must be in new state before drawing");
|
|
|
|
this.reset(); // Ensure that we reset all state to prevent issues.
|
|
|
|
}
|
2023-03-29 04:52:07 +09:00
|
|
|
const { div, l10n, pageColors, pdfPage, viewport } = this;
|
2017-05-29 05:44:00 +09:00
|
|
|
|
2020-02-09 01:43:53 +09:00
|
|
|
if (!pdfPage) {
|
2017-08-04 07:24:19 +09:00
|
|
|
this.renderingState = RenderingStates.FINISHED;
|
2023-03-29 04:51:57 +09:00
|
|
|
throw new Error("pdfPage is not loaded");
|
2017-08-04 07:24:19 +09:00
|
|
|
}
|
|
|
|
|
2017-05-29 05:44:00 +09:00
|
|
|
this.renderingState = RenderingStates.RUNNING;
|
|
|
|
|
|
|
|
// Wrap the canvas so that if it has a CSS transform for high DPI the
|
|
|
|
// overflow will be hidden in Firefox.
|
2019-12-27 08:22:32 +09:00
|
|
|
const canvasWrapper = document.createElement("div");
|
2017-05-29 05:44:00 +09:00
|
|
|
canvasWrapper.classList.add("canvasWrapper");
|
2022-12-23 05:55:25 +09:00
|
|
|
div.append(canvasWrapper);
|
2014-09-28 03:03:28 +09:00
|
|
|
|
2022-11-22 01:15:39 +09:00
|
|
|
if (
|
|
|
|
!this.textLayer &&
|
2023-04-22 20:07:07 +09:00
|
|
|
this.#textLayerMode !== TextLayerMode.DISABLE &&
|
[api-minor] Remove the `textLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `textLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 08:09:14 +09:00
|
|
|
!pdfPage.isPureXfa
|
2022-11-22 01:15:39 +09:00
|
|
|
) {
|
|
|
|
this._accessibilityManager ||= new TextAccessibilityManager();
|
|
|
|
|
[api-minor] Remove the `textLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `textLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 08:09:14 +09:00
|
|
|
this.textLayer = new TextLayerBuilder({
|
2022-12-14 21:19:41 +09:00
|
|
|
highlighter: this._textHighlighter,
|
2022-07-29 00:59:03 +09:00
|
|
|
accessibilityManager: this._accessibilityManager,
|
2023-04-22 20:07:07 +09:00
|
|
|
enablePermissions:
|
|
|
|
this.#textLayerMode === TextLayerMode.ENABLE_PERMISSIONS,
|
2022-07-29 21:47:57 +09:00
|
|
|
});
|
2023-10-27 21:55:12 +09:00
|
|
|
this.textLayer.onAppend = textLayerDiv => {
|
|
|
|
// Pause translation when inserting the textLayer in the DOM.
|
|
|
|
this.l10n.pause();
|
|
|
|
this.div.append(textLayerDiv);
|
|
|
|
this.l10n.resume();
|
|
|
|
};
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
|
|
|
|
2021-11-07 02:36:49 +09:00
|
|
|
if (
|
[api-minor] Remove the `annotationLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `annotationLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 07:34:55 +09:00
|
|
|
!this.annotationLayer &&
|
|
|
|
this.#annotationMode !== AnnotationMode.DISABLE
|
2021-11-07 02:36:49 +09:00
|
|
|
) {
|
[api-minor] Remove the `annotationLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `annotationLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 07:34:55 +09:00
|
|
|
const {
|
|
|
|
annotationStorage,
|
|
|
|
downloadManager,
|
|
|
|
enableScripting,
|
|
|
|
fieldObjectsPromise,
|
|
|
|
hasJSActionsPromise,
|
|
|
|
linkService,
|
2023-09-25 17:56:12 +09:00
|
|
|
} = this.#layerProperties;
|
[api-minor] Remove the `annotationLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `annotationLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 07:34:55 +09:00
|
|
|
|
2021-11-07 02:36:49 +09:00
|
|
|
this._annotationCanvasMap ||= new Map();
|
[api-minor] Remove the `annotationLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `annotationLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 07:34:55 +09:00
|
|
|
this.annotationLayer = new AnnotationLayerBuilder({
|
|
|
|
pageDiv: div,
|
|
|
|
pdfPage,
|
|
|
|
annotationStorage,
|
|
|
|
imageResourcesPath: this.imageResourcesPath,
|
|
|
|
renderForms: this.#annotationMode === AnnotationMode.ENABLE_FORMS,
|
|
|
|
linkService,
|
|
|
|
downloadManager,
|
|
|
|
enableScripting,
|
|
|
|
hasJSActionsPromise,
|
|
|
|
fieldObjectsPromise,
|
|
|
|
annotationCanvasMap: this._annotationCanvasMap,
|
|
|
|
accessibilityManager: this._accessibilityManager,
|
|
|
|
});
|
2021-11-07 02:36:49 +09:00
|
|
|
}
|
|
|
|
|
2023-03-29 04:52:07 +09:00
|
|
|
const renderContinueCallback = cont => {
|
|
|
|
showCanvas?.(false);
|
|
|
|
if (this.renderingQueue && !this.renderingQueue.isHighestPriority(this)) {
|
|
|
|
this.renderingState = RenderingStates.PAUSED;
|
|
|
|
this.resume = () => {
|
|
|
|
this.renderingState = RenderingStates.RUNNING;
|
|
|
|
cont();
|
|
|
|
};
|
|
|
|
return;
|
[api-minor] Remove the `xfaLayerFactory` in the viewer
Please note that this functionality has never really mattered for the Firefox PDF Viewer, the GENERIC viewer, or even the "simpleviewer"/"singlepageviewer" component-examples. Hence, in practice this means that only the "pageviewer" component-example[1] have ever really utilized this.
Using factories to initialize various layers in the viewer, rather than simply invoking the relevant code directly, seems (at least to me) like a somewhat roundabout way of doing things.
Not only does this lead to more code, both to write and maintain, but since many of the layers have common parameters (e.g. an `AnnotationStorage`-instance) there's also some duplication.
Hence this patch, which removes the `xfaLayerFactory` and instead uses a lookup-function in the `PDFPageView`-class to access the external viewer-properties as necessary.
Note that this should even be an improvement for the "pageviewer" component-example, since most layers will now work by default rather than require manual configuration.
---
[1] In practice we generally suggest using the "simpleviewer", or "singlepageviewer", since it does *most* things out-of-the-box and given that a lot of functionality really require *a viewer* and not just a single page in order to work.
2022-12-07 08:13:44 +09:00
|
|
|
}
|
2023-03-29 04:52:07 +09:00
|
|
|
cont();
|
2017-05-29 05:44:00 +09:00
|
|
|
};
|
|
|
|
|
2022-12-12 22:24:27 +09:00
|
|
|
const { width, height } = viewport;
|
2019-12-27 08:22:32 +09:00
|
|
|
const canvas = document.createElement("canvas");
|
2022-06-08 01:56:30 +09:00
|
|
|
canvas.setAttribute("role", "presentation");
|
2017-05-29 05:44:00 +09:00
|
|
|
|
|
|
|
// Keep the canvas hidden until the first draw callback, or until drawing
|
|
|
|
// is complete when `!this.renderingQueue`, to prevent black flickering.
|
2021-02-08 08:21:49 +09:00
|
|
|
canvas.hidden = true;
|
2023-03-29 04:52:07 +09:00
|
|
|
const hasHCM = !!(pageColors?.background && pageColors?.foreground);
|
|
|
|
|
2023-03-31 17:02:17 +09:00
|
|
|
let showCanvas = isLastShow => {
|
2023-03-07 03:09:56 +09:00
|
|
|
// In HCM, a final filter is applied on the canvas which means that
|
|
|
|
// before it's applied we've normal colors. Consequently, to avoid to have
|
|
|
|
// a final flash we just display it once all the drawing is done.
|
2023-03-31 17:02:17 +09:00
|
|
|
if (!hasHCM || isLastShow) {
|
2021-02-08 08:21:49 +09:00
|
|
|
canvas.hidden = false;
|
2023-03-31 17:02:17 +09:00
|
|
|
showCanvas = null; // Only invoke the function once.
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
|
|
|
};
|
2022-06-12 19:20:25 +09:00
|
|
|
canvasWrapper.append(canvas);
|
2017-05-29 05:44:00 +09:00
|
|
|
this.canvas = canvas;
|
|
|
|
|
2019-12-27 08:22:32 +09:00
|
|
|
const ctx = canvas.getContext("2d", { alpha: false });
|
2022-02-19 00:38:25 +09:00
|
|
|
const outputScale = (this.outputScale = new OutputScale());
|
2017-05-29 05:44:00 +09:00
|
|
|
|
2023-07-24 17:33:08 +09:00
|
|
|
if (
|
|
|
|
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) &&
|
|
|
|
this.maxCanvasPixels === 0
|
|
|
|
) {
|
|
|
|
const invScale = 1 / this.scale;
|
2017-05-29 05:44:00 +09:00
|
|
|
// Use a scale that makes the canvas have the originally intended size
|
|
|
|
// of the page.
|
2023-07-24 17:33:08 +09:00
|
|
|
outputScale.sx *= invScale;
|
|
|
|
outputScale.sy *= invScale;
|
|
|
|
this.#hasRestrictedScaling = true;
|
|
|
|
} else if (this.maxCanvasPixels > 0) {
|
2022-12-12 22:24:27 +09:00
|
|
|
const pixelsInViewport = width * height;
|
2019-12-27 08:22:32 +09:00
|
|
|
const maxScale = Math.sqrt(this.maxCanvasPixels / pixelsInViewport);
|
2017-05-29 05:44:00 +09:00
|
|
|
if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
|
|
|
|
outputScale.sx = maxScale;
|
|
|
|
outputScale.sy = maxScale;
|
2023-07-24 17:33:08 +09:00
|
|
|
this.#hasRestrictedScaling = true;
|
2017-05-29 05:44:00 +09:00
|
|
|
} else {
|
2023-07-24 17:33:08 +09:00
|
|
|
this.#hasRestrictedScaling = false;
|
2016-10-19 23:16:57 +09:00
|
|
|
}
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
2019-12-27 08:22:32 +09:00
|
|
|
const sfx = approximateFraction(outputScale.sx);
|
|
|
|
const sfy = approximateFraction(outputScale.sy);
|
2022-11-22 01:15:39 +09:00
|
|
|
|
2023-03-29 04:52:07 +09:00
|
|
|
canvas.width = roundToDivide(width * outputScale.sx, sfx[0]);
|
|
|
|
canvas.height = roundToDivide(height * outputScale.sy, sfy[0]);
|
2022-11-22 01:15:39 +09:00
|
|
|
const { style } = canvas;
|
2023-03-29 04:52:07 +09:00
|
|
|
style.width = roundToDivide(width, sfx[1]) + "px";
|
|
|
|
style.height = roundToDivide(height, sfy[1]) + "px";
|
2021-11-07 02:36:49 +09:00
|
|
|
|
2017-05-29 05:44:00 +09:00
|
|
|
// Add the viewport so it's known what it was originally drawn with.
|
2023-03-29 04:52:07 +09:00
|
|
|
this.#viewportMap.set(canvas, viewport);
|
2017-05-29 05:44:00 +09:00
|
|
|
|
|
|
|
// Rendering area
|
2022-02-18 06:21:59 +09:00
|
|
|
const transform = outputScale.scaled
|
|
|
|
? [outputScale.sx, 0, 0, outputScale.sy, 0, 0]
|
|
|
|
: null;
|
2019-12-27 08:22:32 +09:00
|
|
|
const renderContext = {
|
2017-05-29 05:44:00 +09:00
|
|
|
canvasContext: ctx,
|
|
|
|
transform,
|
2022-12-12 22:24:27 +09:00
|
|
|
viewport,
|
2021-12-12 00:53:59 +09:00
|
|
|
annotationMode: this.#annotationMode,
|
[api-minor] Add support for toggling of Optional Content in the viewer (issue 12096)
*Besides, obviously, adding viewer support:* This patch attempts to improve the general API for Optional Content Groups slightly, by adding a couple of new methods for interacting with the (more complex) data structures of `OptionalContentConfig`-instances. (Thus allowing us to mark some of the data as "private", given that it probably shouldn't be manipulated directly.)
By utilizing not just the "raw" Optional Content Groups, but the data from the `/Order` array when available, we can thus display the Layers in a proper tree-structure with collapsible headings for PDF documents that utilizes that feature.
Note that it's possible to reset all Optional Content Groups to their default visibility state, simply by double-clicking on the Layers-button in the sidebar.
(Currently that's indicated in the Layers-button tooltip, which is obviously easy to overlook, however it's probably the best we can do for now without adding more buttons, or even a dropdown-toolbar, to the sidebar.)
Also, the current Layers-button icons are a little rough around the edges, quite literally, but given that the viewer will soon have its UI modernized anyway they hopefully suffice in the meantime.
To give users *full* control of the visibility of the various Optional Content Groups, even those which according to the `/Order` array should not (by default) be toggleable in the UI, this patch will place those under a *custom* heading which:
- Is collapsed by default, and placed at the bottom of the Layers-tree, to be a bit less obtrusive.
- Uses a slightly different formatting, compared to the "regular" headings.
- Is localizable.
Finally, note that the thumbnails are *purposely* always rendered with all Optional Content Groups at their default visibility state, since that seems the most useful and it's also consistent with other viewers.
To ensure that this works as intended, we'll thus disable the `PDFThumbnailView.setImage` functionality when the Optional Content Groups have been changed in the viewer. (This obviously means that we'll re-render thumbnails instead of using the rendered pages. However, this situation ought to be rare enough for this to not really be a problem.)
2020-08-07 04:01:03 +09:00
|
|
|
optionalContentConfigPromise: this._optionalContentConfigPromise,
|
2021-11-07 02:36:49 +09:00
|
|
|
annotationCanvasMap: this._annotationCanvasMap,
|
2023-03-29 04:52:07 +09:00
|
|
|
pageColors,
|
2017-05-29 05:44:00 +09:00
|
|
|
};
|
2023-03-29 04:52:07 +09:00
|
|
|
const renderTask = (this.renderTask = this.pdfPage.render(renderContext));
|
|
|
|
renderTask.onContinue = renderContinueCallback;
|
2017-05-29 05:44:00 +09:00
|
|
|
|
2023-03-29 04:52:07 +09:00
|
|
|
const resultPromise = renderTask.promise.then(
|
|
|
|
async () => {
|
2023-03-31 17:02:17 +09:00
|
|
|
showCanvas?.(true);
|
2023-03-29 04:52:07 +09:00
|
|
|
await this.#finishRenderTask(renderTask);
|
|
|
|
|
|
|
|
this.#renderTextLayer();
|
|
|
|
|
|
|
|
if (this.annotationLayer) {
|
|
|
|
await this.#renderAnnotationLayer();
|
|
|
|
}
|
|
|
|
|
2023-11-23 03:02:42 +09:00
|
|
|
const { annotationEditorUIManager } = this.#layerProperties;
|
2023-03-29 04:52:07 +09:00
|
|
|
|
2023-11-23 03:02:42 +09:00
|
|
|
if (!annotationEditorUIManager) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.drawLayer ||= new DrawLayerBuilder({
|
|
|
|
pageIndex: this.id,
|
|
|
|
});
|
|
|
|
await this.#renderDrawLayer();
|
|
|
|
this.drawLayer.setParent(canvasWrapper);
|
|
|
|
|
|
|
|
if (!this.annotationEditorLayer) {
|
2023-03-29 04:52:07 +09:00
|
|
|
this.annotationEditorLayer = new AnnotationEditorLayerBuilder({
|
|
|
|
uiManager: annotationEditorUIManager,
|
|
|
|
pageDiv: div,
|
|
|
|
pdfPage,
|
|
|
|
l10n,
|
|
|
|
accessibilityManager: this._accessibilityManager,
|
2023-06-05 18:32:44 +09:00
|
|
|
annotationLayer: this.annotationLayer?.annotationLayer,
|
2023-11-23 03:02:42 +09:00
|
|
|
textLayer: this.textLayer,
|
|
|
|
drawLayer: this.drawLayer.getDrawLayer(),
|
2023-03-29 04:52:07 +09:00
|
|
|
});
|
|
|
|
}
|
|
|
|
this.#renderAnnotationEditorLayer();
|
2017-05-29 05:44:00 +09:00
|
|
|
},
|
2023-03-29 04:52:07 +09:00
|
|
|
error => {
|
2023-01-15 07:15:15 +09:00
|
|
|
// When zooming with a `drawingDelay` set, avoid temporarily showing
|
|
|
|
// a black canvas if rendering was cancelled before the `onContinue`-
|
|
|
|
// callback had been invoked at least once.
|
|
|
|
if (!(error instanceof RenderingCancelledException)) {
|
2023-03-31 17:02:17 +09:00
|
|
|
showCanvas?.(true);
|
2023-01-15 07:15:15 +09:00
|
|
|
}
|
2023-03-29 04:52:07 +09:00
|
|
|
return this.#finishRenderTask(renderTask, error);
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
|
|
|
);
|
2023-03-29 04:52:07 +09:00
|
|
|
|
|
|
|
if (pdfPage.isPureXfa) {
|
|
|
|
if (!this.xfaLayer) {
|
2023-09-25 17:56:12 +09:00
|
|
|
const { annotationStorage, linkService } = this.#layerProperties;
|
2023-03-29 04:52:07 +09:00
|
|
|
|
|
|
|
this.xfaLayer = new XfaLayerBuilder({
|
|
|
|
pdfPage,
|
|
|
|
annotationStorage,
|
|
|
|
linkService,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
this.#renderXfaLayer();
|
|
|
|
}
|
|
|
|
|
|
|
|
div.setAttribute("data-loaded", true);
|
|
|
|
|
|
|
|
this.eventBus.dispatch("pagerender", {
|
|
|
|
source: this,
|
|
|
|
pageNumber: this.id,
|
|
|
|
});
|
|
|
|
return resultPromise;
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
2016-10-19 23:16:57 +09:00
|
|
|
|
2017-05-29 05:44:00 +09:00
|
|
|
/**
|
|
|
|
* @param {string|null} label
|
|
|
|
*/
|
|
|
|
setPageLabel(label) {
|
|
|
|
this.pageLabel = typeof label === "string" ? label : null;
|
|
|
|
|
2023-10-23 20:42:06 +09:00
|
|
|
this.div.setAttribute(
|
|
|
|
"data-l10n-args",
|
|
|
|
JSON.stringify({ page: this.pageLabel ?? this.id })
|
|
|
|
);
|
|
|
|
|
2017-05-29 05:44:00 +09:00
|
|
|
if (this.pageLabel !== null) {
|
|
|
|
this.div.setAttribute("data-page-label", this.pageLabel);
|
|
|
|
} else {
|
|
|
|
this.div.removeAttribute("data-page-label");
|
|
|
|
}
|
|
|
|
}
|
2022-07-24 20:14:58 +09:00
|
|
|
|
|
|
|
/**
|
|
|
|
* For use by the `PDFThumbnailView.setImage`-method.
|
|
|
|
* @ignore
|
|
|
|
*/
|
|
|
|
get thumbnailCanvas() {
|
2023-06-02 01:46:50 +09:00
|
|
|
const { directDrawing, initialOptionalContent, regularAnnotations } =
|
2022-06-27 18:41:37 +09:00
|
|
|
this.#useThumbnailCanvas;
|
2023-06-02 01:46:50 +09:00
|
|
|
return directDrawing && initialOptionalContent && regularAnnotations
|
|
|
|
? this.canvas
|
|
|
|
: null;
|
2022-07-24 20:14:58 +09:00
|
|
|
}
|
2017-05-29 05:44:00 +09:00
|
|
|
}
|
2016-04-09 02:34:27 +09:00
|
|
|
|
2017-03-28 08:07:27 +09:00
|
|
|
export { PDFPageView };
|