annotation_layer_builder.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /* Copyright 2014 Mozilla Foundation
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. /** @typedef {import("../src/display/api").PDFPageProxy} PDFPageProxy */
  16. // eslint-disable-next-line max-len
  17. /** @typedef {import("../src/display/display_utils").PageViewport} PageViewport */
  18. /** @typedef {import("./interfaces").IDownloadManager} IDownloadManager */
  19. /** @typedef {import("./interfaces").IL10n} IL10n */
  20. /** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
  21. // eslint-disable-next-line max-len
  22. /** @typedef {import("./textaccessibility.js").TextAccessibilityManager} TextAccessibilityManager */
  23. import { AnnotationLayer } from "pdfjs-lib";
  24. import { NullL10n } from "./l10n_utils.js";
  25. import { PresentationModeState } from "./ui_utils.js";
  26. /**
  27. * @typedef {Object} AnnotationLayerBuilderOptions
  28. * @property {HTMLDivElement} pageDiv
  29. * @property {PDFPageProxy} pdfPage
  30. * @property {AnnotationStorage} [annotationStorage]
  31. * @property {string} [imageResourcesPath] - Path for image resources, mainly
  32. * for annotation icons. Include trailing slash.
  33. * @property {boolean} renderForms
  34. * @property {IPDFLinkService} linkService
  35. * @property {IDownloadManager} downloadManager
  36. * @property {IL10n} l10n - Localization service.
  37. * @property {boolean} [enableScripting]
  38. * @property {Promise<boolean>} [hasJSActionsPromise]
  39. * @property {Promise<Object<string, Array<Object>> | null>}
  40. * [fieldObjectsPromise]
  41. * @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap]
  42. * @property {TextAccessibilityManager} [accessibilityManager]
  43. */
  44. class AnnotationLayerBuilder {
  45. #numAnnotations = 0;
  46. #onPresentationModeChanged = null;
  47. /**
  48. * @param {AnnotationLayerBuilderOptions} options
  49. */
  50. constructor({
  51. pageDiv,
  52. pdfPage,
  53. linkService,
  54. downloadManager,
  55. annotationStorage = null,
  56. imageResourcesPath = "",
  57. renderForms = true,
  58. l10n = NullL10n,
  59. enableScripting = false,
  60. hasJSActionsPromise = null,
  61. fieldObjectsPromise = null,
  62. annotationCanvasMap = null,
  63. accessibilityManager = null,
  64. }) {
  65. this.pageDiv = pageDiv;
  66. this.pdfPage = pdfPage;
  67. this.linkService = linkService;
  68. this.downloadManager = downloadManager;
  69. this.imageResourcesPath = imageResourcesPath;
  70. this.renderForms = renderForms;
  71. this.l10n = l10n;
  72. this.annotationStorage = annotationStorage;
  73. this.enableScripting = enableScripting;
  74. this._hasJSActionsPromise = hasJSActionsPromise || Promise.resolve(false);
  75. this._fieldObjectsPromise = fieldObjectsPromise || Promise.resolve(null);
  76. this._annotationCanvasMap = annotationCanvasMap;
  77. this._accessibilityManager = accessibilityManager;
  78. this.div = null;
  79. this._cancelled = false;
  80. this._eventBus = linkService.eventBus;
  81. }
  82. /**
  83. * @param {PageViewport} viewport
  84. * @param {string} intent (default value is 'display')
  85. * @returns {Promise<void>} A promise that is resolved when rendering of the
  86. * annotations is complete.
  87. */
  88. async render(viewport, intent = "display") {
  89. if (this.div) {
  90. if (this._cancelled || this.#numAnnotations === 0) {
  91. return;
  92. }
  93. // If an annotationLayer already exists, refresh its children's
  94. // transformation matrices.
  95. AnnotationLayer.update({
  96. viewport: viewport.clone({ dontFlip: true }),
  97. div: this.div,
  98. annotationCanvasMap: this._annotationCanvasMap,
  99. });
  100. return;
  101. }
  102. const [annotations, hasJSActions, fieldObjects] = await Promise.all([
  103. this.pdfPage.getAnnotations({ intent }),
  104. this._hasJSActionsPromise,
  105. this._fieldObjectsPromise,
  106. ]);
  107. if (this._cancelled) {
  108. return;
  109. }
  110. this.#numAnnotations = annotations.length;
  111. // Create an annotation layer div and render the annotations
  112. // if there is at least one annotation.
  113. this.div = document.createElement("div");
  114. this.div.className = "annotationLayer";
  115. this.pageDiv.append(this.div);
  116. if (this.#numAnnotations === 0) {
  117. this.hide();
  118. return;
  119. }
  120. AnnotationLayer.render({
  121. viewport: viewport.clone({ dontFlip: true }),
  122. div: this.div,
  123. annotations,
  124. page: this.pdfPage,
  125. imageResourcesPath: this.imageResourcesPath,
  126. renderForms: this.renderForms,
  127. linkService: this.linkService,
  128. downloadManager: this.downloadManager,
  129. annotationStorage: this.annotationStorage,
  130. enableScripting: this.enableScripting,
  131. hasJSActions,
  132. fieldObjects,
  133. annotationCanvasMap: this._annotationCanvasMap,
  134. accessibilityManager: this._accessibilityManager,
  135. });
  136. this.l10n.translate(this.div);
  137. // Ensure that interactive form elements in the annotationLayer are
  138. // disabled while PresentationMode is active (see issue 12232).
  139. if (this.linkService.isInPresentationMode) {
  140. this.#updatePresentationModeState(PresentationModeState.FULLSCREEN);
  141. }
  142. if (!this.#onPresentationModeChanged) {
  143. this.#onPresentationModeChanged = evt => {
  144. this.#updatePresentationModeState(evt.state);
  145. };
  146. this._eventBus?._on(
  147. "presentationmodechanged",
  148. this.#onPresentationModeChanged
  149. );
  150. }
  151. }
  152. cancel() {
  153. this._cancelled = true;
  154. if (this.#onPresentationModeChanged) {
  155. this._eventBus?._off(
  156. "presentationmodechanged",
  157. this.#onPresentationModeChanged
  158. );
  159. this.#onPresentationModeChanged = null;
  160. }
  161. }
  162. hide() {
  163. if (!this.div) {
  164. return;
  165. }
  166. this.div.hidden = true;
  167. }
  168. #updatePresentationModeState(state) {
  169. if (!this.div) {
  170. return;
  171. }
  172. let disableFormElements = false;
  173. switch (state) {
  174. case PresentationModeState.FULLSCREEN:
  175. disableFormElements = true;
  176. break;
  177. case PresentationModeState.NORMAL:
  178. break;
  179. default:
  180. return;
  181. }
  182. for (const section of this.div.childNodes) {
  183. if (section.hasAttribute("data-internal-link")) {
  184. continue;
  185. }
  186. section.inert = disableFormElements;
  187. }
  188. }
  189. }
  190. export { AnnotationLayerBuilder };