zotero/resource/pdf-renderer/renderer.js

142 lines
4.3 KiB
JavaScript
Raw Normal View History

/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2021 Corporation for Digital Scholarship
Vienna, Virginia, USA
http://digitalscholar.org/
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
const SCALE_FACTOR = 4;
window.pdfjsLib.GlobalWorkerOptions.workerSrc = 'resource://zotero/pdf-reader/pdf.worker.js';
function errObject(err) {
return JSON.parse(JSON.stringify(err, Object.getOwnPropertyNames(err)));
}
let lastPromiseID = 0;
let waitingPromises = {};
async function query(action, data, transfer) {
return new Promise((resolve, reject) => {
lastPromiseID++;
waitingPromises[lastPromiseID] = { resolve, reject };
parent.postMessage({
id: lastPromiseID,
action,
data
}, parent.origin, transfer);
});
}
async function renderAnnotations(buf, annotations) {
let num = 0;
let pdfDocument = await window.pdfjsLib.getDocument({ data: buf }).promise;
let pages = new Map();
for (let annotation of annotations) {
let pageIndex = annotation.position.pageIndex;
let page = pages.get(pageIndex) || [];
page.push(annotation);
pages.set(pageIndex, page);
}
for (let [pageIndex, annotations] of pages) {
let { canvas, viewport } = await renderPage(pdfDocument, pageIndex);
for (let annotation of annotations) {
let position = p2v(annotation.position, viewport);
let rect = position.rects[0];
let [left, top, right, bottom] = rect;
let width = right - left;
let height = bottom - top;
let newCanvas = document.createElement('canvas');
newCanvas.width = width;
newCanvas.height = height;
let newCanvasContext = newCanvas.getContext('2d');
newCanvasContext.drawImage(canvas, left, top, width, height, 0, 0, width, height);
let blob = await new Promise(resolve => newCanvas.toBlob(resolve, 'image/png'));
let image = await new Response(blob).arrayBuffer();
await query('renderedAnnotation', { annotation: { id: annotation.id, image } }, [image]);
num++;
}
}
return num;
}
function p2v(position, viewport) {
return {
pageIndex: position.pageIndex,
rects: position.rects.map(rect => {
let [x1, y2] = viewport.convertToViewportPoint(rect[0], rect[1]);
let [x2, y1] = viewport.convertToViewportPoint(rect[2], rect[3]);
return [Math.min(x1, x2), Math.min(y1, y2), Math.max(x1, x2), Math.max(y1, y2)];
})
};
}
async function renderPage(pdfDocument, pageIndex) {
let page = await pdfDocument.getPage(pageIndex + 1);
var canvas = document.createElement('canvas');
var viewport = page.getViewport({ scale: SCALE_FACTOR });
var context = canvas.getContext('2d', { alpha: false });
canvas.height = viewport.height;
canvas.width = viewport.width;
await page.render({ canvasContext: context, viewport: viewport }).promise;
return { canvas, viewport };
}
window.addEventListener('message', async (event) => {
if (event.source === parent) {
return;
}
let message = event.data;
if (message.responseID) {
let { resolve, reject } = waitingPromises[message.responseID];
delete waitingPromises[message.responseID];
if (message.data) {
resolve(message.data);
}
else {
let err = new Error(message.error.message);
Object.assign(err, message.error);
reject(err);
}
return;
}
if (message.action === 'renderAnnotations') {
try {
let { buf, annotations } = message.data;
let num = await renderAnnotations(buf, annotations);
2021-02-16 09:10:20 +00:00
parent.postMessage({ responseID: message.id, data: num }, parent.origin);
}
catch (e) {
console.log(e);
parent.postMessage({
2021-02-16 09:10:20 +00:00
responseID: message.id,
error: errObject(e)
}, parent.origin);
}
}
});
setTimeout(() => {
query('initialized', {});
}, 100);