Improve PDF importing and introduce rendering
This commit is contained in:
parent
89d9efdec7
commit
a19693fa7a
8 changed files with 439 additions and 163 deletions
|
@ -36,10 +36,15 @@ Zotero.Annotations = new function () {
|
||||||
var file = this._getLibraryCacheDirectory(libraryID);
|
var file = this._getLibraryCacheDirectory(libraryID);
|
||||||
return OS.Path.join(file, key + '.png');
|
return OS.Path.join(file, key + '.png');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.hasCacheImage = async function (item) {
|
||||||
|
return OS.File.exists(this.getCacheImagePath(item));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
this.saveCacheImage = async function ({ libraryID, key }, blob) {
|
this.saveCacheImage = async function ({ libraryID, key }, blob) {
|
||||||
var item = await Zotero.Items.getByLibraryAndKey(libraryID, key);
|
var item = await Zotero.Items.getByLibraryAndKeyAsync(libraryID, key);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
throw new Error(`Item not found`);
|
throw new Error(`Item not found`);
|
||||||
}
|
}
|
||||||
|
@ -96,6 +101,12 @@ Zotero.Annotations = new function () {
|
||||||
}
|
}
|
||||||
return OS.Path.join(...parts);
|
return OS.Path.join(...parts);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.positionEquals = function (position1, position2) {
|
||||||
|
return position1.pageIndex == position2.pageIndex
|
||||||
|
&& JSON.stringify(position1.rects) == JSON.stringify(position2.rects);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
this.toJSON = async function (item) {
|
this.toJSON = async function (item) {
|
||||||
|
@ -103,6 +114,7 @@ Zotero.Annotations = new function () {
|
||||||
o.libraryID = item.libraryID;
|
o.libraryID = item.libraryID;
|
||||||
o.key = item.key;
|
o.key = item.key;
|
||||||
o.type = item.annotationType;
|
o.type = item.annotationType;
|
||||||
|
o.isExternal = item.annotationIsExternal;
|
||||||
o.isAuthor = !item.createdByUserID || item.createdByUserID == Zotero.Users.getCurrentUserID();
|
o.isAuthor = !item.createdByUserID || item.createdByUserID == Zotero.Users.getCurrentUserID();
|
||||||
if (!o.isAuthor) {
|
if (!o.isAuthor) {
|
||||||
o.authorName = Zotero.Users.getName(item.createdByUserID);
|
o.authorName = Zotero.Users.getName(item.createdByUserID);
|
||||||
|
@ -185,10 +197,19 @@ Zotero.Annotations = new function () {
|
||||||
if (json.type == 'highlight') {
|
if (json.type == 'highlight') {
|
||||||
item.annotationText = json.text;
|
item.annotationText = json.text;
|
||||||
}
|
}
|
||||||
|
item.annotationIsExternal = !!json.isExternal;
|
||||||
item.annotationComment = json.comment;
|
item.annotationComment = json.comment;
|
||||||
item.annotationColor = json.color;
|
item.annotationColor = json.color;
|
||||||
item.annotationPageLabel = json.pageLabel;
|
item.annotationPageLabel = json.pageLabel;
|
||||||
item.annotationSortIndex = json.sortIndex;
|
item.annotationSortIndex = json.sortIndex;
|
||||||
|
|
||||||
|
if (item.annotationType == 'image' && item.annotationPosition) {
|
||||||
|
var currentPosition = JSON.parse(item.annotationPosition);
|
||||||
|
if (!this.positionEquals(currentPosition, json.position)) {
|
||||||
|
await this.removeCacheImage(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
item.annotationPosition = JSON.stringify(Object.assign({}, json.position));
|
item.annotationPosition = JSON.stringify(Object.assign({}, json.position));
|
||||||
// TODO: Can colors be set?
|
// TODO: Can colors be set?
|
||||||
item.setTags((json.tags || []).map(t => ({ tag: t.name })));
|
item.setTags((json.tags || []).map(t => ({ tag: t.name })));
|
||||||
|
|
|
@ -253,8 +253,6 @@ class EditorInstance {
|
||||||
html += `<p><span class="citation" data-citation="${encodeURIComponent(JSON.stringify(citation))}">(${formatted})</span></p>`;
|
html += `<p><span class="citation" data-citation="${encodeURIComponent(JSON.stringify(citation))}">(${formatted})</span></p>`;
|
||||||
}
|
}
|
||||||
else if (item.isNote()) {
|
else if (item.isNote()) {
|
||||||
// TODO: Remove when fixed
|
|
||||||
item._loaded.childItems = true;
|
|
||||||
let note = item.note;
|
let note = item.note;
|
||||||
let attachments = await Zotero.Items.getAsync(item.getAttachments());
|
let attachments = await Zotero.Items.getAsync(item.getAttachments());
|
||||||
for (let attachment of attachments) {
|
for (let attachment of attachments) {
|
||||||
|
@ -417,8 +415,6 @@ class EditorInstance {
|
||||||
if (this._isAttachment) {
|
if (this._isAttachment) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO: Remove when fixed
|
|
||||||
this._item._loaded.childItems = true;
|
|
||||||
let attachmentItems = this._item.getAttachments().map(id => Zotero.Items.get(id));
|
let attachmentItems = this._item.getAttachments().map(id => Zotero.Items.get(id));
|
||||||
let abandonedItems = attachmentItems.filter(item => !attachmentKeys.includes(item.key));
|
let abandonedItems = attachmentItems.filter(item => !attachmentKeys.includes(item.key));
|
||||||
for (let item of abandonedItems) {
|
for (let item of abandonedItems) {
|
||||||
|
@ -895,7 +891,6 @@ class EditorInstance {
|
||||||
editorInstance._item = note;
|
editorInstance._item = note;
|
||||||
let jsonAnnotations = [];
|
let jsonAnnotations = [];
|
||||||
for (let annotation of annotations) {
|
for (let annotation of annotations) {
|
||||||
annotation._loaded.childItems = true;
|
|
||||||
let jsonAnnotation = await Zotero.Annotations.toJSON(annotation);
|
let jsonAnnotation = await Zotero.Annotations.toJSON(annotation);
|
||||||
jsonAnnotation.itemId = attachmentItem.id;
|
jsonAnnotation.itemId = attachmentItem.id;
|
||||||
jsonAnnotations.push(jsonAnnotation);
|
jsonAnnotations.push(jsonAnnotation);
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
const WORKER_URL = 'chrome://zotero/content/xpcom/pdfWorker/worker.js';
|
const WORKER_URL = 'chrome://zotero/content/xpcom/pdfWorker/worker.js';
|
||||||
const CMAPS_URL = 'resource://zotero/pdf-reader/cmaps/';
|
const CMAPS_URL = 'resource://zotero/pdf-reader/cmaps/';
|
||||||
|
const RENDERER_URL = 'resource://zotero/pdf-renderer/renderer.html';
|
||||||
|
|
||||||
class PDFWorker {
|
class PDFWorker {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -122,20 +123,16 @@ class PDFWorker {
|
||||||
Zotero.debug(event);
|
Zotero.debug(event);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isPDFAttachment(item) {
|
|
||||||
return item.isAttachment() && item.attachmentContentType === 'application/pdf';
|
|
||||||
}
|
|
||||||
|
|
||||||
canImport(item) {
|
canImport(item) {
|
||||||
if (this.isPDFAttachment(item)) {
|
if (item.isPDFAttachment()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (item.isRegularItem()) {
|
else if (item.isRegularItem()) {
|
||||||
let ids = item.getAttachments();
|
let ids = item.getAttachments();
|
||||||
for (let id of ids) {
|
for (let id of ids) {
|
||||||
let attachment = Zotero.Items.get(id);
|
let attachment = Zotero.Items.get(id);
|
||||||
if (this.isPDFAttachment(attachment)) {
|
if (attachment.isPDFAttachment()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,8 +151,8 @@ class PDFWorker {
|
||||||
async export(itemID, path, isPriority, password) {
|
async export(itemID, path, isPriority, password) {
|
||||||
return this._enqueue(async () => {
|
return this._enqueue(async () => {
|
||||||
let attachment = await Zotero.Items.getAsync(itemID);
|
let attachment = await Zotero.Items.getAsync(itemID);
|
||||||
if (!this.isPDFAttachment(attachment)) {
|
if (!attachment.isPDFAttachment()) {
|
||||||
throw new Error('not a valid attachment');
|
throw new Error('Item must be a PDF attachment');
|
||||||
}
|
}
|
||||||
let items = attachment.getAnnotations();
|
let items = attachment.getAnnotations();
|
||||||
let annotations = [];
|
let annotations = [];
|
||||||
|
@ -163,7 +160,7 @@ class PDFWorker {
|
||||||
annotations.push({
|
annotations.push({
|
||||||
id: item.key,
|
id: item.key,
|
||||||
type: item.annotationType,
|
type: item.annotationType,
|
||||||
authorName: Zotero.Users.getName(item.createdByUserID) || '',
|
authorName: Zotero.Users.getName(item.createdByUserID) || Zotero.Users.getCurrentUsername() || '',
|
||||||
comment: item.annotationComment || '',
|
comment: item.annotationComment || '',
|
||||||
color: item.annotationColor,
|
color: item.annotationColor,
|
||||||
position: JSON.parse(item.annotationPosition),
|
position: JSON.parse(item.annotationPosition),
|
||||||
|
@ -187,7 +184,7 @@ class PDFWorker {
|
||||||
*/
|
*/
|
||||||
async exportParent(item, directory) {
|
async exportParent(item, directory) {
|
||||||
if (!item.isRegularItem()) {
|
if (!item.isRegularItem()) {
|
||||||
throw new Error('regular item not provided');
|
throw new Error('Item must be a regular item');
|
||||||
}
|
}
|
||||||
if (!directory) {
|
if (!directory) {
|
||||||
throw new Error('\'directory\' not provided');
|
throw new Error('\'directory\' not provided');
|
||||||
|
@ -196,7 +193,7 @@ class PDFWorker {
|
||||||
let ids = item.getAttachments();
|
let ids = item.getAttachments();
|
||||||
for (let id of ids) {
|
for (let id of ids) {
|
||||||
let attachment = Zotero.Items.get(id);
|
let attachment = Zotero.Items.get(id);
|
||||||
if (this.isPDFAttachment(attachment)) {
|
if (attachment.isPDFAttachment()) {
|
||||||
let path = OS.Path.join(directory, attachment.attachmentFilename);
|
let path = OS.Path.join(directory, attachment.attachmentFilename);
|
||||||
promises.push(this.export(id, path));
|
promises.push(this.export(id, path));
|
||||||
}
|
}
|
||||||
|
@ -207,57 +204,56 @@ class PDFWorker {
|
||||||
/**
|
/**
|
||||||
* Import annotations from PDF attachment
|
* Import annotations from PDF attachment
|
||||||
*
|
*
|
||||||
* @param {Integer} itemID
|
* @param {Integer} itemID Attachment item id
|
||||||
* @param {Boolean} save Save imported annotations, or otherwise just return the number of importable annotations
|
|
||||||
* @param {Boolean} isPriority
|
* @param {Boolean} isPriority
|
||||||
* @param {String} password
|
* @param {String} password
|
||||||
* @returns {Promise<Integer>} Number of annotations
|
* @returns {Promise<Integer>} Number of annotations
|
||||||
*/
|
*/
|
||||||
async import(itemID, save, isPriority, password) {
|
async import(itemID, isPriority, password) {
|
||||||
return this._enqueue(async () => {
|
return this._enqueue(async () => {
|
||||||
let attachment = await Zotero.Items.getAsync(itemID);
|
let attachment = await Zotero.Items.getAsync(itemID);
|
||||||
if (!this.isPDFAttachment(attachment)) {
|
if (!attachment.isPDFAttachment()) {
|
||||||
throw new Error('not a valid PDF attachment');
|
throw new Error('Item must be a PDF attachment');
|
||||||
}
|
}
|
||||||
// TODO: Remove when fixed
|
|
||||||
attachment._loaded.childItems = true;
|
let mtime = Math.floor(await attachment.attachmentModificationTime / 1000);
|
||||||
let items = attachment.getAnnotations();
|
if (attachment.attachmentLastProcessedModificationTime === mtime) {
|
||||||
let existingAnnotations = [];
|
return false;
|
||||||
for (let item of items) {
|
|
||||||
existingAnnotations.push({
|
|
||||||
id: item.key,
|
|
||||||
type: item.annotationType,
|
|
||||||
comment: item.annotationComment || '',
|
|
||||||
position: JSON.parse(item.annotationPosition)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let existingAnnotations = attachment
|
||||||
|
.getAnnotations()
|
||||||
|
.filter(x => x.annotationIsExternal)
|
||||||
|
.map(annotation => ({
|
||||||
|
id: annotation.key,
|
||||||
|
type: annotation.annotationType,
|
||||||
|
position: JSON.parse(annotation.annotationPosition),
|
||||||
|
comment: annotation.annotationComment || ''
|
||||||
|
}));
|
||||||
|
|
||||||
let path = await attachment.getFilePath();
|
let path = await attachment.getFilePath();
|
||||||
let buf = await OS.File.read(path, {});
|
let buf = await OS.File.read(path, {});
|
||||||
buf = new Uint8Array(buf).buffer;
|
buf = new Uint8Array(buf).buffer;
|
||||||
let res = await this._query('import', { buf, existingAnnotations, password }, [buf]);
|
let { imported, deleted } = await this._query('import', {
|
||||||
let annotations = res.annotations;
|
buf, existingAnnotations, password
|
||||||
if (save) {
|
}, [buf]);
|
||||||
for (let annotation of annotations) {
|
|
||||||
// TODO: Utilize the saved Zotero item key for deduplication. Newer annotation modificaiton date wins
|
for (let annotation of imported) {
|
||||||
annotation.key = Zotero.DataObjectUtilities.generateKey();
|
annotation.key = Zotero.DataObjectUtilities.generateKey();
|
||||||
await Zotero.Annotations.saveFromJSON(attachment, annotation);
|
annotation.isExternal = true;
|
||||||
}
|
await Zotero.Annotations.saveFromJSON(attachment, annotation);
|
||||||
attachment.attachmentHasUnimportedAnnotations = false;
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
attachment.attachmentHasUnimportedAnnotations = !!annotations.length;
|
for (let key of deleted) {
|
||||||
|
let annotation = Zotero.Items.getByLibraryAndKey(attachment.libraryID, key);
|
||||||
|
await annotation.eraseTx();
|
||||||
}
|
}
|
||||||
for (let reader of Zotero.Reader._readers) {
|
|
||||||
if (reader._itemID === itemID) {
|
attachment.attachmentLastProcessedModificationTime = mtime;
|
||||||
reader.toggleImportPrompt(attachment.attachmentHasUnimportedAnnotations);
|
await attachment.saveTx({ skipDateModifiedUpdate: true });
|
||||||
}
|
|
||||||
}
|
return !!(imported.length || deleted.length);
|
||||||
attachment.attachmentLastProcessedModificationTime = Math.floor(
|
}, isPriority);
|
||||||
await attachment.attachmentModificationTime / 1000
|
|
||||||
);
|
|
||||||
await attachment.saveTx();
|
|
||||||
return annotations.length;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -267,14 +263,14 @@ class PDFWorker {
|
||||||
*/
|
*/
|
||||||
async importParent(item) {
|
async importParent(item) {
|
||||||
if (!item.isRegularItem()) {
|
if (!item.isRegularItem()) {
|
||||||
throw new Error('regular item not provided');
|
throw new Error('Item must be a regular item');
|
||||||
}
|
}
|
||||||
let promises = [];
|
let promises = [];
|
||||||
let ids = item.getAttachments();
|
let ids = item.getAttachments();
|
||||||
for (let id of ids) {
|
for (let id of ids) {
|
||||||
let attachment = Zotero.Items.get(id);
|
let attachment = Zotero.Items.get(id);
|
||||||
if (this.isPDFAttachment(attachment)) {
|
if (attachment.isPDFAttachment()) {
|
||||||
promises.push(this.import(id, true));
|
promises.push(this.import({ itemID: id, isPriority: true }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
@ -282,3 +278,169 @@ class PDFWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.PDFWorker = new PDFWorker();
|
Zotero.PDFWorker = new PDFWorker();
|
||||||
|
|
||||||
|
|
||||||
|
// PDF Renderer
|
||||||
|
class PDFRenderer {
|
||||||
|
constructor() {
|
||||||
|
this._browser = null;
|
||||||
|
this._lastPromiseID = 0;
|
||||||
|
this._waitingPromises = {};
|
||||||
|
this._queue = [];
|
||||||
|
this._processingQueue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _processQueue() {
|
||||||
|
await this._init();
|
||||||
|
if (this._processingQueue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._processingQueue = true;
|
||||||
|
let item;
|
||||||
|
while ((item = this._queue.shift())) {
|
||||||
|
if (item) {
|
||||||
|
let [fn, resolve, reject] = item;
|
||||||
|
try {
|
||||||
|
resolve(await fn());
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._processingQueue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _enqueue(fn, isPriority) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (isPriority) {
|
||||||
|
this._queue.unshift([fn, resolve, reject]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._queue.push([fn, resolve, reject]);
|
||||||
|
}
|
||||||
|
this._processQueue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async _query(action, data, transfer) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this._lastPromiseID++;
|
||||||
|
this._waitingPromises[this._lastPromiseID] = { resolve, reject };
|
||||||
|
this._browser.contentWindow.postMessage({
|
||||||
|
id: this._lastPromiseID,
|
||||||
|
action,
|
||||||
|
data
|
||||||
|
}, this._browser.contentWindow.origin, transfer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async _init() {
|
||||||
|
if (this._browser) return;
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this._browser = Zotero.Browser.createHiddenBrowser();
|
||||||
|
let doc = this._browser.ownerDocument;
|
||||||
|
let container = doc.createElement('hbox');
|
||||||
|
container.style.position = 'fixed';
|
||||||
|
container.style.zIndex = '-1';
|
||||||
|
container.append(this._browser);
|
||||||
|
doc.documentElement.append(container);
|
||||||
|
this._browser.style.width = '1px';
|
||||||
|
this._browser.style.height = '1px';
|
||||||
|
this._browser.addEventListener('DOMContentLoaded', (event) => {
|
||||||
|
if (this._browser.contentWindow.location.href === 'about:blank') return;
|
||||||
|
this._browser.contentWindow.addEventListener('message', _handleMessage);
|
||||||
|
});
|
||||||
|
this._browser.loadURI(RENDERER_URL);
|
||||||
|
|
||||||
|
let _handleMessage = async (event) => {
|
||||||
|
let message = event.data;
|
||||||
|
if (message.responseId) {
|
||||||
|
let { resolve, reject } = this._waitingPromises[message.responseId];
|
||||||
|
delete this._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 === 'initialized') {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
else if (message.action === 'renderedAnnotation') {
|
||||||
|
let { id, image } = message.data.annotation;
|
||||||
|
let item = await Zotero.Items.getAsync(id);
|
||||||
|
let win = Zotero.getMainWindow();
|
||||||
|
if (!win) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let blob = new win.Blob([new Uint8Array(image)]);
|
||||||
|
await Zotero.Annotations.saveCacheImage(item, blob);
|
||||||
|
await Zotero.Notifier.trigger('modify', 'item', [item.id]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render missing image annotation images for attachment
|
||||||
|
*
|
||||||
|
* @param {Integer} itemID Attachment item id
|
||||||
|
* @param {Boolean} isPriority
|
||||||
|
* @returns {Promise<Integer>}
|
||||||
|
*/
|
||||||
|
async renderAttachmentAnnotations(itemID, isPriority) {
|
||||||
|
return this._enqueue(async () => {
|
||||||
|
let attachment = await Zotero.Items.getAsync(itemID);
|
||||||
|
let annotations = [];
|
||||||
|
for (let annotation of attachment.getAnnotations()) {
|
||||||
|
if (annotation.annotationType === 'image'
|
||||||
|
&& !await Zotero.Annotations.hasCacheImage(annotation)) {
|
||||||
|
annotations.push({
|
||||||
|
id: annotation.id,
|
||||||
|
position: JSON.parse(annotation.annotationPosition)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!annotations.length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let path = await attachment.getFilePath();
|
||||||
|
let buf = await OS.File.read(path, {});
|
||||||
|
buf = new Uint8Array(buf).buffer;
|
||||||
|
return this._query('renderAnnotations', { buf, annotations }, [buf]);
|
||||||
|
}, isPriority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render image annotation image
|
||||||
|
*
|
||||||
|
* @param {Integer} itemID Attachment item id
|
||||||
|
* @param {Boolean} isPriority
|
||||||
|
* @returns {Promise<Boolean>}
|
||||||
|
*/
|
||||||
|
async renderAnnotation(itemID, isPriority) {
|
||||||
|
return this._enqueue(async () => {
|
||||||
|
let annotation = await Zotero.Items.getAsync(itemID);
|
||||||
|
if (await Zotero.Annotations.hasCacheImage(annotation)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let attachment = await Zotero.Items.getAsync(annotation.parentID);
|
||||||
|
let path = await attachment.getFilePath();
|
||||||
|
let buf = await OS.File.read(path, {});
|
||||||
|
buf = new Uint8Array(buf).buffer;
|
||||||
|
let annotations = [{
|
||||||
|
id: annotation.id,
|
||||||
|
position: JSON.parse(annotation.annotationPosition)
|
||||||
|
}];
|
||||||
|
return !!await this._query('renderAnnotations', { buf, annotations }, [buf]);
|
||||||
|
}, isPriority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.PDFRenderer = new PDFRenderer();
|
||||||
|
|
|
@ -1,8 +1,27 @@
|
||||||
// Temporary stuff
|
/*
|
||||||
Zotero.PDF = {
|
***** BEGIN LICENSE BLOCK *****
|
||||||
dateChecked: {},
|
|
||||||
hasUnmachedAnnotations: {}
|
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 *****
|
||||||
|
*/
|
||||||
|
|
||||||
class ReaderInstance {
|
class ReaderInstance {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -46,7 +65,7 @@ class ReaderInstance {
|
||||||
annotations,
|
annotations,
|
||||||
state,
|
state,
|
||||||
location,
|
location,
|
||||||
promptImport: item.attachmentHasUnimportedAnnotations,
|
promptImport: false,
|
||||||
showItemPaneToggle: this._showItemPaneToggle,
|
showItemPaneToggle: this._showItemPaneToggle,
|
||||||
sidebarWidth: this._sidebarWidth,
|
sidebarWidth: this._sidebarWidth,
|
||||||
sidebarOpen: this._sidebarOpen,
|
sidebarOpen: this._sidebarOpen,
|
||||||
|
@ -68,10 +87,10 @@ class ReaderInstance {
|
||||||
this._setTitleValue(title);
|
this._setTitleValue(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setAnnotations(ids) {
|
async setAnnotations(items) {
|
||||||
let annotations = [];
|
let annotations = [];
|
||||||
for (let id of ids) {
|
for (let item of items) {
|
||||||
let annotation = await this._getAnnotation(id);
|
let annotation = await this._getAnnotation(item);
|
||||||
if (annotation) {
|
if (annotation) {
|
||||||
annotations.push(annotation);
|
annotations.push(annotation);
|
||||||
}
|
}
|
||||||
|
@ -90,10 +109,6 @@ class ReaderInstance {
|
||||||
async navigate(location) {
|
async navigate(location) {
|
||||||
this._postMessage({ action: 'navigate', location });
|
this._postMessage({ action: 'navigate', location });
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleImportPrompt(enable) {
|
|
||||||
this._postMessage({ action: 'toggleImportPrompt', enable });
|
|
||||||
}
|
|
||||||
|
|
||||||
enableAddToNote(enable) {
|
enableAddToNote(enable) {
|
||||||
this._postMessage({ action: 'enableAddToNote', enable });
|
this._postMessage({ action: 'enableAddToNote', enable });
|
||||||
|
@ -292,23 +307,10 @@ class ReaderInstance {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let savedAnnotation = await Zotero.Annotations.saveFromJSON(attachment, annotation, saveOptions);
|
let savedAnnotation = await Zotero.Annotations.saveFromJSON(attachment, annotation, saveOptions);
|
||||||
if (annotation.image) {
|
|
||||||
|
if (annotation.image && !await Zotero.Annotations.hasCacheImage(savedAnnotation)) {
|
||||||
let blob = this._dataURLtoBlob(annotation.image);
|
let blob = this._dataURLtoBlob(annotation.image);
|
||||||
let attachmentIds = savedAnnotation.getAttachments();
|
await Zotero.Annotations.saveCacheImage(savedAnnotation, blob);
|
||||||
if (attachmentIds.length) {
|
|
||||||
let attachment = Zotero.Items.get(attachmentIds[0]);
|
|
||||||
let path = await attachment.getFilePathAsync();
|
|
||||||
await Zotero.File.putContentsAsync(path, blob);
|
|
||||||
await Zotero.Sync.Storage.Local.updateSyncStates([attachment], 'to_upload');
|
|
||||||
Zotero.Notifier.trigger('modify', 'item', attachment.id, { instanceID: this._instanceID });
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
await Zotero.Attachments.importEmbeddedImage({
|
|
||||||
blob,
|
|
||||||
parentItemID: savedAnnotation.id,
|
|
||||||
saveOptions
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -360,11 +362,11 @@ class ReaderInstance {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case 'import': {
|
// case 'import': {
|
||||||
Zotero.debug('Importing PDF annotations');
|
// Zotero.debug('Importing PDF annotations');
|
||||||
Zotero.PDFWorker.import(this._itemID, true, true);
|
// Zotero.PDFWorker.import(this._itemID, true, true);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
case 'importDismiss': {
|
case 'importDismiss': {
|
||||||
Zotero.debug('Dismiss PDF annotations');
|
Zotero.debug('Dismiss PDF annotations');
|
||||||
return;
|
return;
|
||||||
|
@ -434,7 +436,8 @@ class ReaderInstance {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return item JSON in the pdf-reader ready format
|
* Return item JSON in the pdf-reader ready format
|
||||||
* @param itemID
|
*
|
||||||
|
* @param {Zotero.Item} item
|
||||||
* @returns {Object|null}
|
* @returns {Object|null}
|
||||||
*/
|
*/
|
||||||
async _getAnnotation(item) {
|
async _getAnnotation(item) {
|
||||||
|
@ -442,10 +445,9 @@ class ReaderInstance {
|
||||||
if (!item || !item.isAnnotation()) {
|
if (!item || !item.isAnnotation()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// TODO: Remve when fixed
|
|
||||||
item._loaded.childItems = true;
|
|
||||||
let json = await Zotero.Annotations.toJSON(item);
|
let json = await Zotero.Annotations.toJSON(item);
|
||||||
json.id = item.key;
|
json.id = item.key;
|
||||||
|
json.readOnly = !json.isAuthor || json.isExternal;
|
||||||
delete json.key;
|
delete json.key;
|
||||||
for (let key in json) {
|
for (let key in json) {
|
||||||
json[key] = json[key] || '';
|
json[key] = json[key] || '';
|
||||||
|
@ -622,7 +624,7 @@ class Reader {
|
||||||
this._sidebarOpen = false;
|
this._sidebarOpen = false;
|
||||||
this._bottomPlaceholderHeight = 800;
|
this._bottomPlaceholderHeight = 800;
|
||||||
this._readers = [];
|
this._readers = [];
|
||||||
this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'reader');
|
this._notifierID = Zotero.Notifier.registerObserver(this, ['item', 'tab'], 'reader');
|
||||||
this.onChangeSidebarWidth = null;
|
this.onChangeSidebarWidth = null;
|
||||||
this.onChangeSidebarOpen = null;
|
this.onChangeSidebarOpen = null;
|
||||||
|
|
||||||
|
@ -682,47 +684,42 @@ class Reader {
|
||||||
reader.setBottomPlaceholderHeight(height);
|
reader.setBottomPlaceholderHeight(height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notify(event, type, ids, extraData) {
|
notify(event, type, ids, extraData) {
|
||||||
// Listen for the parent item, PDF attachment and its annotation items updates
|
if (type === 'tab') {
|
||||||
for (let readerWindow of this._readers) {
|
var reader = Zotero.Reader.getByTabID(ids[0]);
|
||||||
if (event === 'delete') {
|
if (reader) {
|
||||||
let disappearedIds = readerWindow.annotationItemIDs.filter(x => ids.includes(x));
|
this.triggerAnnotationsImportCheck(reader._itemID);
|
||||||
if (disappearedIds.length) {
|
|
||||||
let keys = disappearedIds.map(id => extraData[id].key);
|
|
||||||
readerWindow.unsetAnnotations(keys);
|
|
||||||
}
|
|
||||||
if (ids.includes(readerWindow._itemID)) {
|
|
||||||
readerWindow.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
let item = Zotero.Items.get(readerWindow._itemID);
|
else if (type === 'item') {
|
||||||
// TODO: Remove when fixed
|
// Listen for the parent item, PDF attachment and its annotations updates
|
||||||
item._loaded.childItems = true;
|
for (let reader of this._readers) {
|
||||||
let annotationItems = item.getAnnotations();
|
if (event === 'delete') {
|
||||||
readerWindow.annotationItemIDs = annotationItems.map(x => x.id);
|
let disappearedIds = reader.annotationItemIDs.filter(x => ids.includes(x));
|
||||||
let affectedAnnotationIds = annotationItems.filter(annotation => {
|
if (disappearedIds.length) {
|
||||||
let annotationID = annotation.id;
|
let keys = disappearedIds.map(id => extraData[id].key);
|
||||||
let imageAttachmentID = null;
|
reader.unsetAnnotations(keys);
|
||||||
annotation._loaded.childItems = true;
|
}
|
||||||
let annotationAttachments = annotation.getAttachments();
|
if (ids.includes(reader._itemID)) {
|
||||||
if (annotationAttachments.length) {
|
reader.close();
|
||||||
imageAttachmentID = annotationAttachments[0];
|
|
||||||
}
|
}
|
||||||
return (
|
|
||||||
ids.includes(annotationID) && !(extraData[annotationID]
|
|
||||||
&& extraData[annotationID].instanceID === readerWindow._instanceID)
|
|
||||||
|| ids.includes(imageAttachmentID) && !(extraData[imageAttachmentID]
|
|
||||||
&& extraData[imageAttachmentID].instanceID === readerWindow._instanceID)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
if (affectedAnnotationIds.length) {
|
|
||||||
readerWindow.setAnnotations(affectedAnnotationIds);
|
|
||||||
}
|
}
|
||||||
// Update title if the PDF attachment or the parent item changes
|
else {
|
||||||
if (ids.includes(readerWindow._itemID) || ids.includes(item.parentItemID)) {
|
let item = Zotero.Items.get(reader._itemID);
|
||||||
readerWindow.updateTitle();
|
let annotationItems = item.getAnnotations();
|
||||||
|
reader.annotationItemIDs = annotationItems.map(x => x.id);
|
||||||
|
let affectedAnnotations = annotationItems.filter(({ id }) => (
|
||||||
|
ids.includes(id)
|
||||||
|
&& !(extraData && extraData[id] && extraData[id].instanceID === reader._instanceID)
|
||||||
|
));
|
||||||
|
if (affectedAnnotations.length) {
|
||||||
|
reader.setAnnotations(affectedAnnotations);
|
||||||
|
}
|
||||||
|
// Update title if the PDF attachment or the parent item changes
|
||||||
|
if (ids.includes(reader._itemID) || ids.includes(item.parentItemID)) {
|
||||||
|
reader.updateTitle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -812,7 +809,7 @@ class Reader {
|
||||||
let item = await Zotero.Items.getAsync(itemID);
|
let item = await Zotero.Items.getAsync(itemID);
|
||||||
let mtime = await item.attachmentModificationTime;
|
let mtime = await item.attachmentModificationTime;
|
||||||
if (item.attachmentLastProcessedModificationTime < Math.floor(mtime / 1000)) {
|
if (item.attachmentLastProcessedModificationTime < Math.floor(mtime / 1000)) {
|
||||||
await Zotero.PDFWorker.import(itemID, false);
|
await Zotero.PDFWorker.import(itemID, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2745,7 +2745,6 @@ var ZoteroPane = new function()
|
||||||
'createParent',
|
'createParent',
|
||||||
'renameAttachments',
|
'renameAttachments',
|
||||||
'reindexItem',
|
'reindexItem',
|
||||||
'importAnnotations',
|
|
||||||
'createNoteFromAnnotations'
|
'createNoteFromAnnotations'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -2793,8 +2792,7 @@ var ZoteroPane = new function()
|
||||||
canIndex = true,
|
canIndex = true,
|
||||||
canRecognize = true,
|
canRecognize = true,
|
||||||
canUnrecognize = true,
|
canUnrecognize = true,
|
||||||
canRename = true,
|
canRename = true;
|
||||||
canImportAnnotations = true;
|
|
||||||
var canMarkRead = collectionTreeRow.isFeed();
|
var canMarkRead = collectionTreeRow.isFeed();
|
||||||
var markUnread = true;
|
var markUnread = true;
|
||||||
|
|
||||||
|
@ -2816,10 +2814,6 @@ var ZoteroPane = new function()
|
||||||
canUnrecognize = false;
|
canUnrecognize = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canImportAnnotations && !Zotero.PDFWorker.canImport(item)) {
|
|
||||||
canImportAnnotations = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show rename option only if all items are child attachments
|
// Show rename option only if all items are child attachments
|
||||||
if (canRename && (!item.isAttachment() || item.isTopLevelItem() || item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL)) {
|
if (canRename && (!item.isAttachment() || item.isTopLevelItem() || item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL)) {
|
||||||
canRename = false;
|
canRename = false;
|
||||||
|
@ -2899,9 +2893,6 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canImportAnnotations) {
|
|
||||||
show.push(m.importAnnotations);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Single item selected
|
// Single item selected
|
||||||
|
@ -2971,9 +2962,6 @@ var ZoteroPane = new function()
|
||||||
show.push(m.duplicateItem);
|
show.push(m.duplicateItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Zotero.PDFWorker.canImport(item)) {
|
|
||||||
show.push(m.importAnnotations);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Zotero.EditorInstance.canCreateNoteFromAnnotations(item)) {
|
if (Zotero.EditorInstance.canCreateNoteFromAnnotations(item)) {
|
||||||
show.push(m.createNoteFromAnnotations);
|
show.push(m.createNoteFromAnnotations);
|
||||||
|
@ -4579,17 +4567,6 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.importAnnotationsForSelected = async function () {
|
|
||||||
let items = ZoteroPane.getSelectedItems();
|
|
||||||
for (let item of items) {
|
|
||||||
if (item.isRegularItem()) {
|
|
||||||
Zotero.PDFWorker.importParent(item);
|
|
||||||
}
|
|
||||||
else if (item.isAttachment()) {
|
|
||||||
Zotero.PDFWorker.import(item.id, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.reportMetadataForSelected = async function () {
|
this.reportMetadataForSelected = async function () {
|
||||||
let items = ZoteroPane.getSelectedItems();
|
let items = ZoteroPane.getSelectedItems();
|
||||||
|
|
|
@ -308,10 +308,6 @@
|
||||||
<menuitem class="menuitem-iconic zotero-menuitem-create-parent" oncommand="ZoteroPane_Local.createParentItemsFromSelected();"/>
|
<menuitem class="menuitem-iconic zotero-menuitem-create-parent" oncommand="ZoteroPane_Local.createParentItemsFromSelected();"/>
|
||||||
<menuitem class="menuitem-iconic zotero-menuitem-rename-from-parent" oncommand="ZoteroPane_Local.renameSelectedAttachmentsFromParents()"/>
|
<menuitem class="menuitem-iconic zotero-menuitem-rename-from-parent" oncommand="ZoteroPane_Local.renameSelectedAttachmentsFromParents()"/>
|
||||||
<menuitem class="menuitem-iconic zotero-menuitem-reindex" oncommand="ZoteroPane_Local.reindexItem();"/>
|
<menuitem class="menuitem-iconic zotero-menuitem-reindex" oncommand="ZoteroPane_Local.reindexItem();"/>
|
||||||
<!-- <menuitem class="menuitem-iconic zotero-menuitem-import-annotations" label="&zotero.items.menu.importAnnotations;" oncommand="ZoteroPane.importAnnotationsForSelected()"/>-->
|
|
||||||
<menuitem class="menuitem-iconic zotero-menuitem-import-annotations" label="Import annotations" oncommand="ZoteroPane.importAnnotationsForSelected()"/>
|
|
||||||
<!-- <menuitem class="menuitem-iconic zotero-menuitem-export-annotations" label="&zotero.items.menu.exportAnnotations;" oncommand="ZoteroPane.exportAnnotationsForSelected()"/>-->
|
|
||||||
<menuitem class="menuitem-iconic zotero-menuitem-export-annotations" label="Export Annotations" oncommand="ZoteroPane.exportAnnotationsForSelected()"/>
|
|
||||||
<menuitem class="menuitem-iconic zotero-menuitem-create-note-from-annotations" label="&zotero.items.menu.createNoteFromAnnotations;" oncommand="ZoteroPane.createNoteFromSelected()"/>
|
<menuitem class="menuitem-iconic zotero-menuitem-create-note-from-annotations" label="&zotero.items.menu.createNoteFromAnnotations;" oncommand="ZoteroPane.createNoteFromSelected()"/>
|
||||||
</menupopup>
|
</menupopup>
|
||||||
|
|
||||||
|
|
10
resource/pdf-renderer/renderer.html
Normal file
10
resource/pdf-renderer/renderer.html
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>PDF Renderer</title>
|
||||||
|
<script src="resource://zotero/pdf-reader/pdf.js"></script>
|
||||||
|
<script src="renderer.js"></script>
|
||||||
|
</head>
|
||||||
|
<body></body>
|
||||||
|
</html>
|
118
resource/pdf-renderer/renderer.js
Normal file
118
resource/pdf-renderer/renderer.js
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
***** 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)));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderAnnotations(buf, annotations, send) {
|
||||||
|
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);
|
||||||
|
newCanvas.toBlob(async (blob) => {
|
||||||
|
let image = await new Response(blob).arrayBuffer();
|
||||||
|
send({ id: annotation.id, image });
|
||||||
|
}, 'image/png');
|
||||||
|
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.action === 'renderAnnotations') {
|
||||||
|
try {
|
||||||
|
let { buf, annotations } = message.data;
|
||||||
|
let num = await renderAnnotations(
|
||||||
|
buf,
|
||||||
|
annotations,
|
||||||
|
(annotation) => {
|
||||||
|
parent.postMessage({ action: 'renderedAnnotation', data: { annotation } }, parent.origin, [annotation.image]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
parent.postMessage({ responseId: message.id, data: num }, parent.origin);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
parent.postMessage({
|
||||||
|
responseId: message.id,
|
||||||
|
error: errObject(e)
|
||||||
|
}, parent.origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
parent.postMessage({ action: 'initialized' }, parent.origin);
|
||||||
|
}, 100);
|
Loading…
Add table
Add a link
Reference in a new issue