Reputation: 35410
This is most probably due to my limited experience with JavaScript, but I haven't been able to figure this out. This is a Vue 2 + Typescript project, but the specific problem doesn't have anything to do with Vue or Typescript.
OK. So I have added the following functions in one of my components. Primary purpose of this component is to take a PDF file and return the rasterized version of its first page in the form of a Blob
. Here are my functions:
async importButtonHandler () {
if (this.file) {
const blob = await this.pdfToBlob(this.file)
if (blob) {
const file2 = new File([blob], 'raster_pdf', { type: 'image/png' })
// Further processing
}
}
}
async pdfToBlob (pdfFile: File): Promise<Blob|null> {
const canvas: HTMLCanvasElement = document.createElement('canvas')
const buffer = await this.readFileAsync(pdfFile)
const typedarray = new Uint8Array(buffer)
// get handle of pdf document
const doc = await pdfjsLib.getDocument(typedarray).promise
// get handle of page
const pdfpage = await doc.getPage(1)
// get viewport to render the page at required scale
const viewport = pdfpage.getViewport()
// set canvas height same as viewport height
canvas.height = viewport.height
canvas.width = viewport.width
const renderContext: RenderParameters = {
canvasContext: canvas.getContext('2d') as CanvasRenderingContext2D,
viewport: viewport
}
// render the page contents in the canvas
await pdfpage.render(renderContext).promise
return new Promise((resolve, reject) => {
try {
canvas.toBlob(resolve)
} catch (error) {
reject(error)
}
})
}
readFileAsync (file: File): Promise<ArrayBuffer> {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => {
resolve(reader.result as ArrayBuffer)
}
reader.onerror = reject
reader.readAsArrayBuffer(file)
})
}
It hits the function pdfToBlob
and calls readFileAsync
from therein, reads the file into buffer
correctly, but immediately after that (on const typedarray = new Uint8Array(buffer)
) instead of executing that line, it hits pdfToBlob
function again and then throws an exception saying:
TypeError: Cannot read properties of undefined (reading 'getDocument')
and for the life of me I can't figure out why it enters the function again (there is no other place where this function is called).
My gut feeling is that it is about the Promise that I'm returning in the function, but all my experimentation hasn't led me to any clue.
Can someone help me figure out what's wrong here?
Upvotes: 1
Views: 362
Reputation: 35410
Figured it out (kind of).
Firstly, my pdfjsLib
needed to be imported correctly and supplied with a worker before calling getDocument
. This was the cause of my second issue. Here is the correct way of importing the lib and worker in Vue 2/Typescript project:
import * as pdfjsLib from 'pdfjs-dist'
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry'
Then supply worker to the library and call getDocument:
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker
const doc = await pdfjsLib.getDocument(YOUR_PDF_DATA).promise
Rest of the code in my OP works correctly. Note that pdf.worker.entry
doesn't have type definitions (or at least I couldn't find them anywhere), but it works nonetheless.
The first issue of getting handler called multiple times seems to be a known issue (or behavior if you like) in Vuetify. They suggest to use native
, or prevent
or stop
when defining the click
attribute (like @click.stop="importButtonHandler"
) to mitigate the behavior. I have tried it without success. My handler is still called twice. In my particular scenario this doesn't cause any functional harm so I can live with it for now, but I can think of situations where this behavior could be disastrous.
Hope this helps someone down the road.
Upvotes: 1