I was hoping to encapsulate some of the Document/Page loading logic in Vue components, but I'm having trouble getting it to work. Here's what I've tried. (In case it's relevant, I'm using Vite + Vue + TS with "pdfjs-dist": "^2.14.305"
, "vue": "^3.2.25"
.)
First, I have a component called PDFDocument. It is passed the PDF URL and scale as props, and it handles getting the document and loading the pages.
// PDFDocument.vue
<template>
<div class="pdf-document">
<PDFPage v-for="page in pages" v-bind="{page, scale}" :key="page.pageNumber"/>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import * as pdfjsLib from 'pdfjs-dist';
import PDFPage from './PDFPage.vue';
pdfjsLib.GlobalWorkerOptions.workerSrc = "../../node_modules/pdfjs-dist/build/pdf.worker.js";
export default defineComponent({
props: {
url: { type: String, required: true },
scale: { type: Number, required: true },
},
data() {
let pdf: pdfjsLib.PDFDocumentProxy | undefined;
let pages: pdfjsLib.PDFPageProxy[] = [];
return { pdf, pages };
},
beforeMount() {
let loadingTask = pdfjsLib.getDocument(this.url);
loadingTask.promise.then((pdf) => {
this.pdf = pdf;
const promises = [];
for (let i = 1; i <= pdf.numPages; i++) {
promises.push(pdf.getPage(i));
}
Promise.all(promises).then(pages => {
this.pages = pages;
console.log(this.pages);
});
});
},
components: { PDFPage }
})
</script>
This seems to work, but already I'm encountering my first problem. If I try to use this.pdf.getPage(i)
instead of pdf.getPage(i)
, I get an error: Uncaught (in promise) TypeError: Cannot read from private field
. But just using pdf
fixes that for now, so okay.
Then I have a PDFPage component that handles setting up the canvas for a page and getting the PDFPageProxy
to render onto it.
// PDFPage.vue
<template>
<canvas
class="pdf-page"
:width="width"
:height="height"
:style="canvasStyle"
></canvas>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import * as pdfjsLib from 'pdfjs-dist';
pdfjsLib.GlobalWorkerOptions.workerSrc = "../../node_modules/pdfjs-dist/build/pdf.worker.js";
export default defineComponent({
props: {
page: {type: pdfjsLib.PDFPageProxy, required: true},
scale: {type: Number, required: true},
},
data() {
let viewport: pdfjsLib.PageViewport | undefined;
let renderTask: pdfjsLib.RenderTask | undefined;
return { viewport, renderTask }
},
computed: {
width() { if (!this.viewport) return 0; return this.viewport.width; },
height() { if (!this.viewport) return 0; return this.viewport.height; },
canvasStyle() {
const {width: actualWidth, height: actualHeight} = this.actualSizeViewport;
const pxRatio = window.devicePixelRatio || 1;
const [pxWidth, pxHeight] = [actualWidth, actualHeight]
.map(dim => Math.ceil(dim / pxRatio));
return `width: ${pxWidth}px; height: ${pxHeight}px;`
},
actualSizeViewport() {
return (this.viewport as pdfjsLib.PageViewport).clone({scale: this.scale});
},
},
methods: {
drawPage() {
if (this.renderTask) return;
const {viewport} = this;
const canvasContext = this.$el.getContext('2d');
const renderContext = {canvasContext, viewport};
this.renderTask = this.page.render(renderContext);
(this.renderTask as pdfjsLib.RenderTask).promise.then(
() => this.$emit('rendered', this.page)
);
}
},
created() {
this.viewport = this.page.getViewport(this.scale);
},
mounted() {
this.drawPage();
}
})
</script>
Here, I get the error Uncaught (in promise) TypeError: Cannot read from private field
(or sometimes Cannot write to private field
) when trying to do this.renderTask = this.page.render(renderContext)
, which I think is a problem because I'm using this.page
.
Does anyone know how to get around this? I feel like it might have to do with the fact that these Proxy
objects claim to be proxies to a worker thread, and I might be disrespecting that?