mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-02 14:09:11 -05:00
1a9487d5fd
[web] fixes/improvements for pdf caching GitOrigin-RevId: 2ba0ed4798fd4dfb57f2dfca8be92ae1a769934f
218 lines
6.1 KiB
JavaScript
218 lines
6.1 KiB
JavaScript
import { captureMessage } from '../../../infrastructure/error-reporter'
|
|
import { generatePdfCachingTransportFactory } from './pdf-caching-transport'
|
|
|
|
const params = new URLSearchParams(window.location.search)
|
|
const disableFontFace = params.get('disable-font-face') === 'true'
|
|
const disableStream = process.env.NODE_ENV !== 'test'
|
|
|
|
const DEFAULT_RANGE_CHUNK_SIZE = 128 * 1024 // 128K chunks
|
|
|
|
export default class PDFJSWrapper {
|
|
constructor(container) {
|
|
this.container = container
|
|
}
|
|
|
|
async init() {
|
|
const { PDFJS, PDFJSViewer, cMapUrl, imageResourcesPath } = await import(
|
|
'./pdf-js-versions'
|
|
).then(m => {
|
|
return m.default
|
|
})
|
|
|
|
this.PDFJS = PDFJS
|
|
this.genPdfCachingTransport = generatePdfCachingTransportFactory(PDFJS)
|
|
this.PDFJSViewer = PDFJSViewer
|
|
this.cMapUrl = cMapUrl
|
|
this.imageResourcesPath = imageResourcesPath
|
|
|
|
// create the event bus
|
|
const eventBus = new PDFJSViewer.EventBus()
|
|
|
|
// create the link service
|
|
const linkService = new PDFJSViewer.PDFLinkService({
|
|
eventBus,
|
|
externalLinkTarget: 2,
|
|
externalLinkRel: 'noopener',
|
|
})
|
|
|
|
// create the localization
|
|
// const l10n = new PDFJSViewer.GenericL10n('en-GB') // TODO: locale mapping?
|
|
|
|
// create the viewer
|
|
const viewer = new PDFJSViewer.PDFViewer({
|
|
container: this.container,
|
|
eventBus,
|
|
imageResourcesPath,
|
|
linkService,
|
|
// l10n, // commented out since it currently breaks `aria-label` rendering in pdf pages
|
|
enableScripting: false, // default is false, but set explicitly to be sure
|
|
enableXfa: false, // default is false (2021-10-12), but set explicitly to be sure
|
|
renderInteractiveForms: false,
|
|
maxCanvasPixels: 8192 * 8192, // default is 4096 * 4096, increased for better resolution at high zoom levels
|
|
})
|
|
|
|
linkService.setViewer(viewer)
|
|
|
|
this.eventBus = eventBus
|
|
this.linkService = linkService
|
|
this.viewer = viewer
|
|
}
|
|
|
|
// load a document from a URL
|
|
loadDocument(url, pdfFile) {
|
|
// cancel any previous loading task
|
|
if (this.loadDocumentTask) {
|
|
this.loadDocumentTask.destroy()
|
|
this.loadDocumentTask = undefined
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const rangeTransport = this.genPdfCachingTransport(url, pdfFile, reject)
|
|
let rangeChunkSize = DEFAULT_RANGE_CHUNK_SIZE
|
|
if (rangeTransport && pdfFile.size < 2 * DEFAULT_RANGE_CHUNK_SIZE) {
|
|
// pdf.js disables the "bulk" download optimization when providing a
|
|
// custom range transport. Restore it by bumping the chunk size.
|
|
rangeChunkSize = pdfFile.size
|
|
}
|
|
this.loadDocumentTask = this.PDFJS.getDocument({
|
|
url,
|
|
cMapUrl: this.cMapUrl,
|
|
cMapPacked: true,
|
|
disableFontFace,
|
|
rangeChunkSize,
|
|
disableAutoFetch: true,
|
|
disableStream,
|
|
textLayerMode: 2, // PDFJSViewer.TextLayerMode.ENABLE,
|
|
range: rangeTransport,
|
|
})
|
|
|
|
this.loadDocumentTask.promise
|
|
.then(doc => {
|
|
if (!this.loadDocumentTask) {
|
|
return // ignoring the response since loading task has been aborted
|
|
}
|
|
|
|
const previousDoc = this.viewer.pdfDocument
|
|
|
|
this.viewer.setDocument(doc)
|
|
this.linkService.setDocument(doc)
|
|
resolve(doc)
|
|
|
|
if (previousDoc) {
|
|
previousDoc.cleanup().catch(console.error)
|
|
previousDoc.destroy()
|
|
}
|
|
})
|
|
.catch(error => {
|
|
if (this.loadDocumentTask) {
|
|
if (!error || error.name !== 'MissingPDFException') {
|
|
captureMessage(`pdf preview error ${error}`)
|
|
}
|
|
|
|
reject(error)
|
|
}
|
|
})
|
|
.finally(() => {
|
|
this.loadDocumentTask = undefined
|
|
})
|
|
})
|
|
}
|
|
|
|
// update the current scale value if the container size changes
|
|
updateOnResize() {
|
|
if (!this.isVisible()) {
|
|
return
|
|
}
|
|
|
|
const currentScaleValue = this.viewer.currentScaleValue
|
|
|
|
if (
|
|
currentScaleValue === 'auto' ||
|
|
currentScaleValue === 'page-fit' ||
|
|
currentScaleValue === 'page-width'
|
|
) {
|
|
this.viewer.currentScaleValue = currentScaleValue
|
|
}
|
|
|
|
this.viewer.update()
|
|
}
|
|
|
|
// get the page and offset of a click event
|
|
clickPosition(event, pageElement, textLayer) {
|
|
const { viewport } = this.viewer.getPageView(textLayer.pageNumber - 1)
|
|
|
|
const pageRect = pageElement.querySelector('canvas').getBoundingClientRect()
|
|
|
|
const dx = event.clientX - pageRect.left
|
|
const dy = event.clientY - pageRect.top
|
|
|
|
const [left, top] = viewport.convertToPdfPoint(dx, dy)
|
|
|
|
return {
|
|
page: textLayer.pageNumber - 1,
|
|
offset: {
|
|
left,
|
|
top: viewport.viewBox[3] - top,
|
|
},
|
|
}
|
|
}
|
|
|
|
// get the current page, offset and page size
|
|
get currentPosition() {
|
|
const pageIndex = this.viewer.currentPageNumber - 1
|
|
const pageView = this.viewer.getPageView(pageIndex)
|
|
const pageRect = pageView.div.getBoundingClientRect()
|
|
|
|
const containerRect = this.container.getBoundingClientRect()
|
|
const dy = containerRect.top - pageRect.top
|
|
const dx = containerRect.left - pageRect.left
|
|
const [left, top] = pageView.viewport.convertToPdfPoint(dx, dy)
|
|
const [, , width, height] = pageView.viewport.viewBox
|
|
|
|
return {
|
|
page: pageIndex,
|
|
offset: { top, left },
|
|
pageSize: { height, width },
|
|
}
|
|
}
|
|
|
|
scrollToPosition(position, scale = null) {
|
|
const destArray = [
|
|
null,
|
|
{
|
|
name: 'XYZ', // 'XYZ' = scroll to the given coordinates
|
|
},
|
|
position.offset.left,
|
|
position.offset.top,
|
|
scale,
|
|
]
|
|
|
|
this.viewer.scrollPageIntoView({
|
|
pageNumber: position.page + 1,
|
|
destArray,
|
|
})
|
|
|
|
// scroll the page down by an extra few pixels to account for the pdf.js viewer page border
|
|
this.viewer.container.scrollBy({
|
|
top: -9,
|
|
})
|
|
}
|
|
|
|
isVisible() {
|
|
return this.viewer.container.offsetParent !== null
|
|
}
|
|
|
|
abortDocumentLoading() {
|
|
this.loadDocumentTask = undefined
|
|
}
|
|
|
|
destroy() {
|
|
if (this.loadDocumentTask) {
|
|
this.loadDocumentTask.destroy()
|
|
this.loadDocumentTask = undefined
|
|
}
|
|
if (this.viewer) {
|
|
this.viewer.destroy()
|
|
}
|
|
}
|
|
}
|