diff --git a/chrome/content/zotero/xpcom/pdfExport.js b/chrome/content/zotero/xpcom/pdfExport.js deleted file mode 100644 index ad916b6f4c..0000000000 --- a/chrome/content/zotero/xpcom/pdfExport.js +++ /dev/null @@ -1,168 +0,0 @@ -class PDFExport { - constructor() { - this._queue = []; - this._queueProcessing = false; - this._processingItemID = null; - this._progressQueue = Zotero.ProgressQueues.create({ - id: 'pdf-export', - title: 'pdfExport.title', - columns: [ - 'recognizePDF.pdfName.label', - 'pdfImport.annotations.label' - ] - }); - - this._progressQueue.addListener('cancel', () => { - this._queue = []; - }); - } - - hasAnnotations(item) { - item._loaded.childItems = true; - return item.isAttachment() && item.getAnnotations().length; - } - - canExport(item) { - if (this.hasAnnotations(item)) { - return true; - } - else if (item.isRegularItem()) { - let ids = item.getAttachments(); - for (let id of ids) { - let attachment = Zotero.Items.get(id); - if (this.hasAnnotations(attachment)) { - return true; - } - } - } - - return false; - } - - /** - * Triggers queue processing and returns when all items in the queue are processed - * @return {Promise} - */ - async _processQueue() { - // await Zotero.Schema.schemaUpdatePromise; - - if (this._queueProcessing) return; - this._queueProcessing = true; - - while (1) { - let data = this._queue.pop(); - if (!data) break; - - let { itemID, path } = data; - - this._processingItemID = itemID; - - this._progressQueue.updateRow(itemID, Zotero.ProgressQueue.ROW_PROCESSING, Zotero.getString('general.processing')); - - try { - let item = await Zotero.Items.getAsync(itemID); - - if (!item) { - throw new Error(); - } - - let num = await this._exportItemAnnotations(item, path); - this._progressQueue.updateRow(itemID, Zotero.ProgressQueue.ROW_SUCCEEDED, num); - } - catch (e) { - Zotero.logError(e); - - this._progressQueue.updateRow( - itemID, - Zotero.ProgressQueue.ROW_FAILED, - e instanceof Zotero.Exception.Alert - ? e.message - : Zotero.getString('general.error') - ); - } - } - - this._queueProcessing = false; - this._processingItemID = null; - } - - /** - * Adds items to the queue and triggers processing - * @param {Zotero.Item[]} items - */ - async export(items) { - let pdfItems = []; - - if (!Array.isArray(items)) { - items = [items]; - } - - for (let item of items) { - if (this.hasAnnotations(item)) { - pdfItems.push(item); - } - else if (item.isRegularItem()) { - let ids = item.getAttachments(); - for (let id of ids) { - let attachment = Zotero.Items.get(id); - if (this.hasAnnotations(attachment)) { - pdfItems.push(attachment); - } - } - } - } - - for (let item of pdfItems) { - if ( - this._processingItemID === item.id || - this._queue.find(x => x.itemID === item.id) - ) { - continue; - } - this._queue.unshift({ itemID: item.id }); - this._progressQueue.addRow(item); - } - await this._processQueue(); - } - - async exportToPath(item, path, isPriority) { - if (isPriority) { - this._queue.push({ itemID: item.id, path }); - } - else { - this._queue.unshift({ itemID: item.id, path }); - } - this._progressQueue.addRow(item); - await this._processQueue(); - } - - async _exportItemAnnotations(item, path) { - let ids = item.getAnnotations(); - - let annotations = []; - for (let id of ids) { - try { - annotations.push(Zotero.Annotations.toJSON(Zotero.Items.get(id))); - } catch (e) { - Zotero.logError(e); - } - } - - annotations.id = annotations.key; - // annotations.image = annotations.imageURL; - - for (let annotation of annotations) { - delete annotation.key; - for (let key in annotation) { - annotation[key] = annotation[key] || ''; - } - - annotation.authorName = ''; - } - - await Zotero.PDFWorker.writeAnnotations(item.id, annotations, path); - return annotations.length; - } -} - -Zotero.PDFExport = new PDFExport(); diff --git a/chrome/content/zotero/xpcom/pdfImport.js b/chrome/content/zotero/xpcom/pdfImport.js deleted file mode 100644 index 7623a09ab2..0000000000 --- a/chrome/content/zotero/xpcom/pdfImport.js +++ /dev/null @@ -1,167 +0,0 @@ -// TODO: Import ToC - -class PdfImport { - constructor() { - this._queue = []; - this._queueProcessing = false; - this._processingItemID = null; - this._progressQueue = Zotero.ProgressQueues.create({ - id: 'pdf-import', - title: 'pdfImport.title', - columns: [ - 'recognizePDF.pdfName.label', - 'pdfImport.annotations.label' - ] - }); - - this._progressQueue.addListener('cancel', () => { - this._queue = []; - }); - } - - isPDFAttachment(item) { - return item.isAttachment() && item.attachmentContentType === 'application/pdf'; - } - - canImport(item) { - if (this.isPDFAttachment(item)) { - return true; - } - else if (item.isRegularItem()) { - let ids = item.getAttachments(); - for (let id of ids) { - let attachment = Zotero.Items.get(id); - if (this.isPDFAttachment(attachment)) { - return true; - } - } - } - }; - - /** - * Triggers queue processing and returns when all items in the queue are processed - * @return {Promise} - */ - async _processQueue() { - // await Zotero.Schema.schemaUpdatePromise; - - if (this._queueProcessing) return; - this._queueProcessing = true; - - while (1) { - let itemID = this._queue.pop(); - if (!itemID) break; - - this._processingItemID = itemID; - - this._progressQueue.updateRow(itemID, Zotero.ProgressQueue.ROW_PROCESSING, Zotero.getString('general.processing')); - - try { - let item = await Zotero.Items.getAsync(itemID); - - if (!item) { - throw new Error(); - } - - let num = await this._importItemAnnotations(item); - this._progressQueue.updateRow(itemID, Zotero.ProgressQueue.ROW_SUCCEEDED, num); - } - catch (e) { - Zotero.logError(e); - - this._progressQueue.updateRow( - itemID, - Zotero.ProgressQueue.ROW_FAILED, - e instanceof Zotero.Exception.Alert - ? e.message - : Zotero.getString('general.error') - ); - } - } - - this._queueProcessing = false; - this._processingItemID = null; - } - - /** - * Adds items to the queue and triggers processing - * @param {Zotero.Item[]} items - */ - async import(items, isPriority) { - let pdfItems = []; - - if (!Array.isArray(items)) { - items = [items]; - } - - for (let item of items) { - if (this.isPDFAttachment(item)) { - pdfItems.push(item); - } - else if (item.isRegularItem()) { - let ids = item.getAttachments(); - for (let id of ids) { - let attachment = Zotero.Items.get(id); - if (this.isPDFAttachment(attachment)) { - pdfItems.push(attachment); - } - } - } - } - - for (let item of pdfItems) { - if ( - this._processingItemID === item.id || - this._queue.includes(item.id) - ) { - continue; - } - this._queue.unshift(item.id); - this._progressQueue.addRow(item); - } - await this._processQueue(); - } - - similarAnnotions(annotation1, annotation2) { - return (annotation1.position.pageIndex === annotation2.position.pageIndex && - JSON.stringify(annotation1.position.rects) === JSON.stringify(annotation2.position.rects)); - } - - async _importItemAnnotations(item) { - if (!item.isAttachment() || item.attachmentContentType !== 'application/pdf') { - throw new Error('Not a valid PDF attachment'); - } - - // TODO: Remove when fixed - item._loaded.childItems = true; - let ids = item.getAnnotations(); - let existingAnnotations = []; - for (let id of ids) { - try { - existingAnnotations.push(Zotero.Annotations.toJSON(Zotero.Items.get(id))); - } catch (e) { - Zotero.logError(e); - } - } - - let annotations = await Zotero.PDFWorker.readAnnotations(item.id); - annotations = annotations.filter(x => ['highlight', 'note'].includes(x.type)); - - let num = 0; - for (let annotation of annotations) { - annotation.comment = annotation.comment || ''; - if (existingAnnotations.some(existingAnnotation => this.similarAnnotions(existingAnnotation, annotation))) { - continue; - } - - // TODO: Utilize the saved Zotero item key for deduplication - annotation.key = Zotero.DataObjectUtilities.generateKey(); - let annotationItem = await Zotero.Annotations.saveFromJSON(item, annotation); - num++; - } - - return num; - } -} - -Zotero.PDFImport = new PdfImport(); diff --git a/chrome/content/zotero/xpcom/pdfWorker/manager.js b/chrome/content/zotero/xpcom/pdfWorker/manager.js new file mode 100644 index 0000000000..fa7b6c1d33 --- /dev/null +++ b/chrome/content/zotero/xpcom/pdfWorker/manager.js @@ -0,0 +1,283 @@ +/* + ***** BEGIN LICENSE BLOCK ***** + + Copyright © 2020 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 . + + ***** END LICENSE BLOCK ***** +*/ + +const WORKER_URL = 'chrome://zotero/content/xpcom/pdfWorker/worker.js'; +const CMAPS_URL = 'resource://zotero/pdf-reader/cmaps/'; + +class PDFWorker { + constructor() { + this._worker = null; + this._lastPromiseID = 0; + this._waitingPromises = {}; + this._queue = []; + this._processingQueue = false; + } + + async _processQueue() { + 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._worker.postMessage({ id: this._lastPromiseID, action, data }, transfer); + }); + } + + _init() { + if (this._worker) return; + this._worker = new Worker(WORKER_URL); + this._worker.addEventListener('message', async (event) => { + let message = event.data; + // console.log(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.id) { + let respData = null; + try { + if (message.action === 'FetchBuiltInCMap') { + let response = await Zotero.HTTP.request( + 'GET', + CMAPS_URL + event.data.data.name + '.bcmap', + { responseType: 'arraybuffer' } + ); + respData = { + compressionType: 1, + cMapData: new Uint8Array(response.response) + }; + } + } + catch (e) { + Zotero.debug('Failed to fetch CMap data:'); + Zotero.debug(e); + } + this._worker.postMessage({ responseId: event.data.id, data: respData }); + } + }); + this._worker.addEventListener('error', (event) => { + Zotero.debug('PDF Web Worker error:'); + Zotero.debug(event); + }); + } + + isPDFAttachment(item) { + return item.isAttachment() && item.attachmentContentType === 'application/pdf'; + } + + canImport(item) { + if (this.isPDFAttachment(item)) { + return true; + } + else if (item.isRegularItem()) { + let ids = item.getAttachments(); + for (let id of ids) { + let attachment = Zotero.Items.get(id); + if (this.isPDFAttachment(attachment)) { + return true; + } + } + } + } + + /** + * Export attachment with annotations to specified path + * + * @param {Integer} itemID + * @param {String} path + * @param {Boolean} isPriority + * @param {String} password + * @returns {Promise} Number of written annotations + */ + async export(itemID, path, isPriority, password) { + return this._enqueue(async () => { + let attachment = await Zotero.Items.getAsync(itemID); + if (!this.isPDFAttachment(attachment)) { + throw new Error('not a valid attachment'); + } + let ids = attachment.getAnnotations(); + let annotations = []; + for (let id of ids) { + let item = await Zotero.Items.getAsync(id); + annotations.push({ + id: item.key, + type: item.annotationType, + authorName: Zotero.Users.getName(item.createdByUserID) || '', + comment: item.annotationComment || '', + color: item.annotationColor, + position: JSON.parse(item.annotationPosition), + dateModified: item.dateModified + }); + } + let attachmentPath = await attachment.getFilePath(); + let buf = await OS.File.read(attachmentPath, {}); + buf = new Uint8Array(buf).buffer; + let res = await this._query('export', { buf, annotations, password }, [buf]); + await OS.File.writeAtomic(path, new Uint8Array(res.buf)); + return annotations.length; + }); + } + + /** + * Export children PDF attachments with annotations + * + * @param {Zotero.Item} item + * @param {String} directory + */ + async exportParent(item, directory) { + if (!item.isRegularItem()) { + throw new Error('regular item not provided'); + } + if (!directory) { + throw new Error('\'directory\' not provided'); + } + let promises = []; + let ids = item.getAttachments(); + for (let id of ids) { + let attachment = Zotero.Items.get(id); + if (this.isPDFAttachment(attachment)) { + let path = OS.Path.join(directory, attachment.attachmentFilename); + promises.push(this.export(id, path)); + } + } + await Promise.all(promises); + } + + /** + * Import annotations from PDF attachment + * + * @param {Integer} itemID + * @param {Boolean} save Save imported annotations, or otherwise just return the number of importable annotations + * @param {Boolean} isPriority + * @param {String} password + * @returns {Promise} Number of annotations + */ + async import(itemID, save, isPriority, password) { + return this._enqueue(async () => { + let attachment = await Zotero.Items.getAsync(itemID); + if (!this.isPDFAttachment(attachment)) { + throw new Error('not a valid PDF attachment'); + } + // TODO: Remove when fixed + attachment._loaded.childItems = true; + let ids = attachment.getAnnotations(); + let existingAnnotations = []; + for (let id of ids) { + let item = await Zotero.Items.getAsync(id); + existingAnnotations.push({ + id: item.key, + type: item.annotationType, + comment: item.annotationComment || '', + position: JSON.parse(item.annotationPosition) + }); + } + let path = await attachment.getFilePath(); + let buf = await OS.File.read(path, {}); + buf = new Uint8Array(buf).buffer; + let res = await this._query('import', { buf, existingAnnotations, password }, [buf]); + let annotations = res.annotations; + if (save) { + for (let annotation of annotations) { + // TODO: Utilize the saved Zotero item key for deduplication. Newer annotation modificaiton date wins + annotation.key = Zotero.DataObjectUtilities.generateKey(); + await Zotero.Annotations.saveFromJSON(attachment, annotation); + } + Zotero.PDF.hasUnmachedAnnotations[itemID] = false; + } + else { + Zotero.PDF.hasUnmachedAnnotations[itemID] = !!annotations.length; + } + for (let readerWindow of Zotero.Reader._readerWindows) { + if (readerWindow._itemID === itemID) { + readerWindow.toggleImportPrompt(!!Zotero.PDF.hasUnmachedAnnotations[itemID]); + } + } + Zotero.PDF.dateChecked[itemID] = Zotero.Date.dateToISO(new Date()); + return annotations.length; + }); + } + + /** + * Import children PDF attachment annotations + * + * @param {Zotero.Item} item + */ + async importParent(item) { + if (!item.isRegularItem()) { + throw new Error('regular item not provided'); + } + let promises = []; + let ids = item.getAttachments(); + for (let id of ids) { + let attachment = Zotero.Items.get(id); + if (this.isPDFAttachment(attachment)) { + promises.push(this.import(id, true)); + } + } + await Promise.all(promises); + } +} + +Zotero.PDFWorker = new PDFWorker(); diff --git a/chrome/content/zotero/xpcom/pdfWorker/transport.js b/chrome/content/zotero/xpcom/pdfWorker/transport.js deleted file mode 100644 index 3ae1ec8bf5..0000000000 --- a/chrome/content/zotero/xpcom/pdfWorker/transport.js +++ /dev/null @@ -1,106 +0,0 @@ - -const CMAPS_URL = 'resource://zotero/pdf-reader/cmaps/'; - -class PDFWorker { - constructor() { - this.worker = null; - this.promiseId = 0; - this.waitingPromises = {}; - } - - async query(op, data, transfer) { - return new Promise((resolve, reject) => { - this.promiseId++; - this.waitingPromises[this.promiseId] = {resolve, reject}; - this.worker.postMessage({id: this.promiseId, op, data}, transfer); - }); - } - - init() { - if (this.worker) return; - this.worker = new Worker('chrome://zotero/content/xpcom/pdfWorker/worker.js'); - - this.worker.addEventListener('message', async e => { - let message = e.data; - // console.log(e.data) - if (message.responseId) { - let { resolve, reject } = this.waitingPromises[message.responseId]; - if (message.data) { - resolve(message.data); - } - else { - reject(message.error); - } - return; - } - - if (message.id) { - let respData = null; - - try { - if (message.op === 'FetchBuiltInCMap') { - let response = await Zotero.HTTP.request( - "GET", - CMAPS_URL + e.data.data.name + '.bcmap', - {responseType: 'arraybuffer'} - ); - respData = { - compressionType: 1, - cMapData: new Uint8Array(response.response) - }; - } - } - catch (e) { - Zotero.debug('Failed to fetch CMap data:'); - Zotero.debug(e); - } - this.worker.postMessage({responseId: e.data.id, data: respData}); - } - }); - - this.worker.addEventListener('error', e => { - Zotero.debug('PDF Web Worker error:'); - Zotero.debug(e); - }); - } - - async writeAnnotations(itemID, annotations, path) { - Zotero.debug("Writing annotations"); - this.init(); - - - let password = ''; - - let item = await Zotero.Items.getAsync(itemID); - let itemFilePath = await item.getFilePath(); - - let buf = await OS.File.read(itemFilePath, {}); - buf = new Uint8Array(buf).buffer; - - let res = await this.query('write', {buf, annotations, password}, [buf]); - - if (!path) { - path = itemFilePath; - } - - await OS.File.writeAtomic(path, new Uint8Array(res.buf)); - } - - async readAnnotations(itemID) { - this.init(); - - let password = ''; - - let item = await Zotero.Items.getAsync(itemID); - let path = await item.getFilePath(); - - let buf = await OS.File.read(path, {}); - buf = new Uint8Array(buf).buffer; - - let res = await this.query('read', {buf, password}, [buf]); - - return res.annotations; - } -} - -Zotero.PDFWorker = new PDFWorker(); diff --git a/chrome/content/zotero/xpcom/reader.js b/chrome/content/zotero/xpcom/reader.js index aacbc2a072..c1addaab5a 100644 --- a/chrome/content/zotero/xpcom/reader.js +++ b/chrome/content/zotero/xpcom/reader.js @@ -1,8 +1,12 @@ -let PDFStates = {}; +// Temporary stuff +Zotero.PDF = { + dateChecked: {}, + hasUnmachedAnnotations: {} +}; class ReaderWindow { constructor() { - this.annotationItemIds = []; + this.annotationItemIDs = []; this._instanceID = Zotero.Utilities.randomString(); this._window = null; this._iframeWindow = null; @@ -115,7 +119,8 @@ class ReaderWindow { state, location, enablePrev: !!this._prevHistory.length, - enableNext: !!this._nextHistory.length + enableNext: !!this._nextHistory.length, + promptImport: !!Zotero.PDF.hasUnmachedAnnotations[this._itemID] }, [buf]); return true; } @@ -156,6 +161,10 @@ class ReaderWindow { this._postMessage({ action: 'navigate', location }); } + toggleImportPrompt(enable) { + this._postMessage({ action: 'toggleImportPrompt', enable }); + } + close() { this._window.close(); } @@ -337,7 +346,7 @@ class ReaderWindow { let annotation = Zotero.Items.getByLibraryAndKey(libraryID, key); // A small check, as we are receiving a list of item keys from a less secure code if (annotation && annotation.isAnnotation() && annotation.parentID === this._itemID) { - this.annotationItemIds = this.annotationItemIds.filter(id => id !== annotation.id); + this.annotationItemIDs = this.annotationItemIDs.filter(id => id !== annotation.id); await annotation.eraseTx(); } } @@ -380,8 +389,7 @@ class ReaderWindow { } case 'import': { Zotero.debug('Importing PDF annotations'); - let item = Zotero.Items.get(this._itemID); - Zotero.PDFImport.import(item); + Zotero.PDFWorker.import(this._itemID, true, true); return; } case 'importDismiss': { @@ -534,7 +542,7 @@ class Reader { // Listen for the parent item, PDF attachment and its annotation items updates for (let readerWindow of this._readerWindows) { if (event === 'delete') { - let disappearedIds = readerWindow.annotationItemIds.filter(x => ids.includes(x)); + let disappearedIds = readerWindow.annotationItemIDs.filter(x => ids.includes(x)); if (disappearedIds.length) { let keys = disappearedIds.map(id => extraData[id].key); readerWindow.unsetAnnotations(keys); @@ -547,9 +555,9 @@ class Reader { let item = Zotero.Items.get(readerWindow._itemID); // TODO: Remove when fixed item._loaded.childItems = true; - let annotationItemIds = item.getAnnotations(); - readerWindow.annotationItemIds = annotationItemIds; - let affectedAnnotationIds = annotationItemIds.filter(annotationID => { + let annotationItemIDs = item.getAnnotations(); + readerWindow.annotationItemIDs = annotationItemIDs; + let affectedAnnotationIds = annotationItemIDs.filter(annotationID => { let annotation = Zotero.Items.get(annotationID); let imageAttachmentID = null; annotation._loaded.childItems = true; @@ -586,6 +594,7 @@ class Reader { } async open(itemID, location) { + this.triggerAnnotationsImportCheck(itemID); let reader = this._getReaderWindow(itemID); if (reader) { if (location) { @@ -605,6 +614,15 @@ class Reader { } reader._window.focus(); } + + async triggerAnnotationsImportCheck(itemID) { + let item = await Zotero.Items.getAsync(itemID); + let mtime = await item.attachmentModificationTime; + let dateModified = Zotero.Date.dateToISO(new Date(mtime)); + if (!Zotero.PDF.dateChecked[itemID] || Zotero.PDF.dateChecked[itemID] < dateModified) { + await Zotero.PDFWorker.import(itemID, false); + } + } } Zotero.Reader = new Reader(); diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index 6ec39af8d2..ee9d1481c0 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -2746,8 +2746,7 @@ var ZoteroPane = new function() 'createParent', 'renameAttachments', 'reindexItem', - 'importAnnotations', - 'exportAnnotations' + 'importAnnotations' ]; var m = {}; @@ -2795,8 +2794,7 @@ var ZoteroPane = new function() canRecognize = true, canUnrecognize = true, canRename = true, - canImportAnnotations = true, - canExportAnnotations = true; + canImportAnnotations = true; var canMarkRead = collectionTreeRow.isFeed(); var markUnread = true; @@ -2818,14 +2816,10 @@ var ZoteroPane = new function() canUnrecognize = false; } - if (canImportAnnotations && !Zotero.PDFImport.canImport(item)) { + if (canImportAnnotations && !Zotero.PDFWorker.canImport(item)) { canImportAnnotations = false; } - if (canExportAnnotations && !Zotero.PDFExport.canExport(item)) { - canExportAnnotations = false; - } - // Show rename option only if all items are child attachments if (canRename && (!item.isAttachment() || item.isTopLevelItem() || item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL)) { canRename = false; @@ -2908,10 +2902,6 @@ var ZoteroPane = new function() if (canImportAnnotations) { show.push(m.importAnnotations); } - - if (canExportAnnotations) { - show.push(m.exportAnnotations); - } } // Single item selected @@ -2981,13 +2971,9 @@ var ZoteroPane = new function() show.push(m.duplicateItem); } - if (Zotero.PDFImport.canImport(item)) { + if (Zotero.PDFWorker.canImport(item)) { show.push(m.importAnnotations); } - - if (Zotero.PDFExport.canExport(item)) { - show.push(m.exportAnnotations); - } } // Update attachment submenu @@ -4578,16 +4564,16 @@ var ZoteroPane = new function() } }; - this.exportAnnotationsForSelected = async function () { - var items = ZoteroPane.getSelectedItems(); - Zotero.PDFExport.export(items); - Zotero.ProgressQueues.get('pdf-export').getDialog().open(); - }; - this.importAnnotationsForSelected = async function () { - var items = ZoteroPane.getSelectedItems(); - Zotero.PDFImport.import(items); - Zotero.ProgressQueues.get('pdf-import').getDialog().open(); + 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 () { @@ -4709,7 +4695,7 @@ var ZoteroPane = new function() var rv = await fp.show(); if (rv === fp.returnOK || rv === fp.returnReplace) { let outputFile = fp.file; - Zotero.PDFExport.exportToPath(item, outputFile, true); + await Zotero.PDFWorker.export(item.id, outputFile, true); } }; diff --git a/chrome/content/zotero/zoteroPane.xul b/chrome/content/zotero/zoteroPane.xul index 323ec585e2..d84fbd6e44 100644 --- a/chrome/content/zotero/zoteroPane.xul +++ b/chrome/content/zotero/zoteroPane.xul @@ -292,10 +292,7 @@ - - - - + diff --git a/chrome/locale/en-US/zotero/zotero.dtd b/chrome/locale/en-US/zotero/zotero.dtd index e4647b4d16..6fd497f201 100644 --- a/chrome/locale/en-US/zotero/zotero.dtd +++ b/chrome/locale/en-US/zotero/zotero.dtd @@ -100,7 +100,6 @@ - diff --git a/chrome/locale/en-US/zotero/zotero.properties b/chrome/locale/en-US/zotero/zotero.properties index 68a89f87df..527702cdbb 100644 --- a/chrome/locale/en-US/zotero/zotero.properties +++ b/chrome/locale/en-US/zotero/zotero.properties @@ -1131,11 +1131,6 @@ recognizePDF.reportMetadata = Report Incorrect Metadata recognizePDF.pdfName.label = PDF Name recognizePDF.itemName.label = Item Name -pdfExport.title = PDF Annotations Export - -pdfImport.title = PDF Annotations Import -pdfImport.annotations.label = Annotations - rtfScan.openTitle = Select a file to scan rtfScan.scanning.label = Scanning RTF Document… rtfScan.saving.label = Formatting RTF Document… diff --git a/chrome/skin/default/zotero/overlay.css b/chrome/skin/default/zotero/overlay.css index b619c3a3de..0a5835f484 100644 --- a/chrome/skin/default/zotero/overlay.css +++ b/chrome/skin/default/zotero/overlay.css @@ -620,24 +620,6 @@ margin-top: 1px; } -#zotero-tb-pq-pdf-export { - list-style-image: url(chrome://zotero/skin/pdf-search.png); -} - -#zotero-tb-pq-pdf-export .toolbarbutton-icon { - width: 18px; - margin-top: 1px; -} - -#zotero-tb-pq-pdf-import { - list-style-image: url(chrome://zotero/skin/pdf-search.png); -} - -#zotero-tb-pq-pdf-import .toolbarbutton-icon { - width: 18px; - margin-top: 1px; -} - /* Sync error icon */ #zotero-tb-sync-error { list-style-image: url(chrome://zotero/skin/error.png); diff --git a/components/zotero-service.js b/components/zotero-service.js index 63ded69215..5147925321 100644 --- a/components/zotero-service.js +++ b/components/zotero-service.js @@ -47,7 +47,7 @@ const xpcomFilesAll = [ 'http', 'mimeTypeHandler', 'openurl', - 'pdfWorker/transport', + 'pdfWorker/manager', 'ipc', 'profile', 'progressWindow', @@ -113,8 +113,6 @@ const xpcomFilesLocal = [ 'progressQueueDialog', 'quickCopy', 'recognizePDF', - 'pdfExport', - 'pdfImport', 'report', 'retractions', 'router', diff --git a/pdf-worker b/pdf-worker index 20b9f8f7a1..9067fc6a92 160000 --- a/pdf-worker +++ b/pdf-worker @@ -1 +1 @@ -Subproject commit 20b9f8f7a197b809c310db0c94bc7a5d22ed9bd0 +Subproject commit 9067fc6a9245019b0a4670f8a2b5d81f9f36ad0f