v_1.7
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
// Loads PDF.js (vendored first, CDN fallback) and renders one page at a time,
|
||||
// scaled to fit its container and sharp on high-DPI screens.
|
||||
|
||||
let readyPromise = null;
|
||||
|
||||
function loadScript(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const s = document.createElement('script');
|
||||
s.src = src;
|
||||
s.onload = resolve;
|
||||
s.onerror = () => reject(new Error('Failed to load ' + src));
|
||||
document.head.appendChild(s);
|
||||
});
|
||||
}
|
||||
|
||||
export function ensurePdfjs() {
|
||||
if (window.pdfjsLib) return Promise.resolve();
|
||||
if (readyPromise) return readyPromise;
|
||||
|
||||
const CDN = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174';
|
||||
readyPromise = loadScript('/vendor/pdfjs/pdf.min.js')
|
||||
.then(() => {
|
||||
if (!window.pdfjsLib) throw new Error('vendor missing');
|
||||
window.__pdfWorker = '/vendor/pdfjs/pdf.worker.min.js';
|
||||
})
|
||||
.catch(() => loadScript(`${CDN}/pdf.min.js`).then(() => {
|
||||
window.__pdfWorker = `${CDN}/pdf.worker.min.js`;
|
||||
}))
|
||||
.then(() => {
|
||||
window.pdfjsLib.GlobalWorkerOptions.workerSrc = window.__pdfWorker;
|
||||
});
|
||||
return readyPromise;
|
||||
}
|
||||
|
||||
export class PdfViewer {
|
||||
constructor(container) {
|
||||
this.container = container;
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.canvas.className = 'pdf-canvas';
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
container.appendChild(this.canvas);
|
||||
this.pdf = null;
|
||||
this.numPages = 0;
|
||||
this.page = 1;
|
||||
this._busy = false;
|
||||
this._queued = null;
|
||||
this._onResize = () => this.refit();
|
||||
window.addEventListener('resize', this._onResize);
|
||||
}
|
||||
|
||||
async load(url) {
|
||||
await ensurePdfjs();
|
||||
this.pdf = await window.pdfjsLib.getDocument(url).promise;
|
||||
this.numPages = this.pdf.numPages;
|
||||
return this.numPages;
|
||||
}
|
||||
|
||||
async render(n) {
|
||||
if (!this.pdf) return;
|
||||
n = Math.max(1, Math.min(this.numPages, Math.trunc(n) || 1));
|
||||
this.page = n;
|
||||
if (this._busy) { this._queued = n; return; }
|
||||
this._busy = true;
|
||||
try {
|
||||
const page = await this.pdf.getPage(n);
|
||||
const base = page.getViewport({ scale: 1 });
|
||||
const cw = this.container.clientWidth || 800;
|
||||
const ch = this.container.clientHeight || 600;
|
||||
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
||||
const fit = Math.min(cw / base.width, ch / base.height);
|
||||
const vp = page.getViewport({ scale: fit * dpr });
|
||||
this.canvas.width = Math.floor(vp.width);
|
||||
this.canvas.height = Math.floor(vp.height);
|
||||
this.canvas.style.width = Math.floor(vp.width / dpr) + 'px';
|
||||
this.canvas.style.height = Math.floor(vp.height / dpr) + 'px';
|
||||
await page.render({ canvasContext: this.ctx, viewport: vp }).promise;
|
||||
} finally {
|
||||
this._busy = false;
|
||||
if (this._queued !== null) {
|
||||
const q = this._queued;
|
||||
this._queued = null;
|
||||
if (q !== n) this.render(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refit() { if (this.pdf) this.render(this.page); }
|
||||
|
||||
destroy() { window.removeEventListener('resize', this._onResize); }
|
||||
}
|
||||
Reference in New Issue
Block a user