diff --git a/chrome/content/zotero/xpcom/data/item.js b/chrome/content/zotero/xpcom/data/item.js index 6b8c256b05..c37de5900b 100644 --- a/chrome/content/zotero/xpcom/data/item.js +++ b/chrome/content/zotero/xpcom/data/item.js @@ -3962,23 +3962,14 @@ Zotero.Item.prototype.getAnnotations = function (includeTrashed) { * Determine if the item is a PDF attachment that exists on disk and contains * embedded markup annotations. * - * @return {Promise} + * @return {Promise} Rejects if file does not exist on disk */ Zotero.Item.prototype.hasEmbeddedAnnotations = async function () { if (!this.isPDFAttachment()) { return false; } - let path = await this.getFilePathAsync(); - if (!path) { - return false; - } - - let contents = await Zotero.File.getContentsAsync(path); - // Check for "markup" annotations per the PDF spec - // https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf, p. 390 - let re = /\/Subtype\s*\/(Text|FreeText|Line|Square|Circle|Polygon|PolyLine|Highlight|Underline|Squiggly|StrikeOut|Stamp|Caret|Ink|FileAttachment|Sound|Redact)/; - return re.test(contents); + return Zotero.PDFWorker.hasAnnotations(this.id, true); }; diff --git a/chrome/content/zotero/xpcom/data/items.js b/chrome/content/zotero/xpcom/data/items.js index de59e35a04..e3c25bada5 100644 --- a/chrome/content/zotero/xpcom/data/items.js +++ b/chrome/content/zotero/xpcom/data/items.js @@ -1151,9 +1151,15 @@ Zotero.Items = function() { } // Check whether master and other have embedded annotations - if (await otherAttachment.hasEmbeddedAnnotations()) { + // Error -> be safe and assume the item does have embedded annotations + let logAndBeSafe = (e) => { + Zotero.logError(e); + return true; + }; + + if (await otherAttachment.hasEmbeddedAnnotations().catch(logAndBeSafe)) { // Other yes, master yes -> keep both - if (await masterAttachment.hasEmbeddedAnnotations()) { + if (await masterAttachment.hasEmbeddedAnnotations().catch(logAndBeSafe)) { Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key}, ` + 'but both have embedded annotations - keeping both'); otherAttachment.parentItemID = item.id; diff --git a/chrome/content/zotero/xpcom/pdfWorker/manager.js b/chrome/content/zotero/xpcom/pdfWorker/manager.js index a99b3100d9..c593d8266c 100644 --- a/chrome/content/zotero/xpcom/pdfWorker/manager.js +++ b/chrome/content/zotero/xpcom/pdfWorker/manager.js @@ -684,6 +684,47 @@ class PDFWorker { return result; }, isPriority); } + + /** + * Determine whether the PDF has any embedded annotations + * + * @param {Integer} itemID Attachment item id + * @param {Boolean} [isPriority] + * @param {String} [password] + * @returns {Promise} + */ + async hasAnnotations(itemID, isPriority, password) { + return this._enqueue(async () => { + let attachment = await Zotero.Items.getAsync(itemID); + + Zotero.debug(`Detecting embedded annotations in item ${attachment.libraryKey}`); + + if (!attachment.isPDFAttachment()) { + throw new Error('Item must be a PDF attachment'); + } + + let path = await attachment.getFilePathAsync(); + let buf = await OS.File.read(path, {}); + buf = new Uint8Array(buf).buffer; + + try { + var result = await this._query('hasAnnotations', { buf, password }, [buf]); + } + catch (e) { + let error = new Error(`Worker 'hasAnnotations' failed: ${JSON.stringify({ error: e.message })}`); + try { + error.name = JSON.parse(e.message).name; + } + catch (e) { + Zotero.logError(e); + } + Zotero.logError(error); + throw error; + } + + return result.hasAnnotations; + }, isPriority); + } } Zotero.PDFWorker = new PDFWorker();