pdf-js-wrapper.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // NOTE: using "legacy" build as main build requires webpack v5
  2. // import PDFJS from 'pdfjs-dist/webpack'
  3. import * as PDFJS from 'pdfjs-dist/legacy/build/pdf'
  4. import * as PDFJSViewer from 'pdfjs-dist/legacy/web/pdf_viewer'
  5. import PDFJSWorker from 'pdfjs-dist/legacy/build/pdf.worker'
  6. import 'pdfjs-dist/legacy/web/pdf_viewer.css'
  7. import getMeta from '../../../utils/meta'
  8. import { captureMessage } from '../../../infrastructure/error-reporter'
  9. if (typeof window !== 'undefined' && 'Worker' in window) {
  10. PDFJS.GlobalWorkerOptions.workerPort = new PDFJSWorker()
  11. }
  12. const params = new URLSearchParams(window.location.search)
  13. const disableFontFace = params.get('disable-font-face') === 'true'
  14. const cMapUrl = getMeta('ol-pdfCMapsPath')
  15. const imageResourcesPath = getMeta('ol-pdfImageResourcesPath')
  16. const rangeChunkSize = 128 * 1024 // 128K chunks
  17. export default class PDFJSWrapper {
  18. constructor(container) {
  19. this.container = container
  20. // create the event bus
  21. const eventBus = new PDFJSViewer.EventBus()
  22. // create the link service
  23. const linkService = new PDFJSViewer.PDFLinkService({
  24. eventBus,
  25. externalLinkTarget: 2,
  26. externalLinkRel: 'noopener',
  27. })
  28. // create the localization
  29. // const l10n = new PDFJSViewer.GenericL10n('en-GB') // TODO: locale mapping?
  30. // create the viewer
  31. const viewer = new PDFJSViewer.PDFViewer({
  32. container,
  33. eventBus,
  34. imageResourcesPath,
  35. linkService,
  36. // l10n, // commented out since it currently breaks `aria-label` rendering in pdf pages
  37. enableScripting: false, // default is false, but set explicitly to be sure
  38. enableXfa: false, // default is false (2021-10-12), but set explicitly to be sure
  39. renderInteractiveForms: false,
  40. })
  41. linkService.setViewer(viewer)
  42. this.eventBus = eventBus
  43. this.linkService = linkService
  44. this.viewer = viewer
  45. }
  46. // load a document from a URL
  47. loadDocument(url) {
  48. // prevents any previous loading task from populating the viewer
  49. this.loadDocumentTask = undefined
  50. return new Promise((resolve, reject) => {
  51. this.loadDocumentTask = PDFJS.getDocument({
  52. url,
  53. cMapUrl,
  54. cMapPacked: true,
  55. disableFontFace,
  56. rangeChunkSize,
  57. disableAutoFetch: true,
  58. disableStream: true,
  59. textLayerMode: 2, // PDFJSViewer.TextLayerMode.ENABLE,
  60. })
  61. this.loadDocumentTask.promise
  62. .then(doc => {
  63. if (!this.loadDocumentTask) {
  64. return // ignoring the response since loading task has been aborted
  65. }
  66. const previousDoc = this.viewer.pdfDocument
  67. this.viewer.setDocument(doc)
  68. this.linkService.setDocument(doc)
  69. resolve(doc)
  70. if (previousDoc) {
  71. previousDoc.cleanup().catch(console.error)
  72. previousDoc.destroy()
  73. }
  74. })
  75. .catch(error => {
  76. if (this.loadDocumentTask) {
  77. if (error.name !== 'MissingPDFException') {
  78. captureMessage(`pdf preview error ${error}`)
  79. }
  80. reject(error)
  81. }
  82. })
  83. .finally(() => {
  84. this.loadDocumentTask = undefined
  85. })
  86. })
  87. }
  88. // update the current scale value if the container size changes
  89. updateOnResize() {
  90. const currentScaleValue = this.viewer.currentScaleValue
  91. if (
  92. currentScaleValue === 'auto' ||
  93. currentScaleValue === 'page-fit' ||
  94. currentScaleValue === 'page-width'
  95. ) {
  96. this.viewer.currentScaleValue = currentScaleValue
  97. }
  98. this.viewer.update()
  99. }
  100. // get the page and offset of a click event
  101. clickPosition(event, pageElement, textLayer) {
  102. const { viewport } = this.viewer.getPageView(textLayer.pageNumber - 1)
  103. const pageRect = pageElement.querySelector('canvas').getBoundingClientRect()
  104. const dx = event.clientX - pageRect.left
  105. const dy = event.clientY - pageRect.top
  106. const [left, top] = viewport.convertToPdfPoint(dx, dy)
  107. return {
  108. page: textLayer.pageNumber - 1,
  109. offset: {
  110. left,
  111. top: viewport.viewBox[3] - top,
  112. },
  113. }
  114. }
  115. // get the current page, offset and page size
  116. get currentPosition() {
  117. const pageIndex = this.viewer.currentPageNumber - 1
  118. const pageView = this.viewer.getPageView(pageIndex)
  119. const pageRect = pageView.div.getBoundingClientRect()
  120. const containerRect = this.container.getBoundingClientRect()
  121. const dy = containerRect.top - pageRect.top
  122. const [, , width, height] = pageView.viewport.viewBox
  123. const [, top] = pageView.viewport.convertToPdfPoint(0, dy)
  124. return {
  125. page: pageIndex,
  126. offset: { top, left: 0 },
  127. pageSize: { height, width },
  128. }
  129. }
  130. set currentPosition(position) {
  131. const destArray = [
  132. null,
  133. {
  134. name: 'XYZ', // 'XYZ' = scroll to the given coordinates
  135. },
  136. position.offset.left,
  137. position.offset.top,
  138. null,
  139. ]
  140. this.viewer.scrollPageIntoView({
  141. pageNumber: position.page + 1,
  142. destArray,
  143. })
  144. }
  145. abortDocumentLoading() {
  146. this.loadDocumentTask = undefined
  147. }
  148. destroy() {
  149. if (this.loadDocumentTask) {
  150. this.loadDocumentTask.destroy()
  151. this.loadDocumentTask = undefined
  152. }
  153. this.viewer.destroy()
  154. }
  155. }