From cab0fa93e7ac0ee02966300999d911c3add2b756 Mon Sep 17 00:00:00 2001 From: Abe Jellinek Date: Sun, 6 Aug 2023 16:33:02 -0400 Subject: [PATCH] Extend Retrieve Metadata to support EPUBs (#57) --- chrome/content/zotero/collectionTree.jsx | 4 +- chrome/content/zotero/import/folderImport.js | 4 +- chrome/content/zotero/itemTree.jsx | 4 +- .../xpcom/connector/server_connector.js | 6 +- chrome/content/zotero/xpcom/epub.js | 135 ++++++ chrome/content/zotero/xpcom/fulltext.js | 63 +-- .../{recognizePDF.js => recognizeDocument.js} | 136 ++++-- .../zotero/xpcom/translation/translators.js | 1 + chrome/content/zotero/zoteroPane.js | 16 +- chrome/locale/en-US/zotero/preferences.dtd | 2 +- chrome/locale/en-US/zotero/zotero.properties | 9 +- components/zotero-service.js | 3 +- defaults/preferences/zotero.js | 2 +- test/tests/data/recognizeEPUB_test_DC.epub | Bin 0 -> 46649 bytes test/tests/data/recognizeEPUB_test_ISBN.epub | Bin 0 -> 46667 bytes test/tests/recognizeDocumentTest.js | 447 ++++++++++++++++++ test/tests/recognizePDFTest.js | 293 ------------ 17 files changed, 723 insertions(+), 402 deletions(-) create mode 100644 chrome/content/zotero/xpcom/epub.js rename chrome/content/zotero/xpcom/{recognizePDF.js => recognizeDocument.js} (80%) create mode 100644 test/tests/data/recognizeEPUB_test_DC.epub create mode 100644 test/tests/data/recognizeEPUB_test_ISBN.epub create mode 100644 test/tests/recognizeDocumentTest.js delete mode 100644 test/tests/recognizePDFTest.js diff --git a/chrome/content/zotero/collectionTree.jsx b/chrome/content/zotero/collectionTree.jsx index 480f18d5af..98d156ef0e 100644 --- a/chrome/content/zotero/collectionTree.jsx +++ b/chrome/content/zotero/collectionTree.jsx @@ -2004,8 +2004,8 @@ var CollectionTree = class CollectionTree extends LibraryTree { addedItems.push(item); } - // Automatically retrieve metadata for PDFs - Zotero.RecognizePDF.autoRecognizeItems(addedItems); + // Automatically retrieve metadata for PDFs and ebooks + Zotero.RecognizeDocument.autoRecognizeItems(addedItems); } } diff --git a/chrome/content/zotero/import/folderImport.js b/chrome/content/zotero/import/folderImport.js index 26f7c6ac16..819fc75110 100644 --- a/chrome/content/zotero/import/folderImport.js +++ b/chrome/content/zotero/import/folderImport.js @@ -191,7 +191,7 @@ class Zotero_Import_Folder { // eslint-disable-line camelcase,no-unused-vars } } - if (attachmentItem && !Zotero.RecognizePDF.canRecognize(attachmentItem)) { + if (attachmentItem && !Zotero.RecognizeDocument.canRecognize(attachmentItem)) { // @TODO: store hash of an item that cannot be recognized await attachmentItem.saveTx({ skipSelect: true }); attachmentItem = null; @@ -234,7 +234,7 @@ class Zotero_Import_Folder { // eslint-disable-line camelcase,no-unused-vars recognizeQueue.addListener('rowupdated', processRecognizedItem); try { - await Zotero.RecognizePDF.recognizeItems(recognizableItems); + await Zotero.RecognizeDocument.recognizeItems(recognizableItems); } finally { recognizeQueue.removeListener('rowupdated', processRecognizedItem); diff --git a/chrome/content/zotero/itemTree.jsx b/chrome/content/zotero/itemTree.jsx index 69dced5680..1bf7f8fc81 100644 --- a/chrome/content/zotero/itemTree.jsx +++ b/chrome/content/zotero/itemTree.jsx @@ -2572,9 +2572,9 @@ var ItemTree = class ItemTree extends LibraryTree { await Zotero.Notifier.commit(notifierQueue); } - // Automatically retrieve metadata for PDFs + // Automatically retrieve metadata for PDFs and ebooks if (!parentItemID) { - Zotero.RecognizePDF.autoRecognizeItems(addedItems); + Zotero.RecognizeDocument.autoRecognizeItems(addedItems); } } }; diff --git a/chrome/content/zotero/xpcom/connector/server_connector.js b/chrome/content/zotero/xpcom/connector/server_connector.js index 34ae05c00e..4d5a94b332 100644 --- a/chrome/content/zotero/xpcom/connector/server_connector.js +++ b/chrome/content/zotero/xpcom/connector/server_connector.js @@ -333,7 +333,7 @@ Zotero.Server.Connector.SaveSession.prototype._updateItems = Zotero.serial(async this._items.add(newItem); } - // If the item is now a child item (e.g., from Retrieve Metadata for PDF), update the + // If the item is now a child item (e.g., from Retrieve Metadata), update the // parent item instead if (!item.isTopLevelItem()) { item = item.parentItem; @@ -1180,8 +1180,8 @@ Zotero.Server.Connector.SaveSnapshot.prototype = { cookieSandbox }); - // Automatically recognize PDF - Zotero.RecognizePDF.autoRecognizeItems([item]); + // Automatically recognize PDF/EPUB + Zotero.RecognizeDocument.autoRecognizeItems([item]); return item; } diff --git a/chrome/content/zotero/xpcom/epub.js b/chrome/content/zotero/xpcom/epub.js new file mode 100644 index 0000000000..7508a6fbfc --- /dev/null +++ b/chrome/content/zotero/xpcom/epub.js @@ -0,0 +1,135 @@ +/* + ***** BEGIN LICENSE BLOCK ***** + + Copyright © 2023 Corporation for Digital Scholarship + Vienna, Virginia, USA + https://www.zotero.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 ZipReader = Components.Constructor( + "@mozilla.org/libjar/zip-reader;1", + "nsIZipReader", + "open" +); + +Zotero.EPUB = { + async* getSectionDocuments(epubPath) { + let zipReader = new ZipReader(Zotero.File.pathToFile(epubPath)); + let contentOPFDoc = await this._getContentOPF(zipReader); + let manifest = contentOPFDoc.documentElement.querySelector(':scope > manifest'); + let spine = contentOPFDoc.documentElement.querySelector(':scope > spine'); + if (!manifest || !spine) { + throw new Error('content.opf does not contain and '); + } + + let idToHref = new Map(); + for (let manifestItem of manifest.querySelectorAll(':scope > item')) { + if (!manifestItem.hasAttribute('id') + || !manifestItem.hasAttribute('href') + || manifestItem.getAttribute('media-type') !== 'application/xhtml+xml') { + continue; + } + idToHref.set(manifestItem.getAttribute('id'), manifestItem.getAttribute('href')); + } + + for (let spineItem of spine.querySelectorAll('itemref')) { + let id = spineItem.getAttribute('idref'); + let href = idToHref.get(id); + if (!href || !zipReader.hasEntry(href)) { + continue; + } + let entryStream = zipReader.getInputStream(href); + let doc; + try { + doc = await this._parseStreamToDocument(entryStream, 'application/xhtml+xml'); + } + finally { + entryStream.close(); + } + + yield { href, doc }; + } + }, + + async getMetadataRDF(epubPath) { + const DC_NS = 'http://purl.org/dc/elements/1.1/'; + const OPF_NS = 'http://www.idpf.org/2007/opf'; + + let zipReader = new ZipReader(Zotero.File.pathToFile(epubPath)); + let doc = await this._getContentOPF(zipReader); + let metadata = doc.documentElement.querySelector(':scope > metadata'); + + if (!metadata.getAttribute('xmlns')) { + metadata.setAttribute('xmlns', doc.documentElement.namespaceURI || ''); + } + + for (let elem of metadata.querySelectorAll('*')) { + for (let attr of Array.from(elem.attributes)) { + // Null- and unknown-namespace attributes cause rdf.js to ignore the entire element + // (Why?) + if (attr.namespaceURI === null || attr.namespaceURI === OPF_NS) { + elem.removeAttributeNode(attr); + } + } + } + + // If the metadata doesn't contain a dc:type, add one + if (!metadata.getElementsByTagNameNS(DC_NS, 'type').length) { + let dcType = doc.createElementNS(DC_NS, 'type'); + dcType.textContent = 'book'; + metadata.appendChild(dcType); + } + + return new XMLSerializer().serializeToString(metadata); + }, + + /** + * @param {ZipReader} zipReader + * @return {Promise} + */ + async _getContentOPF(zipReader) { + if (!zipReader.hasEntry('META-INF/container.xml')) { + throw new Error('EPUB file does not contain container.xml'); + } + + let containerXMLStream = zipReader.getInputStream('META-INF/container.xml'); + let containerXMLDoc = await this._parseStreamToDocument(containerXMLStream, 'text/xml'); + containerXMLStream.close(); + + let rootFile = containerXMLDoc.documentElement.querySelector(':scope > rootfiles > rootfile'); + if (!rootFile || !rootFile.hasAttribute('full-path')) { + throw new Error('container.xml does not contain '); + } + + let contentOPFStream = zipReader.getInputStream(rootFile.getAttribute('full-path')); + try { + return await this._parseStreamToDocument(contentOPFStream, 'text/xml'); + } + finally { + contentOPFStream.close(); + } + }, + + async _parseStreamToDocument(stream, type) { + let parser = new DOMParser(); + let xml = await Zotero.File.getContentsAsync(stream); + return parser.parseFromString(xml, type); + } +}; diff --git a/chrome/content/zotero/xpcom/fulltext.js b/chrome/content/zotero/xpcom/fulltext.js index cecf3db40a..58a61ba12e 100644 --- a/chrome/content/zotero/xpcom/fulltext.js +++ b/chrome/content/zotero/xpcom/fulltext.js @@ -23,12 +23,6 @@ ***** END LICENSE BLOCK ***** */ -const ZipReader = Components.Constructor( - "@mozilla.org/libjar/zip-reader;1", - "nsIZipReader", - "open" -); - Zotero.Fulltext = Zotero.FullText = new function(){ this.__defineGetter__("fulltextCacheFile", function () { return '.zotero-ft-cache'; }); @@ -418,60 +412,16 @@ Zotero.Fulltext = Zotero.FullText = new function(){ let maxLength = Zotero.Prefs.get('fulltext.textMaxLength'); let item = await Zotero.Items.getAsync(itemID); - let zipReader = new ZipReader(Zotero.File.pathToFile(filePath)); try { - if (!zipReader.hasEntry('META-INF/container.xml')) { - Zotero.debug('EPUB file does not contain container.xml', 2); - return false; - } - - let containerXMLStream = zipReader.getInputStream('META-INF/container.xml'); - let containerXMLDoc = await parseStreamToDocument(containerXMLStream, 'text/xml'); - containerXMLStream.close(); - - let rootFile = containerXMLDoc.documentElement.querySelector(':scope > rootfiles > rootfile'); - if (!rootFile || !rootFile.hasAttribute('full-path')) { - Zotero.debug('container.xml does not contain ', 2); - return false; - } - - let contentOPFStream = zipReader.getInputStream(rootFile.getAttribute('full-path')); - let contentOPFDoc = await parseStreamToDocument(contentOPFStream, 'text/xml'); - contentOPFStream.close(); - - let manifest = contentOPFDoc.documentElement.querySelector(':scope > manifest'); - let spine = contentOPFDoc.documentElement.querySelector(':scope > spine'); - if (!manifest || !spine) { - Zotero.debug('content.opf does not contain and ', 2); - return false; - } - - let idToHref = new Map(); - for (let manifestItem of manifest.querySelectorAll(':scope > item')) { - if (!manifestItem.hasAttribute('id') - || !manifestItem.hasAttribute('href') - || manifestItem.getAttribute('media-type') !== 'application/xhtml+xml') { - continue; - } - idToHref.set(manifestItem.getAttribute('id'), manifestItem.getAttribute('href')); - } - let text = ''; let totalChars = 0; - for (let spineItem of spine.querySelectorAll('itemref')) { - let id = spineItem.getAttribute('idref'); - let href = idToHref.get(id); - if (!href || !zipReader.hasEntry(href)) { + for await (let { href, doc } of Zotero.EPUB.getSectionDocuments(filePath)) { + if (!doc.body) { + Zotero.debug(`Skipping EPUB entry '${href}' with no body`); continue; } - let entryStream = zipReader.getInputStream(href); - let entryDoc = await parseStreamToDocument(entryStream, 'application/xhtml+xml'); - entryStream.close(); - if (!entryDoc.body) { - Zotero.debug(`Skipping EPUB entry '${href}' with no body`); - } - let bodyText = entryDoc.body.innerText; + let bodyText = doc.body.innerText; totalChars += bodyText.length; if (!allText) { bodyText = bodyText.substring(0, maxLength - text.length); @@ -483,8 +433,9 @@ Zotero.Fulltext = Zotero.FullText = new function(){ await indexString(text, itemID, { indexedChars: text.length, totalChars }); return true; } - finally { - zipReader.close(); + catch (e) { + Zotero.logError(e); + return false; } }; diff --git a/chrome/content/zotero/xpcom/recognizePDF.js b/chrome/content/zotero/xpcom/recognizeDocument.js similarity index 80% rename from chrome/content/zotero/xpcom/recognizePDF.js rename to chrome/content/zotero/xpcom/recognizeDocument.js index 1094bacd39..b740b9a206 100644 --- a/chrome/content/zotero/xpcom/recognizePDF.js +++ b/chrome/content/zotero/xpcom/recognizeDocument.js @@ -23,7 +23,7 @@ ***** END LICENSE BLOCK ***** */ -Zotero.RecognizePDF = new function () { +Zotero.RecognizeDocument = new function () { const OFFLINE_RECHECK_DELAY = 60 * 1000; const MAX_PAGES = 5; const UNRECOGNIZE_TIMEOUT = 86400 * 1000; @@ -38,7 +38,7 @@ Zotero.RecognizePDF = new function () { id: 'recognize', title: 'recognizePDF.title', columns: [ - 'recognizePDF.pdfName.label', + 'recognizePDF.attachmentName.label', 'recognizePDF.itemName.label' ] }); @@ -123,13 +123,13 @@ Zotero.RecognizePDF = new function () { /** - * Checks whether a given PDF could theoretically be recognized + * Checks whether a given attachment could theoretically be recognized * @param {Zotero.Item} item * @return {Boolean} True if the PDF can be recognized, false if it cannot be */ this.canRecognize = function (item) { return item.attachmentContentType - && item.attachmentContentType === 'application/pdf' + && (item.isPDFAttachment() || item.isEPUBAttachment()) && item.isTopLevelItem(); }; @@ -137,20 +137,18 @@ Zotero.RecognizePDF = new function () { this.autoRecognizeItems = async function (items) { if (!Zotero.Prefs.get('autoRecognizeFiles')) return; - var pdfs = items.filter((item) => { - return item - && item.isFileAttachment() - && item.attachmentContentType == 'application/pdf'; + var docs = items.filter((item) => { + return item && this.canRecognize(item); }); - if (!pdfs.length) { + if (!docs.length) { return; } var queue = Zotero.ProgressQueues.get('recognize'); var dialog = queue.getDialog(); var numInQueue = queue.getTotal(); - var promise = this.recognizeItems(pdfs); + var promise = this.recognizeItems(docs); // If the queue wasn't empty or more than one file is being saved, show the dialog - if (numInQueue > 0 || pdfs.length > 1) { + if (numInQueue > 0 || docs.length > 1) { dialog.open(); return promise; } @@ -176,9 +174,9 @@ Zotero.RecognizePDF = new function () { return false; } - // Child attachment must be not be in trash and must be a PDF + // Child attachment must be not be in trash and must be a PDF or EPUB var attachments = Zotero.Items.get(item.getAttachments()); - if (!attachments.length || attachments[0].attachmentContentType != 'application/pdf') { + if (!attachments.length || (!attachments[0].isPDFAttachment() && !attachments[0].isEPUBAttachment())) { _newItems.delete(item); return false; } @@ -223,7 +221,7 @@ Zotero.RecognizePDF = new function () { } var version = Zotero.version; - var json = await extractJSON(attachment.id); + var json = await extractPDFJSON(attachment.id); var metadata = item.toJSON(); var data = { description, version, json, metadata }; @@ -258,7 +256,7 @@ Zotero.RecognizePDF = new function () { if (zp) { let selected = zp.getSelectedItems(); if (selected.length) { - // If only the PDF was selected, select the parent when we're done + // If only the attachment was selected, select the parent when we're done selectParent = selected.length == 1 && selected[0] == attachment; } } @@ -327,7 +325,7 @@ Zotero.RecognizePDF = new function () { * @param {Number} itemID Attachment item id * @return {Promise} */ - async function extractJSON(itemID) { + async function extractPDFJSON(itemID) { try { return await Zotero.PDFWorker.getRecognizerData(itemID, true); } @@ -380,20 +378,32 @@ Zotero.RecognizePDF = new function () { } /** - * Retrieves metadata for a PDF and saves it as an item + * Retrieves metadata for a PDF or EPUB and saves it as an item * @param {Zotero.Item} item * @return {Promise} - New item */ async function _recognize(item) { - if (Zotero.RecognizePDF.recognizeStub) { - return Zotero.RecognizePDF.recognizeStub(item); + if (Zotero.RecognizeDocument.recognizeStub) { + return Zotero.RecognizeDocument.recognizeStub(item); } let filePath = await item.getFilePath(); if (!filePath || !await OS.File.exists(filePath)) throw new Zotero.Exception.Alert('recognizePDF.fileNotFound'); - let json = await extractJSON(item.id); + if (item.isPDFAttachment()) { + return _recognizePDF(item, filePath); + } + else if (item.isEPUBAttachment()) { + return _recognizeEPUB(item, filePath); + } + else { + throw new Error('Item must be PDF or EPUB'); + } + } + + async function _recognizePDF(item, filePath) { + let json = await extractPDFJSON(item.id); json.fileName = OS.Path.basename(filePath); let containingTextPages = 0; @@ -414,7 +424,7 @@ Zotero.RecognizePDF = new function () { if (!res) return null; if (res.arxiv) { - Zotero.debug(`RecognizePDF: Getting metadata for arXiv ID ${res.arxiv}`); + Zotero.debug(`RecognizeDocument: Getting metadata for arXiv ID ${res.arxiv}`); let translate = new Zotero.Translate.Search(); translate.setIdentifier({arXiv: res.arxiv}); let translators = await translate.getTranslators(); @@ -432,12 +442,12 @@ Zotero.RecognizePDF = new function () { return newItem; } catch (e) { - Zotero.debug('RecognizePDF: ' + e); + Zotero.debug('RecognizeDocument: ' + e); } } if (res.doi) { - Zotero.debug(`RecognizePDF: Getting metadata for DOI (${res.doi})`); + Zotero.debug(`RecognizeDocument: Getting metadata for DOI (${res.doi})`); let translate = new Zotero.Translate.Search(); translate.setIdentifier({ DOI: res.doi @@ -457,16 +467,16 @@ Zotero.RecognizePDF = new function () { return newItem; } catch (e) { - Zotero.debug('RecognizePDF: ' + e); + Zotero.debug('RecognizeDocument: ' + e); } } else { - Zotero.debug("RecognizePDF: No translators found"); + Zotero.debug("RecognizeDocument: No translators found"); } } if (res.isbn) { - Zotero.debug(`RecognizePDF: Getting metadata by ISBN ${res.isbn}`); + Zotero.debug(`RecognizeDocument: Getting metadata by ISBN ${res.isbn}`); let translate = new Zotero.Translate.Search(); translate.setSearch({'itemType': 'book', 'ISBN': res.isbn}); try { @@ -474,7 +484,7 @@ Zotero.RecognizePDF = new function () { libraryID: false, saveAttachments: false }); - Zotero.debug('RecognizePDF: Translated items:'); + Zotero.debug('RecognizeDocument: Translated items:'); Zotero.debug(translatedItems); if (translatedItems.length) { let newItem = new Zotero.Item; @@ -509,7 +519,7 @@ Zotero.RecognizePDF = new function () { } } catch (e) { - Zotero.debug('RecognizePDF: ' + e); + Zotero.debug('RecognizeDocument: ' + e); } } @@ -561,6 +571,76 @@ Zotero.RecognizePDF = new function () { return null; } + async function _recognizeEPUB(item, filePath) { + let metadata = await Zotero.EPUB.getMetadataRDF(filePath); + if (!metadata) { + throw new Zotero.Exception.Alert("recognizePDF.couldNotRead"); + } + + let libraryID = item.libraryID; + let translate = new Zotero.Translate.Import(); + translate.setTranslator(Zotero.Translators.TRANSLATOR_ID_RDF); + translate.setString(metadata); + + try { + let [rdfItemJSON] = await translate.translate({ + libraryID: false, + saveAttachments: false + }); + + let itemJSON = rdfItemJSON; + let isbn = Zotero.Utilities.cleanISBN(rdfItemJSON.ISBN || ''); + if (isbn) { + try { + translate = new Zotero.Translate.Search(); + translate.setSearch({ ISBN: isbn }); + let [isbnItemJSON] = await translate.translate({ + libraryID: false, + saveAttachments: false + }); + if (isbnItemJSON?.ISBN?.split(' ') + .map(resolvedISBN => Zotero.Utilities.cleanISBN(resolvedISBN)) + .includes(isbn)) { + itemJSON = isbnItemJSON; + } + else if (isbnItemJSON) { + Zotero.debug(`RecognizeDocument: ISBN mismatch (was ${isbn}, got ${isbnItemJSON.ISBN})`); + } + } + catch (e) { + Zotero.debug('RecognizeDocument: Error while resolving ISBN: ' + e); + } + } + + if (Zotero.Prefs.get('automaticTags')) { + itemJSON.tags = itemJSON.tags.map((tag) => { + if (typeof tag == 'string') { + return { + tag, + type: 1 + }; + } + tag.type = 1; + return tag; + }); + } + else { + itemJSON.tags = []; + } + + let item = new Zotero.Item(); + item.libraryID = libraryID; + item.fromJSON(itemJSON); + await item.saveTx(); + return item; + } + catch (e) { + Zotero.debug('RecognizeDocument: ' + e); + } + + return null; + } + /** * To customize the recognizer endpoint, set either recognize.url (used directly) * or services.url (used with a 'recognizer/' suffix). diff --git a/chrome/content/zotero/xpcom/translation/translators.js b/chrome/content/zotero/xpcom/translation/translators.js index ff5df9c739..5301906945 100644 --- a/chrome/content/zotero/xpcom/translation/translators.js +++ b/chrome/content/zotero/xpcom/translation/translators.js @@ -37,6 +37,7 @@ Zotero.Translators = new function() { this.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT = 'a45eca67-1ee8-45e5-b4c6-23fb8a852873'; this.TRANSLATOR_ID_NOTE_MARKDOWN = '1412e9e2-51e1-42ec-aa35-e036a895534b'; this.TRANSLATOR_ID_NOTE_HTML = '897a81c2-9f60-4bec-ae6b-85a5030b8be5'; + this.TRANSLATOR_ID_RDF = '5e3ad958-ac79-463d-812b-a86a9235c28f'; /** * Initializes translator cache, loading all translator metadata into memory diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index c734812612..21b14e171a 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -3555,11 +3555,11 @@ var ZoteroPane = new function() canIndex = false; } - if (canRecognize && !Zotero.RecognizePDF.canRecognize(item)) { + if (canRecognize && !Zotero.RecognizeDocument.canRecognize(item)) { canRecognize = false; } - if (canUnrecognize && !Zotero.RecognizePDF.canUnrecognize(item)) { + if (canUnrecognize && !Zotero.RecognizeDocument.canUnrecognize(item)) { canUnrecognize = false; } @@ -3718,7 +3718,7 @@ var ZoteroPane = new function() show.add(m.sep3); } - if (Zotero.RecognizePDF.canUnrecognize(item)) { + if (Zotero.RecognizeDocument.canUnrecognize(item)) { show.add(m.sep5); show.add(m.unrecognize); } @@ -3726,7 +3726,7 @@ var ZoteroPane = new function() if (item.isAttachment()) { var showSep5 = false; - if (Zotero.RecognizePDF.canRecognize(item)) { + if (Zotero.RecognizeDocument.canRecognize(item)) { show.add(m.recognizePDF); showSep5 = true; } @@ -3892,7 +3892,7 @@ var ZoteroPane = new function() menu.childNodes[m.createBib].setAttribute('label', Zotero.getString('pane.items.menu.createBib' + multiple)); menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.items.menu.generateReport' + multiple)); menu.childNodes[m.createParent].setAttribute('label', Zotero.getString('pane.items.menu.createParent' + multiple)); - menu.childNodes[m.recognizePDF].setAttribute('label', Zotero.getString('pane.items.menu.recognizePDF' + multiple)); + menu.childNodes[m.recognizePDF].setAttribute('label', Zotero.getString('pane.items.menu.recognizeDocument')); menu.childNodes[m.renameAttachments].setAttribute('label', Zotero.getString('pane.items.menu.renameAttachments' + multiple)); menu.childNodes[m.reindexItem].setAttribute('label', Zotero.getString('pane.items.menu.reindexItem' + multiple)); @@ -4378,7 +4378,7 @@ var ZoteroPane = new function() // Automatically retrieve metadata for top-level PDFs if (!parentItemID) { - Zotero.RecognizePDF.autoRecognizeItems(addedItems); + Zotero.RecognizeDocument.autoRecognizeItems(addedItems); } }; @@ -5294,7 +5294,7 @@ var ZoteroPane = new function() this.recognizeSelected = function() { - Zotero.RecognizePDF.recognizeItems(ZoteroPane.getSelectedItems()); + Zotero.RecognizeDocument.recognizeItems(ZoteroPane.getSelectedItems()); Zotero.ProgressQueues.get('recognize').getDialog().open(); }; @@ -5302,7 +5302,7 @@ var ZoteroPane = new function() this.unrecognizeSelected = async function () { var items = ZoteroPane.getSelectedItems(); for (let item of items) { - await Zotero.RecognizePDF.unrecognize(item); + await Zotero.RecognizeDocument.unrecognize(item); } }; diff --git a/chrome/locale/en-US/zotero/preferences.dtd b/chrome/locale/en-US/zotero/preferences.dtd index 86f9c082fa..143ad8ca69 100644 --- a/chrome/locale/en-US/zotero/preferences.dtd +++ b/chrome/locale/en-US/zotero/preferences.dtd @@ -14,7 +14,7 @@ - + diff --git a/chrome/locale/en-US/zotero/zotero.properties b/chrome/locale/en-US/zotero/zotero.properties index 58ef9f735b..9d741cb1b4 100644 --- a/chrome/locale/en-US/zotero/zotero.properties +++ b/chrome/locale/en-US/zotero/zotero.properties @@ -368,8 +368,7 @@ pane.items.menu.generateReport = Generate Report from Item… pane.items.menu.generateReport.multiple = Generate Report from Items… pane.items.menu.reindexItem = Reindex Item pane.items.menu.reindexItem.multiple = Reindex Items -pane.items.menu.recognizePDF = Retrieve Metadata for PDF -pane.items.menu.recognizePDF.multiple = Retrieve Metadata for PDFs +pane.items.menu.recognizeDocument = Retrieve Metadata pane.items.menu.createParent = Create Parent Item… pane.items.menu.createParent.multiple = Create Parent Items pane.items.menu.renameAttachments = Rename File from Parent Metadata @@ -1191,15 +1190,15 @@ proxies.notification.settings.button = Proxy Settings… proxies.recognized.message = Adding this proxy will allow Zotero to recognize items from its pages and will automatically redirect future requests to %1$S through %2$S. proxies.recognized.add = Add Proxy -recognizePDF.title = PDF Metadata Retrieval +recognizePDF.title = Metadata Retrieval recognizePDF.noOCR = PDF does not contain OCRed text -recognizePDF.couldNotRead = Could not read text from PDF +recognizePDF.couldNotRead = Could not read text from document recognizePDF.noMatches = No matching references found recognizePDF.fileNotFound = File not found recognizePDF.error = An unexpected error occurred recognizePDF.recognizing.label = Retrieving Metadata… recognizePDF.complete.label = Metadata Retrieval Complete -recognizePDF.pdfName.label = PDF Name +recognizePDF.attachmentName.label = Attachment Name recognizePDF.itemName.label = Item Name rtfScan.openTitle = Select a file to scan diff --git a/components/zotero-service.js b/components/zotero-service.js index 3a4afa4ccb..8545922213 100644 --- a/components/zotero-service.js +++ b/components/zotero-service.js @@ -102,6 +102,7 @@ const xpcomFilesLocal = [ 'dictionaries', 'duplicates', 'editorInstance', + 'epub', 'feedReader', 'fileDragDataProvider', 'fulltext', @@ -117,7 +118,7 @@ const xpcomFilesLocal = [ 'progressQueue', 'progressQueueDialog', 'quickCopy', - 'recognizePDF', + 'recognizeDocument', 'report', 'retractions', 'router', diff --git a/defaults/preferences/zotero.js b/defaults/preferences/zotero.js index 8b6ad17215..913fd1c02e 100644 --- a/defaults/preferences/zotero.js +++ b/defaults/preferences/zotero.js @@ -35,7 +35,7 @@ pref("extensions.zotero.recursiveCollections", false); pref("extensions.zotero.autoRecognizeFiles", true); pref("extensions.zotero.autoRenameFiles", true); pref("extensions.zotero.autoRenameFiles.linked", false); -pref("extensions.zotero.autoRenameFiles.fileTypes", "application/pdf"); +pref("extensions.zotero.autoRenameFiles.fileTypes", "application/pdf,application/epub+zip"); pref("extensions.zotero.attachmentRenameTemplate", "{{ firstCreator suffix=\" - \" }}{{ year suffix=\" - \" }}{{ title truncate=\"100\" }}"); pref("extensions.zotero.capitalizeTitles", false); pref("extensions.zotero.launchNonNativeFiles", false); diff --git a/test/tests/data/recognizeEPUB_test_DC.epub b/test/tests/data/recognizeEPUB_test_DC.epub new file mode 100644 index 0000000000000000000000000000000000000000..15697dc01a499b851bd6f2636dbd96e6017db627 GIT binary patch literal 46649 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 Gj{X;T)M|(T literal 0 HcmV?d00001 diff --git a/test/tests/data/recognizeEPUB_test_ISBN.epub b/test/tests/data/recognizeEPUB_test_ISBN.epub new file mode 100644 index 0000000000000000000000000000000000000000..58703133fc0e3a80b9e9cb5326a58684035038fe GIT binary patch literal 46667 zcma%hQ>-vdu;j7r@7T6&+qP}nwr$(CZQHhe_P@zy@9REPIx|Tp(@9TFbx+Gn0fV3b z06+i$aEAb@*%?$(|F;78AN;okU}Irp;_P8>VqkA?ZDC~KY++|hXJYSSNabl^FaLi% zApmk5-&Jqz(A69M^Z5P`X#YV*L|K4FLROT{$j;W;z{1wVk=EVDS|(NiE|4BY_$|Be zA~Yo6a+Vnd33)BkJy4T(ef@WD-thSq4K4VDnCVVt$*0E%FTIdN`J{q36=q)E&O!Mk zJM>c9)n)->u$X3W*b%Sj@F_Uaxmls`6~Tlt5lCOo^wnRYmZf9n{d9k0U@Ne}lu$Cr z1tG0@$~rx#Do~bg#BOcshX74FbQJ|`mit*%?E;>mH`}Y@5;tf9%rw;|4=wgTW85g?6^96&9zL?1@2wt1ls|FT1yzsa? zvn{&i*3bO!ajR(GOsdk~!EBIv!S~M3$&5GDP3@64mNbsp>D+#u)!!?ar|Xa*>GKQZR&8rTStU#>N^BX6>>r+d~T3Z$NP$8 z>nR}qKMp~GP>bDw8C+6UX^F(d_{TftO|Zf7iic$ehs4DOR0UZQ1wqX){2tgqNl44V zjGe2kpn%p7sP2e`3X9Efdw2ms$K#I%DR{J4^qt<5Six2+kf!o3MCt-Ysqn&hkxh)t z<8$z!HvbUO@=f0aLUzg22)bLoG&VeHc4+B+qbWzf#9)P7MBn@g1FwOK-(-dq6!nv*TqY4Ab*kgK$4yC*TK@fl;Art- z`68MSX+eCb_I~A}=v3N_NI)u)ItRT)Z3#4GgOGVT$WhpI<4EPngk(jtW&LZBYO~u6F~t&ph5~B5qoe= z&1Pc>Aclc=RKv#n4odmdg#2VS}mXNSEbYDL^5w?Vzw-sXRzW> zsASY?Wdkzz%Hu@NdiI1T_B4`&LOgxyxWl6(0$qn^!40S~06KTmMa zbDntncaLBT^*oAFs zRdCbeu_5~#_7ox$T%i^-zm1eH|CVIh2P9#sG8g>UCoR(rIK?Zw-p?7Q3hA3%c7N%K zjH8fIB^A%et-9?>!~;q&RNzc6qX^|*^XVY*xdxFM1$c(TMHDM$J8o&3xf0YZxjs)R z-qSqTC4Fk`&z+qjo5r-Mz$u6|XwL zPn|qgadhQSpC*agGd!G4jbNrmN#2<@-g~2H)EG3T4{YlN-PNc*QF)6=E6jgA?P^K) zu;fp9UiVU$UOs(mN`j)3M>l6k-JV-d5oT+QM{$IoDEW;Ne>GHV?NDbDqPd2h-G%+Y z|GWWHk2m@Ld9$;NMVu;@^W$VNw5f%9SZU^&j)aDoeVXA?^1M-)lx22tdh8syz@Je_ zAibHPry>qwRfS<@a19(e54Ahw@93azCqJ;Ih7s0K+r6%+@rjbMwIyY=yP=ho#K9r_o$EY@zL!?K@>JKo%K+;xX+9;F0iO2vY-cT&cdOV< zzO=Myy6MjXcNJ`u!_Vq_`t)V$8nM)J&)xM8eu2SHt*k+r{9GT zCW|hOo<{Gi#`Z!}VGnNE{_GWjy#% z(fnKj&q23!V|wmFDz@(A7BK-pQ_*H4dRZkz+E~>6-R0wvuJ^OvE;6hV@3Qvihc0*f z`w?AarM63EXZPn9eW_jN_Y_^v=U?x8o86xG)9)N#H@Rz(_iq?p-|y`T+X!81p6};U zlO6BR^VZuQ+TYd5*Ige~?l$+w^AdWUpTf5xTWr{#&*#O1dp8Gf*TrX-++N?$Gcc{+ zBMAC?pB?MbDf%O9cLTRH*Y|JIlvUpMYg^w674JZp?^^Pscb@VMS$iWT+auqXC3Td` zTC57!?{8AWC%tbcZm1m8wzbBONhO}|-vry|*-6*Kvhvhhnb6Bbnd60eoXm>}`Fq}5 zSLJ)bop0@`(ZiSoZ*S#iSK(*p)oqmR6rZ17t4jo)=*NKz$K)og$lBu=-tj`E0`DTc z$cAmCCwAnU%HG3ga5>q)8QUDa=B-2F(|EBC9cO-J2- zXV6(u_2F>+A-f>at3EDxEz#EaemtF6$}*d?pNk4Rm~2)^ZB`Kb$<#2 z;_L49`MaFCa?`8!^cZ2Q`{=^!?eh6K?D4+6>7wK18Lv6!dE~PBysg(x3zO?j*81sK zUy~{NYNJ_gtW?rdYjkqkDP+H`WYw2jQ&EC7(qSFZS*oG5esNH%a~o-(%=|1E6b&=D zF6-(lr3`Jj93tHnw)*$LC>s1%DF>)g5{fct*$yvj6O!qNIu zBI$#T&o4k4Kun;B+977!p32$}2vg$2NtN-5)0+T0Kn#1lt=$CKHNB2C={rzk+fiWc zNc_s&KAbvEMz3nLxc7;o$2lHpRig8|`I(C{+$KQL<#U#CQQDrg6g1Yk5AyE4i5+RR{V?vK)^jjFewc!sBRwRU z`sr6fZ7tz`3Ug5rAV77|x~BAqR6tkyCD~HE?yA6<(rmS-`r8by{8veIM^(2Z(^Jld zos(h>+ja+LNC5P3lGi1XdZWDj^;m0LRk({r_OYUwt4{{sev65{XUGR@6t8pEv?~wT zee3DfSC~4J0T*UFPosS?2*v@GM4d*R+dUr7uLk$nRY?j=y6-HyUt-b`N9(ni1)8lb+2`63O)Mq%D((FNZg|*7qi1hFZX!) zS)~I_Awgxx#(0PmDh{HXPMhPl>i{2~QnHgm4mFwWO+wXuNBVIG!Di#6KzBukCpR#M z@Z(EpgTI!b%ds~~WycOo;VM2?g>AACrXV=J?%2m&X6?VLqnA?YT={x-OU$XyoI{ug zQ50^&6a9F8RC^2FogSGuZWuC0YdRLzhT76t0q!x}7=c7vs!xSPsOHOXeFEG2J#=&B zMyESIdrzhYy7#Kmau>d1s&YNi&_SS8?4<3sAv;kch}Xrm4_CB-QE*Ue;s_CV*dO>9tN$a{9WA0cCxO$&@>#}$DR5xI3(9Cci7jw5_jadB$^9BH64C3|>XF0gY~)~Y)tsQ{^1hn*Pa zs^q;kp&8yX<`r&B{sG`aLm7O9UcXPM^?j+Lr9 zI|7RvXsfEHAaDFqDkNtnm|HUHp|~|fb%FBmRR8Vue%IL1!~O5I!dlqUHnNV1#@FAg zB?q${^IW=PDBKL$3V5Jsdw+(@{T9@Fq@_Lc)8s^;xE;GJU=w)2A7j|GE4hVB`CF8W zsXK&6*u8vMn|b+{>oV!3r!$7cnI^fCGC*}ZTE_3n_VBzT&inD{so@IhcD@#d|t)lkE0KVj!F?z7<_drY0Ki1BFfOR z3-Atp&lb~I;9|}CU_aGBeyJYiCU`6sJmU@s=N-TAW>{uxPz{-6*${QIgU}y@87=Fv8I+NRaHRBoQV9DKct8lu(r%tsW`spOr?Z8o~|0!zzfsI!CSq&v6N^ z8KS@Dk2dW0mQ7DWp6wvbk9N5-BLY@qw6<757Hy`HnxvhJ#erQ!9+o5?qs zo>MZDmmKUQgiyDM!n5VQ@bNpf_oX$1h}DGSp@p8>*u9=3N&@LT+adVvzRDG+LKe&k zS``cS)lcqZfZIOTLmm7AZZdeVy14N~@24FT0R&H>z)bZ{_+HaIhAWc_1OsdGOmWhn z7`F!0Dc4B8p-|8T)0L;kC-DeDBWj2zB8^f`q z=tU#jPR_QFwZFQs7f^8ZI$k@LxJ0(|(YA`>32yvVB5Y@f_VV|NM4@-zd|++`9yzY( znf{$ox-EpQRu!`thEzl*f`4V9-ysrlgifZ0C; z$J?VpKFNdM&&a_a>sot1A76o5`#=7gxF)zT>DRNo3+&DQQ;0YJ1pjxxY4ZuKuLXI? z? zegOAko8euMVAJX01CFL@PbpD2;IHkwVVIy zR__?w$PF|&FH%RnG(Xg=g^f-tb)(a90Y>BaBvoTjn=}N zf|G&^_lK?8V6MTrqq8)XmP>~)`CG11*kOGlaxT@G@O?8wMP=Dmc8sA~cZy7zW#u*L zX=HqezA7;)`^y`;kM|Td@BsX#jo8c_(nB8RDu@w0(HpCapz)-CS~q;UPV*@t^5ZD6rLGe z5GO6WGe^igf$GWPgDV}n1rkSr>2SORfiAlYXS_f(=if>BMy$f!_LzHu3kB1x^j zbLH-K441MLCvUIqf#HGsK={=dtgG&q49ZeSaOcz}Ib}TlXaT`_emzi3=igtl3oS;ikm)0EY2oH5s zes;M9E&2_UE63B9gLCzBnui!_wr>B?M(_FwH<~y+rF7|6w&}86kae0<$(0}Fg*kY7 zwmSvRA2v1T&$`q9>>2uWwt2f&w1V&$^N#B=pQ+?^T>L~c&H;~#h+k5qUtv&7yK}FD zj-Q$%12INaAKK1Nl;LW5z;7$1f|I%+N(eV>=?X43wCJcKX{oAHC_6l~^`Css592BW zf~f1ywBAoRXJ~7ZziumHggWY`4~byI!RnK^@3G=J?x!jxCe3o#WH5WH57_`Ok-CRb zgwqI?=m*#=4>kG_(CFJ|D=mDkkpGzOgd(HHicHl)Bq;cIMsI3F<@rJ>ltqQg+k3@d zFQt~}IJAu2%StcS!E9{H`pSc{dW=ksIqsmcPe;9Y*G~Jdq+wL}ek~mv=8f(Cr7L(9 zhT*^4@>S1cPOD;pRWedFgv0`80_frA;?ig_8P({QUd1#DH3COPynAW4aeqZ?;cJ8= z1)d(-j37arP!GeGRN=*VOb!BhyO&f1d)^H&NQ@Yt|74-OnvK;WdQe}}iLfsJkbrx3 z83Mc%bD=H6!>Yz%|0LoH;dTnOv$Z0@3JhW3tA+a8ivjahCE*zMruD>5!CB_QlRTXP zm4GM%^GjxiISBh>RV_9R45uO9@hs1Gy>fm4dGff@%fYq5Nrdlcpc80RZ_%7^*Q}-E zzavnma)s@==Q}*RKCg$Js;#t#WbypA+vUc^he_NVz`a3l5I?G~(LnfWlje#Q_yTH! zzI*gRlBkc;5bYgj6#ipXu=Fa4IaRSc1Xh7#{PHZ_#o}9a*q$z>H|8myew}@e__qU9 zYdhSMHa{;ByFbs`|LlJ+q+Q>wC`5S~C9^Gib_ml3nUsFqM`Ri?AwY?Ok$SST0WN;oqX;GU}w_UDrzihB<1q7+lubYeT`W+HEMXG^usY* z-cRc9?BF&lah2N&hH&HYcp=o<7pK~iz;!seKa;`V>+6kQuV>E3YaNVjz@iVy5FSaz?D zPyQ4sbcL@?JeQl+lGB3@6wApc+i3^l$I?)x3x`HKidv@<%&_;@Y~!xG1_qKbEt4B} zM7MW9J3aTyT1=Mvt$K(n(wqFM?zJ`>PWtZzrISRY?@MspK>+Bs_fORK=T_F?-dXJP zhUL$gjKfq|bb^4s@jk`uY-RQBu2n-a61wxfzt=t!*azzaSvdm6 zwAO0~MH0C34g+$KsXf?B3bX1~vo1)xO=^w~qtoxBFh+IC>=(`Cs5@*Z^uEZBg~&xu zL3}+RBZw5*E|Sb!s4xP%=?$fe14=LOP*x-ejv{xT5P) zE8BuHoQeMAgI;dX(|_jf1kE;RRm!FYt$0&mr;tRv9(EMQz8s4Py1avo7yr&SL#=I+ z5+_>%MOsTZv_ZMA`khTKB(k#(XI33oql^Kz28dKi zxtLRe$LbLcqrzfQQxaTH^C=vtLG4v&7{ly|yhh)0P!SP>et-j(j7sQ{pN@)T(={k# z=5*|cRdU1R;*{3pYRj+oax1RL{!4{rmyQd#=Z05YvpJD}9E%yCGt_M%JUBwOQ3sw>oL9{)+)8ftX$o0a0ezyS z$o^dmkKNQB;nQ_BrZ2iqK!*Xb)MC2~J0!MN_mda$0$^+#r=yFfgUYWf*;t3N%CXC6 z<9+azy=;$haI=Buav~hxKX!k4pe9$PhA+jR{N!M8LjtLdRwqPaQulesTS28#^>@qu zo|?qZRk5ab+pLApMe^dPBBvw5ko)WWi(R*oW2m#<;G-AL#+p97d`yq(^&wn9R4i-< zDlfHs6MAZ7G+1^Od^?fDB`}8+2ML9hUKJx^V+Z>?`MxH6&MKHHy&9-Mv&J-nC<9DVdW13up?y8+HZp8`ay|Kx zb`Y#fF(ye?2pZ!rGjK>$0*B#Fz(Jc-Ly;O(ax{~Fr@Yj+L?Fwe6C%pWRV)mz zsC{C_z;K~d6!}9;1ShJB0I~j`XDsk?FLW)CVEEf zb$^KSg^AuB-Lo})Ju6a2ta7VP%Kj&L8oO7Pub>AxG!cC_;WY{+@>c&4l5C=l9mAtG ztv>l2YJ{f8GuUD(iR4%M3%beIxVR=A`~*P)_Tv#Sp|R3YaUu|JZ@yh4`-5%yLDO|V z!HEUbA^qk$rFtr*w+Sx8G#yM11h8W-jWf)l2~Z2`ag&ai>TE%LczTqZf3sF|xzMSk z9E#M}0O~`rDIc!GBNfJ0>y<{%oz_@9&Ae&N8@OnU9i&+_SaXEs3AMlivADHde2RQT z!1-}1qllHxJ+-=fRS+OKxh9vU!%CvIgvPR>>gv663Hp=ln2VzM14xw({*dYn^*-nc zNV*`|Ibxn1kS7H1)T zkRbI}#Th3~%?hA_22ICxOs9a+PEG+EfUSz%EkJWT@K*u+;t9&+YF{LKIj&N?9dC?~bnJ?iYYDDr9$ zKu$p4kXL2i5c=+_MR&ka6Crf4BZgy@;yy4+Iu84NEk;ZNB1F{&RVN3;wD3ku5#$f3 zLC3R;s7z1oVA8Ipav4wo-VYGCq~|}>n8w?pO3aPwHITz(wco8HY<=ya{@3#L`%_-9 z*k{Wb)KRh}jD64^%j=w=l<S9s2SwjBAO*fp!z$nyN^JAK$yj zmEIdGNAsj5CKi&YV=wu!NBoR1_WIThjT96P@K^a7N!JB|fF3C4j*zq$s)q}RqTED^ zuf+i?{G|HSVUDr4jvI!sHWAa{x)k;Trg2@w-{z6v_Yz?xJ)J@+$oj*rGH=UKt8?){B@&z=!}Y3|RiD$@b=e^$E>9$5+3Vq92w9F~md6EOF*^R#eT@2U{>=s9xAFgCPyu;PDyK zmK&$ok+y2Bh%u+YC^Q11=gfY5a4!F^n7IIO^7W>{uLme&B@=5P1RQutFbfNuSH7BG z8IIJ~(0R(Q6VT&EdwHoCW)^yVp6Ho1O=Gwc{MrZ5(A1(C8Hs!6RO0b^%s~TOFxJKb zsSZEOsDIh)>@KUQnsz_p!Lg-dV9S5MInt!e)4r(ic3f5ZIQ|$RVPeaf>D(6Sy#wkr zP$PDSvJ~!G$`3g=Pw}-@YHz6hdTCxGEHsHLRh|8sL#il@%>Tjl7pD*$(&2jbsu|n% zZ%mSoP4KLoX0iBG$x*B+6M@FR^fb9pd3UW% z7_oSF68V|D-b}nila*;EfFU&EhjaIEduB~C=Wf>Cauu6twPFo_3$H=V6p3!9X7~n9 z?!X8E=H6QhWZ9@xR#pq6j0J*3*~q$4x%C>Z1$WS#;+j!6d6l9=gNM!mEfS`(UWLBf zAN-K|rq&dYz^A;*vrDadSdGC#Xf`GGl!)*w81K zYH|N^yoV=9gYY11Hpx^vwJY+PeqOY{{$xd13-D|SSJAq$QsGsowuh3bQFG@s51qGc z0UfoaBFhHQSYdBH21ylpevbm@Jlu)LmEDtA5Vf)dkDU(+0femKk7UYP~=qYV?< z@j^MUP*oQb&OPnQ^Y~sKMGGNRpcZu*{A@F9v2jZr@+%iz&1sKl)deUw2Xn+QvqUF_ z5}{r&JIU__{UQ`~)gU+2ES#lULH4AujqZtCJKDyCtNo6wRsAeoLaJ9DVl`P|>CXaw z`feaRIPp&g9I3ThPF>_1xb6q`q{M92LP+$rEd4or^lHhTO54cq$Bzncp_p$3s!X4mTu~DDn?S}K=2;oVaV%}A5 zPPlFOmP6nlY~*lXO<}>pU7FSZ;q^N;8y+JJR-Gi{eO}O85>9P%{jlM*lT{r4)NcFm?F;R#gWQDGK)#rgJRE3aw z%=j&es_^nT%?tliC*MdGYk|{f@sAivLc9E|HIZ?q!ZPIy>uJrUM{sFiZDIwc@tZs2 zPm(Zj`62i^Aq>PGM+Cqa9QD2prpTp84montc!|Nrx7}oF9-ww`mO)<@t zYCvLenGv?m zK#c)F-E?*v{%K;YKt-k`QxKFAzX&}+cJx<=M278OJtejCQreDX{^=p2Tr!%{0^LCo zE+m9{+u+(3eQxAI)bHJp05}mJsr}rJKDp8^3Q_CfI_jVIKJozN@jBV>>?N=Q@)6~8 zc-El?qHhJ~xcnM|Fq3Lt&vX`3ubZnVyeZ0U!fk};cTU?rlO6OiltG*e$F8099pQJw z4}5W$e3u+MN@|K7BkSvIH!h8)ib~Q#czU1ul)WA)bI)Hg=78;mRX(7giZjmwSRmR2 zjNW@W|D3O-0g_eAOwlc^ta?N#_lzw@J1H0WEvL~lUVz4VkMl2az^ygPuPz0U!#UVH z4M+xrA{G>iG+pm3CA=57I(A}%aHTA(UkG1P9(TVcJ_ilAzwh_jt z7F0#R*%g38WV*K^R}FQU^fcZeF(bvl3tgIqLE^<%D6HZf%za&f30AR!^M&0NpPK)p4#K(++V>qbURA~VGjxgfF{uJn7gkhu z4pIUGct_0!5~I3ZkQ?dhP2O3lRt&~4$;k*=MBV1ffym^j&L2@-uhqd^WvdrJ&}%G+ zTu~j^T~ERfvd6&Btvhx{bB+x%_(y|LcHFw@GuaP!Vkz{7B?ET30dQzra%6zW*B+WSJdL%FUu57|2;UZ;LxWhQ$a>I!dTHW1%*`}HHfL?2 zRAnX-G$$TfE_Z6r{UOKe53|*Kf4h|w_z-4W;330m_lV_wYq2s;lT2dMGBcf|5i4%| zSIS!K|7WRk)2~xJ#+|AnWP}FzmPlik7QXoHuWzwcZW(?u0Db23Gp=}=@;XIkc9SOO z>*FwiZ2&a0YBGUI&tVh2VL@^*x=wDPfs1QqZljXZ2xem3&HuJ1wq;N*D&8kF97l7Y z0OTbY$0&m!ya1!b1NV2Dy1%3T<}>&jVHH^aS6m2DRzrXozAWh<&Vj7q|216Q;h_0P^fm=*2b_X3nq}a`+=>mwGoKiBS}d2VZP1RCwS_ zf=*>{|2FFw93D!ONs*d!qK5i7yO~#N-UF+TAG0X5N*wgANW`n)+*9s0Pd%}Sak#Q; zFP~G;vND9y!If-JM1ta`@?qbI)T5K>w3nbG+WKU?RU*P~BE6Xeb>=iAZVAhW3OlH| ze4X#;tL)mx^cVsaf@y3n` z_KlHLB4aZm*G<3Kv>J;<&ZcuWOo0p5MD(CZNP71WCpBFw>hs_++ie?^``qyak`})r zqYs`zO%Kkvx`$ULRoBeJ%VFi^{g@RcHLLM`$y-q(N76u5m-=L!$#F>54fE?2pmV)_3wy4e1foi0L7WDY3sN) z^QTsyg6;qq)97Ecd;#%X?4R2kQCVp zQ-QqhMB@-HyCG;WgB~+hV;Oh2Nqru=*MHbkazyR>^51=Oq1Z$7wHXy!8l))*TxV-U zR^Y^f`6-MA7h2{JXAx6x7IAnL?2*DK5J40l`iM{QsmaF$o%x-OquuRi|t$ugYf2Egsss0(?_{Paha=MfdvP=T;^IEEuIQR?J!Dt0{x)Qg(o zOUR>~&foX`vgVtLr$*G3zMv3wtv{N*5HoikA&`a55KnrYkyZiXLivj<#30oonr>iV ze9br1zBbm6u5^%0%tKuwL?m<3Fq<)8raSJ+crNhOV6-coF7f-y=m6p`nj%+P6**lx zv@$!ka8v!^uBZ$2c(_0=EkUZOtOI84nNVeF@H}$`kMX}*6HJKw*95_jGB7lONcenA+InVEk<2A(v?2 z!MWmoQFHHdv70)9>H^GtI%Ku+V5Qq9a+%*}$*#ksCfAX&zV81Nz=TuS`KY-N_uP0t z+aAI_^gst8`hQJ&jw<)n4?sfpR_Lw@yJT8i>(YJ$Q%D7cjkhpc^~!^_Aq z&413R1qK_tW2$;i4CO(4%oui`)wO4ul^#ca9nG#aRw zaO2$Zu=P~}fDDaz3N2ZQT~)|gJ_!Tq;%?~2O({%Vqu&5PD6ma5%73RfS$KZ;>^>H6 zWn3ucTs149^;)N6$u}5R+Xb$cGf3nAN*$|TVf9mHxX)mro7Pc2%T!15yd9|K(!mrA zR>-MTFXMf+jw8uizdOeYx2Gjs>SWcF$e^RXp0EW7Z9mlBC(q#yCgL6c$(dc?10|t? zM$k#Jp=hnZSq=lD*`TT+{w_!SX}>~)Gd^Cs^)O;Y@(^qx_i&YjLtZM)$%`#WjuRjX z<=b3QAc%;Wu`pr!U3QvYcj^v|X~VSd*~@76J^hQn0Z8ZoF+Hza)&`@Pk(+j>(5e5- zYe*Mk5fdS{^QYOb=}lHrbdx(f9^tp#&bp@z=sD&wRFsabtR9-?Fc-%s0K3vVznuPrs~i3pcuIH*EfH@X zcP;s2%6?g*vWb$L-9*dd?Pw=f+qyI23#LIgK-=Oi6{Um2;rxLalKtlXP-$TykfPB% zS?4OO_IJo3IemS>`|o4sb?5QbuV?Bp8Vr{<0;P43`ZLG$GmclO-EppNuh;XB7F<-_ zqtpeeq#BBN^I=`1(oxm5Y->mJxeVuRI?z3laTwU=%ivx#ymT{>v;X|TcqD(1aM_jJ z`AwdIUIWA^eR;RAbgSv=(&`wan#r?h8;Sx8B0`pj<7J-SqrhUiS7ebnneaTSE z=~fnI5}{-F7+;X?+#W~KRfCI>cpWevY$n25W}DyX@kmHYShbCX#K;cSs-PRplhA;f zhmp47y)nV-&RJ$J!D%CnpbV2Ixjgr++<@dA&gcrHM!0g#bd5jvR!*uE#C{CWM`!{3 zVV|bX+zIjO=fImvT3LCZiH2@Cfzp}!vT}pCOr1j(%qdnmA<>jTwf>5CZ81u{=Jxg| zJ+8M>@HIYTOv`NDBg-@%QyWDf>c}NUWyw6uNeS<;n_$~B)xQ-)xok80fplYsd3nLG z?;0yAgQZOIJRaH+)^e##70!ypAG3yMu3}U$_90e@>lsx|Q*RT|9f%w@F+NUdRW0*h zUM>q0uaBq0#PnEi()i>*#Lrv&LouMXBt>fDY;;GognAKQ_YkUZUgDdH?bp z6<+$JvHaat#e_=mXV0K_scsibgHcYa=vIh#`~cyOR9U~bBna|&Bv>t*j6Mf-uWKEU zR=lLv0SFAipo>=|m<cs zAy8nBl?pSs4SW5ftl9K1>9bu-mDfv}ONo@J0)oMtFp?P1&!R8ek;zw&hAF&SWg!wk zOPA#RvYq~0i|<$pJfrKYy`#R}ovFxV87kYTixEEOvA0qH_d0iz?^D&?tP|IwurJCvVPof@Nsksz6Z#5n0i_2BId?&^FFQ;dn?k3{Cue|< z+4OLr!2bw@tz{kkgkZS=S8vlS=S(U0N+ASv?6A~=VvEI@G*}C-G{4dYN5eeB(-V2P zWat~KjF;XC^|y810OC&jicr^P;Wd*&|I~}~dCrT0u~CJ)6FhZz748-oOA5Q`-n<~| z+^yG??0&kuh{Vw6u8ViG;mICTug5N~sAQt$<}xwzc)Qwv`>9HRw67@ zUrsz>%t%+7rk_Gx$U7&r%xBhkooat}UD>-ty{ls$M`Xf6)aq&|fQt|?95ULBId%#x zQ`^Prx0$pClMadCp^H?u-wI93J`PHY9i+? zQEXWf6PGpUGBK12nmAnaaZ{E&>I0Z{b7Nfj9}fBllZm;dYT8iG^lO@25x$qB;;92k zxt~)x7@4gSwPa>|s<|5uVl;)Tw$FAG2Y}l{MOAxckG^*epkg3LyE#Z8QN9UGUxYdc zUK;ZBywzz5nfYV#3)Q({Z~Jwx;FKHT?6M!^N1$e(#TS-iqyvXL#%&t1oKhRgO8 z9XWVpghI{jHPsC(kuMxtwAWe)fe7Q~C(HRz(P z#spgVh|0y%*CU@|c26r0+DuLl2mZ#}q(CKf!kUU9Y%Ka$)~_rwQ%`~vG(l8)K6Oz7`CT3?ZDI_b;JnH!K;`mFbRV3Kii*A& z2MdDrC~pUI?R919rhh=y6P{Bc2%!@q(l>xDYwvQKpL7p59~~hDB|;tjpjP^~Hc4K% z9O#HC4scw3b8NM?XNEelGZghQ1fhrONQCs~Yt>c$UC!4u33Nw`GDNy1*bd;!hI`+@Fj2Zq-UU8$tKgKB zu`T*n^moS0dW9SRxso4`@Z|}xzsNMV zq@@85(7f5K^o1iwSl_4A*860elcZ)9bT}JFNYN}Ku6Kd?=3@%KVSQ`(a=E28!tFA( zHt(}+c#;F6D2P(*apyEJyDvzTi4*(#25$t?`IQ@9tq?a-`te6luhTH|16kzFJ}L}!hv|DiIaRQ-v-=wJ@wx;!H%mM^S@x0T{IwHBL;UJo5Dzoqz&l$rfR z%JFsmryF~lBsg2?P8ChBAP{#Pd}8DmpKNY~*0}SDhnUM^>0+)UwZfT2hT}6OrH!jK z%bsI8sE^ueqA=FSv4|d$0F+UhQq~Q-(84y3g^gjT&RAltFi?ifSdN$c>J`LVvh4+* zJZ2j`*yn{g;|3pmM#k3QL*nj6{t zBV*suW3H>WWT9iIsyM1GbBryk$uGY(J^TzXKuUANzB(0(PV_N7TGSspAfLwRK{sY` zd|S^k{lT6kmnb6Z5nHR~CPp`KT0O8!j=F+_PwhYfsdPb&zEN zcA;WY{b{-4Y=f3AkzxuwjEWv|&64pSm>R7>!5EtO&AJ(#wMQLbvtvjVQBy!2h_LU0C>UoCm&|y7?<~Ljw z$Ao#5Ca0Y}BT8V>HZA5vRzNn5^PQEqAl@ol8qYmIvYV9jTxKja*7kU*^7?yMyPQoQVv<-Zg9w11HS$s61-luOYWvn9ceEgvA-~Y_imqA% zgn8R94vtwoP{qW`&0*r?{azj>GBK`zv#OoLjiN2q{&d$Pl7{9h<*WNKii1{wIk{yD z!n!r#lFi@>V^VZG+;{o|0yhb;nnCIBNeJ(WVnVhxllNI6rd5nJJs=n-MA0)p@9#Do z8=i=Ct%>;OrT+@4UOO`y&iKrcr7Mi{h`(7>W>3Ky=Paa;5J>7QY6Eh6t$0L1$4%>z zLei%)8R8yw!Aa{76=QVEjI?K!yJ9VhFjhOYs+A>_#tp3;g4Aw;4P1F@y*ZlvGsfBH zaM$rmZ;KrsWsw1&0F!hcVkLy0OOX%=>qR{4A4n!5CI(Cu;@zmXI(YUlbwnFnIQ-&Q z5xYBvOz>=HotJpSVhI_T<)*rn9E#9a<1*h+rBiDu^WF`iYP~8-Qm5m1$PJ)p%>k?c zj`f!9F<<4fi3g^}%%&}6XmshpU!;dBWENi{ty|UvuRMLShn*Cg*HprFA;CZny#)T~ z#~YXUZ}2&05u8cDP{TD*9gLE{|1tqzL5Ale{)SSEeA|DM>6Q7)If-&cR~mM^LI%Yo z6wQ9ONp|T&AoESsNkRxN%k_6nl`)U}w12iBKzP*8+gOIO~}rY3n(C@mfm zF$KY7ed?%%^yVxMZZ-t~hfoyu7>GSOISuI7BjuNr+Tk)Yod`KU9|wurO25i#Wy&fB z*DYn~dytUQ>vX>vrNL8vPb8aMr^iLHf66psV037OXCd{+Uj2CMvq|0{#>6e)!G018 zGxW#&n*8QGQcQ0hhe?qo;VK>uRf8c~sE|4>ylgm!0HBFee2|<##}jtGH9~^FM~jHs z7xdbS$Y3}8llEPihp{l@qSgY0rK63cC9_)X#TO3IU_(i28^>AUcZg#{*|FS;iR=;} zzPg$a2?obwcfC5l7bAIQpqyA0hA6^K7HkKdVu`R_4%ezf4D|z*{jnDw4~IGD8FzMj zzZ9@-4AfAa4H4Z6BP&*RcKJyinP9Qg3g<4CA;!;!a~ygr^zihDMi=9oSl` zm|IX}B`(zFMS*1%!(m#r^NwcXglEDwG$*K18O3|dzv&eOT__+>RJn^qzWS$35%~yd zL0hb;8OUgtWommqf2TWNTW)l7>4x@C)jm=qG1KsotIX^RkwgQ+RBAZsgha||72r?%>{56#hOLe{P`zsa1V{|KgVQH zy*x|k9xpWXj+gedwHckdo0wTuKrE;BEowI>)r`sGILtD@;;r2nacsvXtaXpDoRV!{$ZHau}U|S`@m|t@TnOm1PVH<}`(=ZZy6uc}WMzdys#tkPei}b)7Twik+Yv5axD5@{)cE|{ zb1aCb(qF-wy4`g-Ox=!PDqTW8VT`z=fv6-pnV^R!2CeNTp2!$Zin!ZOJP+vsVx{~K zv5sRdcEJhDjil<~nyqc7m5se~I<7eniwXXq@hnQc;;()0s4|P32M-6Fk&%y%8>HnqPLaS|LZ~4GF?j zEW=z_QMv15wsM()FCsnDUn7)Jc|Y=%vW>=zNezh;)lx;e7iDBa%S{|idms_$_nr6L z+_HfT_S4c|=Ir_Y4`b)hn28o{=~R-6ZQHhO+x8dRwr$(2*tTs{Y&#X*oF3fnVgH6b zS^Hgh&NzXUhTaEH*RP*WtCco8RLR>`GEt4j-abO{Qqn%-W!u(Fh`34qWI~@zFxPTI zgTy2d2AdFnBktvuedl0U?=B1-l(dg8{O7S*^9+X|RUjceAD>1nPAVc*&pa9stR ze-(s3xEnV>f|cbw^-L!!@fVDnKw?nc6~dyjz7#740*|_@^@X!c1nzTVR9&>_Xx$&BTMzjkcL;KTA7H*2_zf zbC<0{$?SyE;1+yZ!;|6tmI9%n}j?=TJ21J_WpMtlt*b)O`yELPEx8`tXwg4X6RD0i>eT8^!V_#6iEz~Po z5M;xrZCvV`*p{9s&Ux}S_z=a^^r0~2L+MI^)=M!rBa{-|F#nl?A~Yr-3Hyptp6ty2 zi~_R(#=!9YpWh!sq*%+Lu!j{wtyVj(Ed8(N@Y&!c$XN~G;b0awxT=5n|%o? z@1z;z#`+n&UxjHM>du3*0TCB`_|_~OJk;)cE+bVJ3P9HNKoz1Jl z7YVE*!qI_A<0$l1mdo1nx90*n1kjvZN-4#7)G`*0_Vj|t?o|F9AxCli0yjzhAOew8 za44&a0&7CI!{b6_L&58AGjmlEX_JNEzvM3`MR46_=XDDz#5}I{<#^^3c@9LgYYSEn z0NzV*FX11j2hQ(5UM%3pi=EBY(_vb?TB#poayv=7@&Ro(<8WVb(~^*MeVsa3kqAF9 zm%*Pfv&I-(+av?kF!I?)z^o-Mn=gf{S5a#gmz>P4#|J$h8(~z}#E#K#o=5D;1WD{} zePNbgPU=s;$$p7iM>mvmt8r&kPzPuoMQ?E%&6SU%&< z*v;DZTW&bb;pGqPGgZ4GL;PWs4Im-!T2ti13Kn;z>w;uf3AVM=tFet?u>wb;72$VM z)BXcP4>5RAJ&-^aYq*W5;od?KBSv%iB5U15mfbOk!%%mHe9Ug(8|N1ptj{$f6Q=9} zn6w%Ex`m}}#lNU0jHsDDWs7v$j_9fkiT_i1jcaCnHrQFQQ%;> zc(flxlUx(j`P7G~wtYvsSSjlw0>K2eYJHIC+vGSObS44)x8BkPa)%|Ds1~faLIBg& z8Jw0P*&}EUINQ#dz>7YA5Z=*%l!%D0LF(=R5DUkUY|aRqI-8klZ+Mlq!}H>R#QfzF zAW!9>RNu9Hvqgt}hcLg-<6!c*JVBO3gBm84vljvWU_Q)G*q7P&WvNvzaTI*(^f69r zbc-U7Q6v1?X25bn>t+|YDbB>z$F=0-eqD3+s1fiJCrxhJ)Y%rj{_OVDXQO(_7MV{k zHm&o@E6Em%(R>PQ$nDmWbl6$*Vb z0M=5B1U?Puz{J(Iu9c;E)m)K-pHm-h)h+p*qR9Pz8JuGCENUE}g?vWyp&PYhVAj9s z(C7jTS=fT4j?{5Zf}4QHk{?M4^cS7l9G;OpMG(5>0sS$eKlaV}L^j8}r~iWP9;wtD zj+YwYKIpT*C!4;q&7Mzek2-h}?`!YE(pMbPry=k+tIrZ)DWNX6r7if*J0ZZgN*s9R zK#0ry_Zg9*7j$wQ@SQ~&JUoVML77tBG9 zoj_KlCf$1)Sqp!h#Z#kS*~W?7X(^Nr_(Ib0ZA59eZBflE9v=mY?jTVZnhuQD9du4S zg@0X5lF_BJGT1!t<2!q0Ovn`_AsxhlqL|4uM;x`Sq?rhR8~o!s6B-wa%r+a=lRcgI zk`JpOybZ>n#og*d9P=4EG-vb}=7T7HV$CQ&&_lAnc6hx8sAtmT<*;hqr{& zG(7ut#Hnap^b56uHCA48Li+O$P-{*cNM44tv>7lD-Fp~-Vu+|P9K_4IxgqaZ$51c3 zS5C)_$<-M0VaRH;>0qMmhfX24<1qGwAGDwH0wL&9=ZPuWK64c;8rC+tF7zSt^d1~d zOs{7hm^NePoGwE1THQUmx#)Xi2e_BE(1!$5>bq4jUW<11AAX{@6N-+NG03v8~LO2v4$2F4l zarBkI@ri{NHwAX3E7e&eC;B)+EZrxs`aw443;*R+n)hLLNj`_fg~ZHPcx{YsUO9)6 zL2M5Sm^f+9Cj?8)BZ(HDliNCz#Q zKxO6ewA;~_!I|XZl6oQD3ut#h@9tWpwGMuvL6m!BwT$c&bCnXcysX&>enrrKnLO^F zOy2WPCJ#^|n&Y2@G}bK7u#41(4!q)6v*0Q!qs0-- zAXh&%b%RNnUT7C{LZ{_Cjmix+wq^|so>$@+t7j&C$w9wg}Qe?eaD?#DYlxTNu?Nt?2eVRKcsizrZBx}D_e_!Z(W<0(RDm~B0OPLi3 zieT_N__r=Dmn$-}NE44qz**H8PL5d{37uC9aE+0fF!rip#*lmEx3O;pA7BN<48C-x zpm6YWIC2$AdK3aH{kKX0>p8p{;qe7ySTtFSJ1)AccpfK#cyG_ePZT+A9Ru7MRdh;y zj>$)dZ9)Ugw$N@;i^#ez^HjylTJ9nIc|Tz*jJS^E@NT#JZF1gq8}a39}e>sx>RBqJ&c4bhBIv$iwDZ|5)FLI z!+cM-(^GXbwGvng{hXMxlgZ?XOINFfNNV4?W2I4IOT4z(iw2EAn2hT^svaD5TA4z{ z|2N**_2~walU}i57fjvozT80C_W?;$EffHdtYqU*r@m%#fw=?kU#7?XkY+ zJo71Db<@Ybt@S6OCCrIGs(ybdRdv|ds5TDxKhe99W&*1jBVY!n2wd#pb!q{&tBZau zQJ9c;{qWUjr@j5X5_GIFMV1ih^jFXonv0YRr0C(bkJiDRJl! zH>gIpQl4#tW5Q$^Npf}a4du=G#}Y+zxRGc| zf6GF58Y|i$Sl*?OudeU4;eXZ#Iz4j?Hh7ZRQpwP`)yp`{#lgjCMs3IaKo3BxecTh2 z8rpK5!1!zMeswQgSZm575$?H_?Iwn^&yfH{NfZkT375)FlW6#ZV99mq|8vEz5TFC> zz7o!BIjd&Edk1+XHrR===IX#4Meyoo<)>I#EzisqK+Cb;XKF~84q0tOXo}8*by?l0 zf@PBYl5q?RtEM6;B`tS-*7E1t5p&IA?Hrv~GM|;X`l{RkB!dBNT~Ah(Zo6^q_9>A& z<3f+@Imt~Xf<|mdIH(!p^tPmi~C+1M>=WC_A232w$0 z+Dtul(xw}!d6Q;mJjwDt|LrGc%zRn60dp%gOCSpAQ>fFHY3=x_dY6$v)ZT81QiJ~$ zwknNm4-%ta`x~5R$-6?+b9y+GT=ri1npte9MniSG$M9|sM={=_HqDJdoE5tvZaxDh|Y%g`}d(=(=yUBQW-W=+L(ouYG9)wH|KGxvN&qR zB!mZJ*o432`>(My*}%}KBFv_$XmxX9?6lySqwy_ed7{ssGSM{o>-*m?auN3q&KFr$ zc{yxVKs5(r&(Nb4=jNn_bnnI>_vPx*UJ|{&`YyGeT=#fd(#0G1NW&g8pNm_k zX=b=QPa&Q@sN1gm$&DJPSdNR2q3~FPl!~w z*%Bcgt%H)Jru9Y&>w>1q*rsMWZoF(cKKd=!Qt@p=Pa?gP!vvR9|Lo1YJztSKAz&9i zI9z;6D1X$N@?qo8AnY=EiWe8n%()AQ5gdVeD!TTqS?;p8jd}z4~S}I zG>lV(pqFEfAKA<5kwtP!+11#3jlUb0j`E zbGu|)B@=M&XHMD-Xfs8KYIhu&YTE!znw;j8C6o?7$3Wh@Nzeu zRz1>W7=-u&y;)x$MAHF@SrTZxu=~pRC=jlpoBWu*=7@15Nm>-vp)>(})av^YgkU?P zM(?d|*DXlq^CPe0e^!4(dBd19n=K&%=?)7Z3?(dc788cZAfqH$6@-UHO? z#87Mr?QG1MHDk&Iu;x6sS0K`^NouQdPQ-O62Op`(VuRCDjtWQHyD2UkA)dz4A|t9t z^pKXtb$LukLv@{l^(XS5PNGOy0Mt}IgaQUuz} zSSgs6U)>MkrEsa^B+Bynbr89GpGBswO}bOTaNz zRkBBQ4iAuZl2p*D84(E@dU|8fJSs+S(H3UB9gt7gNc(rE=W|xQ4v_EUKdX^G9%@|- zz4Ljy|J1pu-p*;%n#vjGj0x1cz7{(henEsL@UW$+K9U;m0|&O%Xwko0h8+tG;2V1| zO-;u&tEmbGabKykoh7p@mwBDL7eBi`QO6?xQSWkm-$%owPGExfzb~Q(A@5p8;6W;;V3Bx?mi(VDFe9Tv9>ufXF zcRDrO>tnFyRPr7QRc(VMV51GGbgBE#f+e=uxJ&N4QNaZ<9AF{{?hgX=tM40;5 zX;ttoSlSLn#+S?cs-?jRccN~|w`$(+qOVbLNaC;teW%=z*kgt9x!b@DYrE}C(pqNN z0v-Zck~ttAxH0K)4o;LDJ&I zr4JH!aX`w5veS4>mD;!gUKI@FqB*KUUB*UHtgY$oIkcF(mL2cmm6evklAg|u4z;au zvc2gA*j-~c2#ODuNjO0-z97j2S3&k}!m`lHpfgyd)_OKlFwWDTqOh<_tQ_XMm8`WW z(bViXBv?8D>i&naa#M^iJxS81yv>Gd%&ZkMW;;?Uil$ z7EFuKu6K8{1&cpaJDzO5sDBStiF|CECtSKpG2$g?WeaI>8gRo$RUxwkVvV zo|ICRxZuBabs_Db;BWCOh_EeON9p`MAu#3?s+^zMhTV-^Wi?o2$c}_Ey3Hj||0C3}KfRg9 zknt)zwzR)^hN3Nzqe8;0kOR08Qwob>(mvQ^GoqIP3ucXYlWdZ8VT38*DLU^9>%C3q zuNq~@9og|1+6H#T5g1nMicHv5k!yD;=~2^AxDzLQxX~Szh`Dw(g6!ZSZ|=!M_g|pF zmL1yuf()gJR{kEZ@8@N9`ZUb%s9C&nXC*RZbeO+*WrcMt(tP|@sor0YKhiF8CZANa zY<3pNHM~@hR~A<%2x+nnB0=0u^@Z<3UU8J%X5SxtkqXfr%p0=8HsI}2O>(x!qrkK< zW_lNC6XG%ycjs3_3h=3nJ}E?e3#LpEx;3x1ehXLBVrYk>ch?M*E7)t&o#8dS>LQ1| z;en4fX%;W1N$xU>>(ODH)@c#ah#>5oO6p~>-EMTNL5_+1P$e;(Vl4M=Q?K;=;Z^cD zLa;){WQJFZYV_ntG$lBy5q35$Y*p10>=Bm_!g~pxvI(&Oh4NW79b(Lc83w?gWZU8& z!;|H*xpXP)(&M=FA2}c>g(F?8`Rq4<*xDVd-0Z!)#7z&#-4+5Mhw8|EjQqxsLrbM` zP(#)FybWI|C1jmLJY_Ay%i-PE<9*w0GTXr-A)B8TuMCD-;L>pTF-G#!fMzQ^Ru24O z64gCY{OMdtBFU8L&{b!ZCJCR@L1-jAHK!KizFJ~+gt&fLZc(89_I@d@?wbPcYjf=A z-A;^!t<2?06gOIqCh+-)77EU*B$t})h>{#4Yf|Sh42xlH25tv0;#e(uN7qG;8xZ|< zC}m7U**r&7IG3=^D6ur=zW3o*GpkhOO#RHl_+AlmPk5Ub4iMLEmxLGWgA?<6jneBU{9qz6Q z`L-U3s8AWC=Mgv&qG%RF5zKY#uFwVnYQ`KT3i&=zIFK;31AAJ>W0~m1yRBwE@KLyz zW?43&Kve+Vh%49~pkXzNoTk(Uj{zgKue+%1gLXN5!&>h4W?dpDe?oFXhDD&m#%$?4 zcz+6Mmy*k9<>~sDe2r5x&qPaXR_d#hbGO@s#&#ZWHXeJYMN_fPShEjfw&EqSXUla5 zA0vNfgJExB>0v!?#5|6%*3xp6mVdi7=XbfSh#G!(%%#SC+|$`Yw+Z>VVk)e=es^Zx zvhtV~L^=`;&(68T%gw%{L($o$RhVI+gtCl>NcPlNf0qYkx*KPjsV_6~VY}BkVm_z< zW?^oYK|`nsLRc0nQWv?ajYdqJ~8|9PCBde5~4Z*0^gq4*p#kao+@KZ zKdvkjK;s-Xg;EfdNHx*4WMzao?0HTw#Zt&Fs_Sh|$fkUCTF4B3o1l;x(UKtRijD=1 zTrNccHG;V{*5+73HG5$Q+R6IfyX%{ z3S4W9gt?CeHt|XeQ9)BuLGCB~^4KO)juY}$WEd1S^#!YCETv>GaZ4BaTHSa{(4_!J zDO{yQk9oFtF6@68ADb>d$LmOeyVPqq4ke@ux&1D(IPl#7-+V0S=SC=TI#nqotmuQr{+!bJ3tBzV;w5VR2=kkZ-e zE*kDJ^DU~|9Y6FMjUnB0MIbVZZOMP*6be?gJLQCPb}2*=%@c6Wvs6n`;2BfG`>e)T zL(>cL-Am=|9sI@Lo(ai`Fh)H?=a3+PGPva=G}6CmVx+Z7F^vdA=DAp6zgZA`*q3X*!S;rvxIKy}})5n#8zw6l3x(ap7u4y@g5u zI?iB*J98vKMJJ@25$$m6X<`fR#w=GpC65fm;hdu{rQ$rJBrfT%JnMj^lpN4O zNv8cfN|0xgU&-#)1oZEyr*LG`)W%>$a5RnpdH z(ZI=x@Ra$njA$4U!=Gw<*w5*yfg?|iWCuF3H@)Ik%?@NQ& z9sNyTUSHVCZbW1+RLwC&Nf&TpU#2!b374%?X+SLnAFN!gR7ATJn~$Q;*t>OP=n~XS zwvRhphb|;};?2N$Dhf}@fjt{;E@!cT+We8tM@oA+dqS^n%5f<2%m7SPJH1+{x69;N zSTxgEeXi$nkA7x7^9gb+wb+jdruQYs%fQ}!*Q#rb6cQGO>-q7h9!II&^0W5p5N+{L zUuTw?0f!}r<5^W2taVGtHA#JJ*?z6M zxIyr`5z}{yei=gp0Ya}uvvG|Xwy z8D;$IG6KBa}jrG3d-RGjAW zOF^od8ybg+8kn{sn}dL6=*^ImjH{oFQgCiW z=RHf9;ARDuqKv@yYLrQSmn~1PhYyvve=mh=a}`1%>7ZQ z#$WN1Vy@KHb{1jHpV9APH!g{?`4LCee`>9*hd`%vHM6+M8%k=|{8W&0_(va!{QFKEt>-o^3h3y}gfg?1Z7IJniUPO1Gaw{FPG2qWF7 ztIqnz1FES}3C%6Gogi(?R)ywTm+ki3IcwT`Y|A*yAy@w_$J=*>`|V@(C!x%KutxJw zqmcD^t$ze|IHv17%>e+wgh`&+@Cs=L3QL$~GxC5W4T0~>^pO=i3(FxrfHv8Ik7fKE zS6sMJu|4-01_^Bdzwon*{U@MRajVBk+}wphm7%!5+O5A z?Sc++g6LTCtk~ePQqric7~otp>kYve0pgebhdJSH1e|58leOY#w88$t@BwYe;tvnm z7g3gL*H}Vxu7GPM8Z*g`bxI=Np7nq*?ynd%VN|!``#pq)IxY1-_3Rv&x`nO3hy?u* zrK=kx&!bmR!jT&5(C-MiSFPE!A@jY9mC6Wfgu5=xa)-6ZR2a`a-=iKI60|3=uZIUL z-2*VePRF;-b2$a-m~Q#9E?XA$zp0LeoRQaNU#YjL!JLA_XEfeY@N7-w%nF8>Wb$aB zJSvDuYgo1{4h4l(WvTbT)u-xU;q@VPRJw(Kl+b3+5ViFz=V@i$Av?4bSB#mzVxtZHcUBv)M^C`(nno zuH0@%#}d?u@yqe*pQZdfIQ82H`;P^BCh@aMNMP`Q^lTc$Ub$Hg8I9S0GCz7&``4%S zW59vN%!eq7lVdM#EuThlZ-;O`yOL2(u%AHI zm{~bJ&p0a`q-N*<^V+ZnW+FGCXa7a)cyuD z3*OI5+nG5Ft7!G{InfR3!TuNcogoo?1s4M~bmqRlOTe&#r=^~`T+m-em!vnjZrm_j z7puUp3V}W9NlVvKB+n}n$wQf1>+aBpktivXmun|Q&L~0)YKv>5Xl_NtO#?q;j5JJkbk}8utx^9Jdp0g3u?@ zHrf$bje-=;T2(XkN=Z|U9aq!d3G+xRgu|ULH^>aNazOb*i8TYzmoMYjIkQ9y_CT>` z!-Pk{cCDF)<3B#vJO21jiGU_9OvNXpsSY3pU-Otam}a|!0Q}w8eZoG(R!{JsRH_=7 znQR|ex*zuWW0}w2QbtBj05LJM|Apj!dvE-vn3iJy;)czlca%rsjjBllY&BjksE3sc zI-7Y#?4iV@R$9v>y3NQBVilAdaa_pqS)^(FRlI|9t&W{01svIHQDV0Y^Cl zheMm;$%WQ8bHxKeYj~#leA~`p(Iy6+y8sQs#lpL(mWF)m4qm;VCp1EGTM`fD_c0S;=Xa z{LI+++^(YMt?19JTCOqYA6c$_5Yk~czdlXa-cHzN=2SxxFB8M8(`{It;XGRhC7x0+ zdfz)c<7QzUwMxC9OBs{vH3MxiPqfJra44gaUHmfUjNzNX+B#@sN;MJ-52xb7CG?k} zj`=Ib(mlN$dARP>h|>#K_N(Y2z5E4ikH&fa!0-+RJUK3bk;;MzZ6o%CP+)AWU~70* zFwS?EsTKGOnR`+hR+`1otsJSj@tC*r1GkH^=2?yR{1|x9)qo}xL#WkvhfU;SqhSPYa{gCyI0pEnU z#p`Ts6aUX2-0$P^zlA=QE-`0wXd7_j=wxOkPR*6d3bQu+%lO3CDV@fZIhN5x7O32+ z!T=nTUIZb0ASR(*t}BojhauIne@)H1syZzOKz{g4HuZlJzk$J+EDor|X8z{%!BsDB z6PJH-`;w>5fTg*T6( zLpUIbekiP5trO!1EEva#<9h&E{GP|%6`!gqXNy`vX55&4d|0Yg7W1E32N|E7St0RL z2IDbF!^JM@+>)`%lN~hSrof8F2thyq_u``=7oemWZzz1~Wyum`AKyP5tHq0UZ&cZD zyZohjuAa?>5#UWaKrqiR2I`Wgggg6#G%KMa9uG+!9Rr{ zEk_$J=m$B==^6V9fk7ws^ESiU;Y8`dP`#Ly=(p$%GpyBw7bvS00V@JgUnudCTv8Co z3Y6QFK|p%;F` zxMfk7k%_Sn3*r+F(pV8{yy@P3(9A+xW-t-pU;75srXY~jS@_l9>MWUU!Xqg1EnvAC zhrZ<0TonG3?QG3p+ZGdZ6UY1NzXsL-+iY=*bLv_cd3ntYzR!pIi4U0tUDY**>~=lx zckio{b$;G2e5Cxo4Vp&lffO(8wSVb~D#0tr!OKTUC2vza$nU)$O#xTB2|LmkU zA!Oi+HhY!jDnXdbm!f|hPoE)B#=qXpr(Y+yOx_=062ZPqJoNd)Qt@S+>WOqsd0k$> zORWlDuAyY{27L|q>)SFS2aQ)`_7k+%rpctqq-3f3$5vyRjY^jam+d)m1;(mKv@*;}l56orr2SNp60>)Hj-7ikSW6FBvHEOz90Nga{Qs$FuC zKI+Y7LjxNQM=Ky)8f``VJ<$`dFBYRQY)D+SxS+ir)SNrLyHwW{)^M>opuNxFXvlGi zqMgI!tBGfye^dVUS7ZM&k(fg3%(Yh~rCGwbuwGLnG#|rYhr0~8)=KWnD?7DkVfb}j z6a4@nPfy2byvmn~ozG%U&J^=`!p`5L+vD>v7*)Kt56w7Pl?2cg9Y_qoi_M8j1^L(5 zb#+gRGp!EOj(Y`NbTr)Rvd!$0$kfw-|KuwgN_lBt(ynu-+G;O-Hb`I1Z9rk`g^Kg0 zKgS-9?Me@PD<|zR&plvAxOvkhQWh&We5_Ft#$d@4QQ~5+sGXDA88L`uyfFu&AKX5c zISQjVXv%@>myuddb-3c;|45!aNx0xVy{*?zm8Qff&id8-ujD? zFXWn1JR-awHu8W0eOs$s%hMXPlaA-%jC2$WrZb}v1(ys{RS|WsS0P}@A9h(iwJFKlMZ#%38Xl52WE1k{`9eS2T<3grnR!-*r z_JaDI-vK}+9LegC&+;&?B*ud<`%U4oII=lsx!|XDOpvME8T75E4`mb3 z^^w$Kx;jic)mCI{EkE_E1_5%rM#owj>^k%q4#~h+)w00PnQ2CaiMU{1Xh4~ zDI}e8;=>;S^4R7{fE>Tui!V%|kANs7Zohz#F_wP>w=YG4&?yD(Vo?Jep>QbVV z%`US{$yrC0vE%|{9o_uex8D1~)1&j__$sb$hqPCeyaTH(H#W$ji9yfl1IgTb6N=ec zN`*neIH0kml7U&^R}sJJJ*iLg<%VhdM`>RCrFggrscxJ=jI*)saP0LXS#*2hafrI^ zb7k6;Hr$&wcmCG|dZ*LW@p?fG{7Rr**W;kvq+F7h+WptI;;_E=UIA9;pgnRQh!EO} z3KIsEK3)eV9HlCiv}jOe4NBe9uQx?2bzG(a-*S(s8;Ja=&()Y?;iaxn<_!v6FJPOq4J1XPkg%pzjj2tu8C!|4m9Pq3afq$C( zUm_Q7361)!Vcm-iPh39d^^nQbKLCTACo$jWkNw1UHE?=8RlJULhg_H0brVaR>pzD3 zml{V&wAE?jIJ;;pR%PoF=BTTPioEOYld`COMZfAW6=qt+-bk?ca%j-G9V&W~4Gz6D z6H3fQ$GC3W*@v;IIm~lpK<6xSIn>VznNY2YMo^*+2ur{q3JCZ!f#G^CVkf_=`xt1h zH8{{?vQFFti$YzmI0JB9fCTY-WDH zUs>pCjT^-hPMAgjTUe3IEysEEHfxEX=?&_QZH#(5+SP3 zQlmBh=psZk+wt=9@PJ?4REiruk!vH)Fc0PlVqGc>KI9G+!Ex&%N)5GX#!$w9@dM+d z;ai4gM&y_sg*ODo7*&@)np2Ga$1wgqktNqNP-bFd@<4dmZ`B#Yf1I%o#{&Tfnu~;R zC&__2vUMqZZLyf(N!+eVB23iM{fzf9U9RH6eUki-_Q6M45K=-~rr|ir+=wk&Ng=ZB z!$r74n!dI&BIX{DE|J4+sE3)1_@d;|hHw~op4Cl00A*m-x@;D} zjqB-pX9mzmf|$)I)y7Xpdq_WhDvsUrv)>BsEno%=k~GYr>gH=3Z@$gFq(l5(u47Xv zvASx31BM&OG+<7~e+pP2Agpxinu&k4yJ)Tzog}45U@77~rIOcHNIQP7?BwwHp`KRl zD^uQ7pgUNV0VYE4BK#E#Evl6ss=qf%P2Z&>#|W(eF?-Z6u3X?H->5En|FoZ3%E8Yw z(Mae_A0o5=Harntayi33*+Uqh2{YwT$kVSVd;xUZCuWWZ2l%R;8=Q;3S?z|f0ySVx z%+DSTuxE@^3A0noZ-p?&vCvaO`}&@z#9{@^Uh2VX@a_R$?&3R@mgf^&V=(7Q+g}b7 z&r$C$g>>g$w2tjBJA0vrXgG zul0u@K?buV{6-yP=^#G+4#c;oBj5Lf-Q%{Pv=p@dh zroh#GLdul-i%Uv#Jd-W+r2j{ts6rUL7Y}^-!5j@UXVRN^ns6Y)0 zwv0u9u`hp4UxlCgqsa5M!P)oNXo*8ABfN<}UnPgoGuy;N@dNtT_e3peg&{`dl@F9{ zU*%vdv3;1p0->-4mxD&~qOqiW%cARGgG>@r{R7{9(&NFZd5&HqpHzPR(eb$EXC2G6o*~~nj>s)pKZQJs-SQLHF^HS^ z4W`h!f+Xd+f<3eg`P^)u?cHori>uBC7O?|tK%o632hEs_KY_&IZ=ySi%DgoQ2% zE}4U2KQwo42rkWF*}0YgtEJ`e#b-RBRtdUU_K)r6qe)84VS+2q49|x0W~|+92AT+# z$J3h+W*sS>kL};G)kpCE%i;WwAYF+CR=k*3&xIGD`q%Pl6ezco5MI+O z*@v$K%(a)_c6aYAwK)f>5;mPvx&}Y9_f!CK+Z16p%J;K?XaL)=oc{(#p?w2-a5XkV z*KR002o9P@rHCeGj?Zap^O)p3n~p@Fo${|Y=kF0+FTh-cb1E4I|8kJU8b=;qk5f3s zh&}VDshxnWV9Lrf6VzZy3h(5Hiq>h3bAxL3A9KvNJ0i$Ha#F|NTTR4%!Uf zF9c5pLKKaIP=ug4IYtTeEf^TG-i={@6s$v8pn`R`b~hEz3KUzyS> zJv0wa$<(CzdV67u86KJt&^|!$Cb{%}f;%Sp*s8_|wt0$vq~I%ckpwF_hDC*WY5=uD ziGN4}af4f{?IAYyIB;#Utn?)Po|30{ut3nh_xH)nXmvba55SU1t|Al6NIoRfs6I%P_bj-iZB#Oc%_-LjEjJ-x z!MdVRO?#M_g6aV1dCOu4Z!w?WpD{<6Rx9_j0`k+ZGA6G&;G;-i{(4@nXU{E{cM&X- z1c*!aYe|eLu2*pN;#_?3Bl9I4GL=ay8(Cj{Cg1-LvwZ6ZjJy0Rpym@B2*^|Z|Cd?L z{J&t_5}i#)Y)MC-y1H`HYT zFHrt9#OF!h4ey8t6E#&g5ZK5@?s{yZzUvMX(_9`8O5cyk1F8jhvgrYmue)K7_sbBr zJC=-)yZupgQ9gc;mwo!YrBQyJE!WZBt*1w(vXh%@e*Kruf5YQvXRE8*i;GcrAzv41 zchk*$c6oWZ&tUYAQKq?d{Mos-Y+298F5HsnWe0m_#CrVvqtMqU&op`6`IznoS~A(6 z?oHpKH_A216gG0^Ze9F;GUU7?GX?7agGn^d2)@H62^a!A6kjL zU+>DPd-*VrHpjA~Tdka&rRt6i@iOG49{dhU=G`prZ_F|;ssi!y(wz&YPe|TMlQ_Ts zk%bQe;XD3gfN)IIElo=2-8WDdz+NC)1MI1s*F3A=tlvA5?iF>y zo2$hro4BqfUqnF6$)B6<71iPfzz}grD0Y<|FrYzaU_%7dQIxN?@n+8phMu(}ZII7ps;Z z@65rD<5EA7$?dFvr+51WYt~kO5^tc`-!c8K?D-c|?Jm{a9m5L*v-%_ENmo9-jZR}(-}FfB@Myq4jCwS(WrX8!LD{m`|zWlSVJFF)E_ic_bl1*_X*dV4wozw za*dD=rb=^_vK>&&0`R?Oy>KmSF6}`q>tKp_nbkGoR{zu3IRMGBZGXCLo$fwu+qP|; zwr$(CZQHhO+kM)$b-H`{zWKj*??k+qnXJgD%G!HDGzCZ?s zCb#$_N!Gca8R8%b8b)+;9_zrT%GIly15%>9i?KhO9c#5kHL^&#evnjF;pnJJ%Kwh{ zexK>FK$=rALrh?wNSlRF{sRQiu`W(a&ahUdcFZN59e-Kr|De$UXd-|mVUE%WlaM|> zKpo_w(w97w;aEI+va*XMnp-k~r-dfwZ;ViBZgxMa5HB#9KqkS`NEifqB*XNlFG^>e zII`pyAovbke?$&xu3M6qo6l=f*Q1eHLm;Z93fiCs2M9)j2ti&YeJLke$AwvazC$15 zY{-$YfYd=_^Ek*_bCpk?8y>p4x)GVSm8CzEzrOWjyVwdOcihi~A~>?a>1=m#i)1M!F5)!~wK)NhBIugb|3Kxt#ba)N-B2fiJ8wq$h;7?O;Ff^(@-1w2= zrfvMG^ZJ#QYm8?Ld;y#=gc=W~cT#RsK6lEk1={rmteY_aO1S~>n0_}t&L);iKZyv8 zhePIQXD2+5jz1>G`oj(fqg@}&n66+(a32mo9-By@ z<_IU9Vp}|wo(V{A=nKI_b9}e{4D;N)(_{>jL3gARFOlTjpj`|| z{>P^0^O`6Ae^t18b(n zn_(<~?r1G2Y!-_S6;XE>hr?8PGmE_GkjaAppDxGu4M3}h_D4Ox8p8V>dl%_olvR8* z1`lZwTZAhQQtVW`_o>TJDgQ!zMEM*h-GMMhWlG^!*Hgz+^|H6M3YC@$^#@%Ap4(CV zZ#Mr4`AG@@`BQ}*p!hmMcErhbB8$||_(m5M+c&emrXMOrjV6C`oO^yrA=S@o_tKv# zy1pq1P&{)@u-G<6tFp*NxB>2SG?xC*b5Xj`T8F1EdowQ>#0Ns@>{vdw6Pwr%EWL!5 zcxA2&rjc$Gv5ZT5;OBz541k$dME+!7O}vFW4>xjYtNOzv`GkdGHr4*axC4>^JWNvA39S2YMoSB{c)Wr@h?lOR*vQpaU=nmeLFT~6rr_m2<+ zl>P-&OFVTD>mqa13(_Wj?`l$}MhuUir!rD40dHmV~=2!nE@Z#WfqY*rNLS!U- zDIDI*;A~oNJgZ$I-H_!{<{13wTrv7g|6Tm;j|BMiK}UntGbp?+Hy zJ8Olk%qcMg)V)Mem^GA6lVWIPRQi^Lgi1e{Zor+VOqm*Kc%j-8C<9}(xf$Oq`1gUV z2^=pTAp-@pJR`TML{M=*sADH}tQOrNiozQgumNiNVkN{-(y35L`&#V;N%>yNaQ{85 zX6Ym0Tx$L-!m7UAd;VeR!15DYmI+&TyzvSL-Zb|8t{ak%j5AIfAX6@F?0eDQ9cgad zMYu^y3ZCx>>9C(h`MecF^~TECBY{#M8{1I!38OXRw_;~o$JPSkEP6hpXQ3kaHsW6p zmQ2OMY@#9%y#ry@`1<#)g7Xn#ZfwvTpz^JqgyhJC;9fZd`jBZq4Y}a>$RuqQjmpDj zD2e)G6yb7zjA2=_F_Ws&c@5}kg@M!;3WhP3P{n&vZUvx*#^kzTt@n-a(T)KXs>iR{ zi&Izfyfr>AjV$aW2y&r^5`~E!o9*p-F>c`5gEBK!T2G<7Gd62nB=FU3VngA|u*+1e z|4P5A0GocSjE$~Mp`UNXo8&s$mQH8X@L-oj`?4>zv0@X=IVsfSqV32X;49`_KrV5g z(H^skFjQoJVF)u7AE}C$>+6z^Y{+oO@EX3YF!e#JIMUQ5wt-8`eW5pC?ThB9;9yTm z*D?|C#|yjbFzTg(_AsU~i&^bGq`SF6CU&Ykf=S&YLz3W`kWS23PWetd+VD6hK5Tc* z&YegyB#^(uOE{!!a2Y=6l3Q?4b$fwVrD&A=s(U~!=^C=N8}bZ`bNS8xh=?M;$|c7y(a=5T}(1`SEA!-bSiXB2pG~b92)7;6E+<|>EO0AXNE9?i-)wZ_}PYUPCwnFE9{q07m* zGv#@T>wR7UB zvtEgM>6V8w$Bo;gOsb<<9ntX`A6k^TS^<(7UsSXsTf#Q$p08YF%=8nCU^#V^ekv{H zJXoqB^gN{8A!cmt*uSGG+?aaUpNvEgt8fEl#G>p_q!)psLVt_kln}eNSdK_ETBvr} zCsE<%k;Uj0+gup($#~bTO_(QT6QyT%StlNLRBXa13Zy%W54oycq?5vIrDT=3gEb6A zDX0kehOqa1hF=N{`((Ow<9dTDn#)8`id+n8k@VgzR6nl1ig=ot9@?AL&^Vp78UDcfGa=jyt>RVd zQ>%?C?D*F)BBf>Ca>+8~E!@e6^><0}U*$E0*C=(ltKiJE3c~0IN*_xy{qN*8@74O9 z*1*X4CWWY_B5hrX$#O>A387~pXEOGaDy`RB&QmSRn$tJjB#7r+a1sA{h*=88|J<7j}T z;RZ7jPof$_VhXJJ>FO536=f*IoqnKN zHBJKY2NX}W6B!#i02(tjWk~<73U#|LWZZY?C~0rP;uK*LBfM!SqbJY@8fZ0Pnx<2P zJ5s|!sDe4g<{V0NY}Ex%=ZlFw>esjRsi0pU38f27sv?NW8(%TqZ3cMzR=0!N!_Dv7 zUo!SuAgDMC|F{_T!+M>CouR_({SIP=&aA?Q1-~gD!JJZmi?4%e+ zwSWG_LU#32a`Dj5jB8x82voK#L|%%9J0(=RHSeu;&S(3+$9t0*Wn)pT6&zC2p^uPU zNk+zzek*~ks3nBdJ@aYcC!>BxA`ja3xj2EHAJrAy1Xy2gKKI{V1g{<9R5P-Askf zwk>?LGN9_KhY#!%iB(ycM3yQ~;ZK((CSsEYs6E|285ytbu{;U>A32ve<^B{U=!x4Q zz_Tmh=CvuX+nKD)KX{0K@dGB;QI7^IlB^LB4i>r&HzCxQBFku{y>i8=hIA89<`61M zQRFq9yDW6RC%)VyMlY`xHxs1=aIf~SIn0}rRV#67U@jF@P9icguW=mgK*N^R|E_%O zgmRfU_N2;HQaILFDNQ*Pr-<8FK7Jbw(W-qkp_X&nU?z(UVl3XJK!!d>fO9sp7a<2K zBOsQ@O|>gF<3ExX#YvpkxM$B`l8#Maa< zMR3?56PP+-$%eL8@5N@Z}+n zHX54{_t}p2E1f*>?xH!YHSCIW?_-Cr_85adTo+F5I<|pVrgLm+-Wy%mks(=Es?VS6 zms?S0)f>NLw_t62u5@B~9>CwK84HaZuewLwE~z6qxoxh&Q8!z+`%Q!ewI8fG267%Q zSNLsK_m)4tZ{O;DkhU@cpyg?`A*L%8)siPl{acuU}joS{%>r~WFypA!( z*)N<%sMWLI$83YtTu5v8CnGUQek0y;v`vLA-A_<&o@q!lyJg z?yYZ~69Dn->RTVUY!yF5`MR9gWwA^X$lV2#CW)K!VLm$+%lU(B`uoPjKY@5DL|?>j z)?3u82rycwg>hM9lW&|qBy#2 z=mf0a`{^=>Gm8CQ%)*wpgIV`}Vc&K3DwN}UheDlQ(i$yYcN4W@eWh}X77C$0?1d}b z;uxZnBcO}2dEs%5&dmgvjFxn%ZPq(*244-&C6Aj5c92C0!!coHESONxSVi8!Eg1vIJ!c?^y$Y}-ePnMAY?Wh?unI=OkIZE zRY@H%StWqdLYy!^V6I#Qw~@-__$4fZWCtWxxZ-W%?j$Ml;WV*!wWnJw;9e7*K53EoPZ(6+|2pMLwBYu{L!ph?->}$43i_{t?4OOadl1vQ! z;wGB^A*I_||7!BJ=RGhyW?QLG@G>tHo?-eJDpaVv%%6tR zh?B#I43b{ZosbzM(iGu|f50dhW;&K(62sDKUZ3;kV|f#;=&&nnG=Y9WGg zT2U4_qfQ#Z)^nCNV83h24tpl~5)SO0`orFyDhb>p-rm8?oHq`ogF%v{jTp*FljNF{ zP>_taWJFUj*>xN-YVY-#d-lC9MJCYFdfeW2pMH6=z%H&8yeTTdwWFVTs-7B)q=9gP zy9hvktegQZ77dz;!%~1~=Rwd{9XF>4x~U#YRF*`I;1?k}p*VNu6oErOKh^U{=r2R?0PI3Zi-oIQ%2X~D^1K2qa@)Z4+M(PV%W_KtW0Fn_)qEs6AB+bzcWAiwKDFniZ z27<>fR&YkTq$}%gQ%Z!hb>E2&30?Rn_a+7Ncn%Ih@yF@u=`X+9_yaRqw05i>!jQt> z!sajT@R||`4%ss8&vmiGS+%RL=t^p2ZW%&^=P}hPOkW-yY?<@&(qmpvV3?Sj8d~LM zFwdmB%YfFK7$zxc-cvDMx9rK|xq3soE2E^jkZvv6@0tLvUJwA@Dp%FjOZ(eiS$;Ni z2+EmaZZcimoTP>xuWzcDcg%h@0wspX$>>E9cyM39plsVPs%8Po-Kc4Pk)JKvA@=;p zi_qmrT=A0lIp-c0)ZGsTG)SW6q1|8oCvoDq{bg%bdqIaMI6=QhK{C*{(*|-`R!s1< zhlawNW3WGOjt)XpYNofl*;6`;fO`j&GD@5%59{&UV4O#?E7!_H+y-?RO5-Sp$OT)J zlassff@pYm`Vb3gP z`)N9y^N+88XXbqI@!V~#z1DfZe}*L7yIg-t)q#ie&rdNK7Pn6TI zD)*R|%wN@O0Soo^wOA#wnF^ISC!4I#XOL*LzgSycy#_CZ>M{mQI*@1~t)@&-BgXRD za#;=VKV?`sc2JK`)6vl`erkEIow2AxTA+H+<#q|NJE*+w z${zS8Iw39bNNLjQ(i+Kh_Jj~wolwD|D#lPcbp2sd+R^bx0g zz8&Ju)a~+iTV-WsVK+$QU!vtp#l+LP-^+Jz_To#QyiZ8H$f59X4V=P@%C5s(f~Is2 zQGGn_tRJ$oOfOQ+Qv0UjDB*=|zXrY{L6>o{O0xdWZi`)%Pt7`4`mXFEU4^^YM2U(# zT)Xp1TRF>Pr+_cF)F1q4I9MLrrUM>G-wp0~=wXOYXCTyJ8wa+0{*^~} zXhJ$-T)4=sh*vgIu&@$=mZ!wvk0J++3?|3OWlgJJXEm!%O6#t4MiU8jusYI0EVk9XqZhtpL^Zp?=+`N3;wn~LB_rQ{WW$zx0OM~GUOL)YxSBt5vz*en)$2S2c@)O z%vJ7IKs<3_fZ;e){kF{xOZB8B;HrvN>Iqpw;9;{HcWrZS3T-BM1?3(~v7bu6dU0`b zRgQ@85f5NN-~7Uuz9(M~>jddL97&DHM`H^^6wfQhYz~+~NX{FuKFFSQ zsrNat3S}7>?psF0p54DhKm-67BmV!D5gGm?BPOYD*l(~kdyS~zd5=X9 zZOhEtt=o;;Wk#DIawfS_l$e|?A`1y1uM_o=h|Qgqd`6XV93Q_)POD?)616ZgCsjMH zp+M^DtY~`(megEV$k$k6X4dF%b7x|6Ej3SjUY+eNEp%&0>2P^@70cvwcD1#Bye<@J zJG*`@Z2$IbZJ(7bd4Jc5p?!M(PGGY1_3`<%5AMarxw+lb>0O>f(^2i#*24=kV9A#K z+PvEH(djdD7!smM_sKKS@rPU{HVR(~TlE*48uIb+{?`jYp=^4_OHC-BA>m8vnB|7a+W+g4>!&jFEX2J`|FOiTC?5ByuAEO8@ov?rV7XFPW;>J z%}&rMW|_EnP*j!!LNhCGp2f6MYH6FxF3nkC5GC*8*$eYaF79jVLls>E0sAI1*|=S< zw*!4+ySEKr99(QKCP%xiYqZjJB^nyt-&sh#WnE^WdQ^&Q-=7uSOk_K-a1R%bc{8~@ zob7EN2IlS)_6r6I3QR_r_!zXUC@QHetuz+gD%Vx-?cpvdfqy*qUEG*DdrIx%s)?xYF1k9atY(#ef2c2rnDt>@d1@ZyIpZ)JiHj2!cKR#0djTxFlp9O1 zrhEN;q(p$%G8&0ajery=jf~`Md6I zy_2Kb$vTd*j+!O5zpBo*d)E_pRAs^ZFQcT6S@c50FPvl=jXP#+D$muH_Bf%z2_x~G zJwl~KZ#bLh*sSUwZCXowoY{jdx@$d zCNYa`9n;r}Xe2^`nVeZ-A?HP1J&!y-iSUC zfTh@$(6=n0(nxP_CeFX$I-w*E1>Z=c;fSS05f&%c#S&fYj)#Z{nQm)^g^C9YZE_q! zj0QUx9`e$#HL`#f&GL6uv0_;rgclN62FvOw?`H`(Pj0^zgu8)A>S+kCci^9%m3A_L zlcS1@?h}1q@)pYxO_M;O0_)M6pEFGQpntO?)`bSF=mv%nm<}4F%CfpOg;lLRO*9wP z;eKIRDaq?EHo3Ou@q9s-Bl0-`;)9mfvvIRKRHj*hQM+L1wUv@W(}zpDOY^=4S9%m% zIY{f{mk5DCAa_$=FKI4bis(K!-~hZUkMf591r9j>8x%)W&bA4_rM^VKke^qPsZ_$@ z%q9)#b^r@9;TrfZ!3N+J-UsL|K~xe4piKXR^PZT}g6+PPCFLYiGZcZNw-EJk@Uzhu@k4u*`yydbf-*t=b zhGkh*0Du?{G@5-`(Pr16CIX!{zO1n_!)sl+m>OZcT$MeW={y}XWGf4#%4!)(&e`HkWBeBl~t zvI(Kn%?EMb2L5~2oPf&-!NQZ+GAv4T@0`vw4u(16o2VP7-Nd??)tcD6T@bDalgGOl# z1z3XFg=bwq5BTv+y360zjlw`+8-b)YRq~lEvR`Q~yu5E&jbAB98 zpY_*?`JVN;+Q94aOS6=Ja)y`mei4)J{4&#fNp1%-i5P0MxP$5XaJRBbr)!Q_wez_P}(!!_>WC*oB}hP%YnRgiGLN7$?*%0@fGCSG{bj zl-Ba@6ki?2kGh0PcM_ou>jfeZvXX^Xz6e#+udyK@M(*CK~)@j&Ln&Tw)s<7-^m`4Bg#s9DgtLOg`3f-f}?sbwKK7t_z4Ad{uot$lF5(C{gkpVWN} zWZ|lK-Q;8S4LCKkbCIaGzs4cS15Sj4R4jpW?`=zAV~Q|bl*G&9X|f9$!%<#1Xr--3 zWJ9?C#&B6D+E<9Y}hN7FR(HiS+LPt;)NvI1#G~$vZ|pw2e0^SEdwE}xc5r#r?*ML601{V0g+Z}0G&)rAZish_Q2F^tON2y|^t*e( z$mdGW?|X85K0O=q+G~*(rx_Ae=@kzui-O=n&7T&^9|g?R8TRU08kKhxzd%!uf7wV7 zLDxqgnd#c^G!x)QOqE62HWG8$ZPxxy{=?*5udFNot?lz1->|f~h=N06^Y{7qUxhlIbKkE*dezn%Ra5oF^(oaGe za*WQ6h7bpZjiruwBZ^F0oP`ZRn1BGS@eYfuXe7WwAIYyI-4I5jZP5-)SqaX5N!4^G z6n68l8SnmV|KGY~whUm7W>}Xw$L9&+{5Xm$RcWb>jBp=4NPcvJN5Qj1=G15K#(8Oshyq z5hzNl^v_W;)8g%Ksf@hLieN!xosCF*pe_kh63N^vGqqP)2{sgJuNyoZvD4nHa5yzc z?NaJvkREo`53BLymkTx0vce!**gUlu*0|;`EneU0XgLZgG7P{BRT3ti^wXH`CnkZl0gXs5XziJ?soVz-&)^Tjf&8#1hE`+wO@nD zD@c}HXQs~el&&(=J0Wai+W4XvV751DNcd35$=gCu{9l_I^V4u(+M^IvO=jKW@*4n* zhGd%grOxzXXcjwLzgp$w@Aj2Bgi^nOxIY&S8u z(l1TC!YIN^B$rery3rT6#jp-7>*y~3{-R5ODU9w}L9lh9=g8vW3>rCt;AwifG5g1b4SeSz!uLrsHd& zxD{WZH)6-ZQgtvK9wi!U%~w60tg9hb{AX+$&jg8PGbQ_{<7i(G>xb|NIhT8deU*QQ zDCp&eYBL)luu2&dWVU8Cz6@xx{=f&^?_c$9mIG|cd>a{zNh5vaywKdE6_z4C6zIE( z7WI~XY-|yA++j|knZ7(nrm-#x0ZlHu?{a@$Fv+}MNRfHH8xH`(@0~%J_PWBbZ)8@` zA@fU3EXv$YC<)AnH_S&!3{4UhQ5Rt0i~-pv z7Uu<+3yxDM32OJr^uO=QF1t>gEg!C}AA&X-?eoZ8*7b8&TUU{(4bF*GgrBk>wbH_$ zp~P9W7wQUcKYg%l2QXs!Z?1rdsV+2w3X*a$IX{lUWm&*=`m)bTpo$QGEs#`?w7S_>R@_&5YF9o*;EjS3{ctJle7ge zl;Uy!ltX@6(VFfWE}PABl0P$y_%jLH=ZM;v*;^a!w-5uc9;&j7dX5soKTrSG($et? z`&I*v@cHDsf&F(jStQU?E2JNTym)tt8ttZmF8!n4@R_W^Ex+RqNAYO_p(k1wSS?{XbN%$PGFeK~ zZxP=y!+isya_rP`YiF-6*9+8u{#f${plW({yF0w5!N}-rZ)$gM!gw81iicWnz2q~8 znh<0iR@$T(8+S)#bwN0^$0XopZzInxNb=9%Z=CY<=my~akzY)E^Z9#aDa-yvnP4az z4iF3gz>Na{0QNr-BaSu(G}Z>LO&fYP>&nPrJJ+v2RTR~Eh+ncDYJjoDn}i!T5{rMO znBx|kG1y9W`cFlQCw#oE5$(p)WM&cCQ7H8dS`J?{USqGxyq~T#RH8Q{g-@^f(FbJ2O0T0%O}LdZDj43|uBR;)tW=zSXqHgq$~iR==+9#zrwgdw(HW zDrIlEi#8W|HKnBV`E$6*?YR-PBe-&cBSXt*!B)FE+}&c+aE0_}O{?Sc(ENAg`!+mG zyDYc`9r?(L8#i>!{^D*mPe(W8k(cs88aZ!9yISky+0hQIv|1%Q^2PSkv-XlUb zZlifpjIhfKocEZw!DvM_X}PoS`|{IzQD5)yEjwy0N$#jLXmFE_r+A(A?A*Fz&LJ*k zdm9=atx_LJsvFEPa9HJA7S32QssuQTq`Z1ewULdOXN+e`F#mkTeSVmm-^6@iOTXV4 zhHVROr5c-XtquYc;Na0_FT{Z7yS9df2yr=nSLNfy3Eyjz<`NyW;E4Q5yA*RQbo+-S zn{@9r5%pS#Hyd?GlJ!=eKzqEjo<&=hSDPZ-r9R9>V93DR;puTdD|GLX<xi{P z`r-aj==Ckb)2kdk^|sQxvzshiTEC7|k(CbE-<>aG6v%okC?C4vy^=?(GLnX6eMsI_ zuQ_LFBqC{dNTf?OoObfF;mxy-p?mcNRTA(h5M2(JP8kdh6V!mb{~;Cd5z@dYux^en zSwJf1yNO;o7!C8M0r{}%uuszpf`c^6Mst!M)~vBbgAyfsxI8*IsgMFk;id^38l1 zU~MsVwwdr4VTBd|j4*bzn^#z2N2P%0nE14F^jLV#GQbRr`%EzkrGO`gMZFesCz;Ts z0}}sYl-ghLuW$3eIR#SR>vbppY_s&NvB4l^jEX6~O*F%5 zxb5mT>zw?a91kbR&eLITpJxnDNbMJxaeZU{)s^;tx^n%at3vVZ&OZN(ER7%>g=z{AwQ})`6cTTZi3exv!{|hx^^~WpSSX3a*%#$gqL~6fH_V9Xv zi4*ZZ?UsKWbBfpE_S2$rt`?YRNu`X6#S6^j|Ajo|k!~Q|m094TYU#mn zcBV7+;eEK+(fQj~Y<;T`Hoax7Bi}xP01yBG=|8^0#a_>r?tA-7aIiFrmWKA{LkPb9 z2sfk6UN^fntfVXy;MWQKgCI{bwum`|G8ZnU#^Fo2}8`RuBMx`PM5NRMoog_CMb{%3mG-XDjskcD4Z0 z!ixOV;xZz%e}!l2nOPgz)3{n${*$rx{hPzkSe*U>1pqLD{@=>a-wfREQIHiDkW-*# zU}B?VWMH9ZW1^#HVWneaV`tG}W?^DtrZ%Pi*RcF+X#Ppw_$I%i@Hz1~008i_0|HR} z8~goS=-2)|ANYTh>HniJ{geFPqEP=0FZ2^||2O#`F{%GF|8FsKf159O{oDNicFKQ8 z(EXGA-(Hl$!$w`8K U4-fzVB2mm0p;jg3r1p-WG8~^|S literal 0 HcmV?d00001 diff --git a/test/tests/recognizeDocumentTest.js b/test/tests/recognizeDocumentTest.js new file mode 100644 index 0000000000..dddf9a28e6 --- /dev/null +++ b/test/tests/recognizeDocumentTest.js @@ -0,0 +1,447 @@ +describe("Document Recognition", function() { + var win; + + before(function* () { + this.timeout(60000); + // Load Zotero pane and install PDF tools + yield Zotero.Promise.all([ + loadZoteroPane().then(w => win = w) + ]); + }); + + beforeEach(function* () { + yield selectLibrary(win); + }); + + afterEach(function() { + for(let win of getWindows("chrome://zotero/content/progressQueueDialog.xhtml")) { + win.close(); + } + Zotero.ProgressQueues.get('recognize').cancel(); + Zotero.RecognizeDocument.recognizeStub = null; + Zotero.Prefs.clear('autoRenameFiles.linked'); + }); + + after(function() { + if (win) { + win.close(); + } + }); + + describe("PDFs", function () { + it("should recognize a PDF by DOI and rename the file", async function () { + if (Zotero.automatedTest) this.skip(); // TODO: Mock services + this.timeout(30000); + // Import the PDF + var testdir = getTestDataDirectory(); + testdir.append("recognizePDF_test_DOI.pdf"); + var collection = await createDataObject('collection'); + var attachment = await Zotero.Attachments.importFromFile({ + file: testdir, + collections: [collection.id] + }); + + win.ZoteroPane.recognizeSelected(); + + var addedIDs = await waitForItemEvent("add"); + var modifiedIDs = await waitForItemEvent("modify"); + assert.lengthOf(addedIDs, 1); + var item = Zotero.Items.get(addedIDs[0]); + assert.equal(item.getField("title"), "Shaping the Research Agenda"); + assert.equal(item.getField("libraryCatalog"), "DOI.org (Crossref)"); + assert.lengthOf(modifiedIDs, 2); + + // Wait for status to show as complete + var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; + var completeStr = Zotero.getString("general.finished"); + while (progressWindow.document.getElementById("label").value != completeStr) { + await Zotero.Promise.delay(20); + } + + // The file should have been renamed + assert.equal( + attachment.attachmentFilename, + Zotero.Attachments.getFileBaseNameFromItem(item) + '.pdf' + ); + }); + + it("should recognize a PDF by arXiv ID", async function () { + if (Zotero.automatedTest) this.skip(); // TODO: Mock services + this.timeout(30000); + // Import the PDF + var testdir = getTestDataDirectory(); + testdir.append("recognizePDF_test_arXiv.pdf"); + var attachment = await Zotero.Attachments.importFromFile({ + file: testdir + }); + + // Recognize the PDF + win.ZoteroPane.recognizeSelected(); + + var addedIDs = await waitForItemEvent("add"); + var modifiedIDs = await waitForItemEvent("modify"); + // Item and note + assert.lengthOf(addedIDs, 2); + var item = Zotero.Items.get(addedIDs[0]); + assert.equal(item.getField("title"), "Scaling study of an improved fermion action on quenched lattices"); + assert.lengthOf(modifiedIDs, 1); + + // Wait for status to show as complete + var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; + var completeStr = Zotero.getString("general.finished"); + while (progressWindow.document.getElementById("label").value != completeStr) { + await Zotero.Promise.delay(20); + } + }); + + it("should put new item in same collection", async function () { + if (Zotero.automatedTest) this.skip(); // TODO: Mock services + this.timeout(30000); + // Import the PDF + var testdir = getTestDataDirectory(); + testdir.append("recognizePDF_test_arXiv.pdf"); + var collection = await createDataObject('collection'); + var attachment = await Zotero.Attachments.importFromFile({ + file: testdir, + collections: [collection.id] + }); + + win.ZoteroPane.recognizeSelected(); + + var addedIDs = await waitForItemEvent("add"); + var modifiedIDs = await waitForItemEvent("modify"); + // Item and note + assert.lengthOf(addedIDs, 2); + var item = Zotero.Items.get(addedIDs[0]); + assert.lengthOf(modifiedIDs, 1); + + // Wait for status to show as complete + var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; + var completeStr = Zotero.getString("general.finished"); + while (progressWindow.document.getElementById("label").value != completeStr) { + await Zotero.Promise.delay(20); + } + + assert.isTrue(collection.hasItem(item.id)); + }); + + it("should recognize PDF by arXiv ID and put new item in same collection in group library", async function () { + if (Zotero.automatedTest) this.skip(); // TODO: Mock services + this.timeout(30000); + var testdir = getTestDataDirectory(); + testdir.append("recognizePDF_test_arXiv.pdf"); + var group = await getGroup(); + var collection = await createDataObject('collection', { libraryID: group.libraryID }); + var attachment = await Zotero.Attachments.importFromFile({ + libraryID: group.libraryID, + file: testdir, + collections: [collection.id], + }); + + win.ZoteroPane.recognizeSelected(); + + var addedIDs = await waitForItemEvent("add"); + var modifiedIDs = await waitForItemEvent("modify"); + // Item and note + assert.lengthOf(addedIDs, 2); + var item = Zotero.Items.get(addedIDs[0]); + assert.lengthOf(modifiedIDs, 1); + + // Wait for status to show as complete + var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; + var completeStr = Zotero.getString("general.finished"); + while (progressWindow.document.getElementById("label").value != completeStr) { + await Zotero.Promise.delay(20); + } + + assert.isTrue(collection.hasItem(item.id)); + }); + + it.skip("should recognize PDF by ISBN and put new item in same collection in group library", async function () { + if (Zotero.automatedTest) this.skip(); // TODO: Mock services + this.timeout(30000); + var testdir = getTestDataDirectory(); + testdir.append("recognizePDF_test_ISBN.pdf"); + var group = await getGroup(); + var collection = await createDataObject('collection', { libraryID: group.libraryID }); + var attachment = await Zotero.Attachments.importFromFile({ + libraryID: group.libraryID, + file: testdir, + collections: [collection.id], + }); + + win.ZoteroPane.recognizeSelected(); + + var addedIDs = await waitForItemEvent("add"); + var modifiedIDs = await waitForItemEvent("modify"); + assert.lengthOf(addedIDs, 1); + var item = Zotero.Items.get(addedIDs[0]); + assert.lengthOf(modifiedIDs, 2); + + // Wait for status to show as complete + var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; + var completeStr = Zotero.getString("general.finished"); + while (progressWindow.document.getElementById("label").value != completeStr) { + await Zotero.Promise.delay(20); + } + + assert.isTrue(collection.hasItem(item.id)); + }); + + it("should recognize PDF by title and put new item in same collection in group library", async function () { + if (Zotero.automatedTest) this.skip(); // TODO: Mock services + this.timeout(30000); + var testdir = getTestDataDirectory(); + testdir.append("recognizePDF_test_title.pdf"); + var group = await getGroup(); + var collection = await createDataObject('collection', { libraryID: group.libraryID }); + var attachment = await Zotero.Attachments.importFromFile({ + libraryID: group.libraryID, + file: testdir, + collections: [collection.id], + }); + + win.ZoteroPane.recognizeSelected(); + + var addedIDs = await waitForItemEvent("add"); + var modifiedIDs = await waitForItemEvent("modify"); + assert.lengthOf(addedIDs, 1); + var item = Zotero.Items.get(addedIDs[0]); + assert.lengthOf(modifiedIDs, 2); + + // Wait for status to show as complete + var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; + var completeStr = Zotero.getString("general.finished"); + while (progressWindow.document.getElementById("label").value != completeStr) { + await Zotero.Promise.delay(20); + } + + assert.isTrue(collection.hasItem(item.id)); + }); + + it("should rename a linked file attachment using parent metadata if no existing file attachments and pref enabled", async function () { + Zotero.Prefs.set('autoRenameFiles.linked', true); + var itemTitle = Zotero.Utilities.randomString();; + Zotero.RecognizeDocument.recognizeStub = async function () { + return createDataObject('item', { title: itemTitle }); + }; + + // Link to the PDF + var tempDir = await getTempDirectory(); + var tempFile = OS.Path.join(tempDir, 'test.pdf'); + await OS.File.copy(OS.Path.join(getTestDataDirectory().path, 'test.pdf'), tempFile); + var attachment = await Zotero.Attachments.linkFromFile({ + file: tempFile + }); + + win.ZoteroPane.recognizeSelected(); + + var addedIDs = await waitForItemEvent("add"); + var modifiedIDs = await waitForItemEvent("modify"); + assert.lengthOf(addedIDs, 1); + var item = Zotero.Items.get(addedIDs[0]); + assert.equal(item.getField("title"), itemTitle); + assert.lengthOf(modifiedIDs, 2); + + // Wait for status to show as complete + var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; + var completeStr = Zotero.getString("general.finished"); + while (progressWindow.document.getElementById("label").value != completeStr) { + await Zotero.Promise.delay(20); + } + + // The file should have been renamed + assert.equal( + attachment.attachmentFilename, + Zotero.Attachments.getFileBaseNameFromItem(item) + '.pdf' + ); + }); + + it("shouldn't rename a linked file attachment using parent metadata if pref disabled", async function () { + Zotero.Prefs.set('autoRenameFiles.linked', false); + var itemTitle = Zotero.Utilities.randomString();; + Zotero.RecognizeDocument.recognizeStub = async function () { + return createDataObject('item', { title: itemTitle }); + }; + + // Link to the PDF + var tempDir = await getTempDirectory(); + var tempFile = OS.Path.join(tempDir, 'test.pdf'); + await OS.File.copy(OS.Path.join(getTestDataDirectory().path, 'test.pdf'), tempFile); + var attachment = await Zotero.Attachments.linkFromFile({ + file: tempFile + }); + + win.ZoteroPane.recognizeSelected(); + + var addedIDs = await waitForItemEvent("add"); + var modifiedIDs = await waitForItemEvent("modify"); + assert.lengthOf(addedIDs, 1); + var item = Zotero.Items.get(addedIDs[0]); + assert.equal(item.getField("title"), itemTitle); + assert.lengthOf(modifiedIDs, 2); + + // Wait for status to show as complete + var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; + var completeStr = Zotero.getString("general.finished"); + while (progressWindow.document.getElementById("label").value != completeStr) { + await Zotero.Promise.delay(20); + } + + // The file should not have been renamed + assert.equal(attachment.attachmentFilename, 'test.pdf'); + }); + }); + + describe("Ebooks", function () { + it("should recognize an EPUB by ISBN and rename the file", async function () { + let isbn = '9780656173822'; + let translateStub = sinon.stub(Zotero.Translate.Search.prototype, 'translate') + .callsFake(async function () { + assert.equal(this.search.ISBN, isbn); + return [{ + itemType: 'book', + title: 'The Mania of the Nations on the Planet Mars: ISBN Database Edition', + ISBN: isbn, + attachments: [], + tags: [] + }]; + }); + + let testDir = getTestDataDirectory(); + testDir.append('recognizeEPUB_test_ISBN.epub'); + let collection = await createDataObject('collection'); + let attachment = await Zotero.Attachments.importFromFile({ + file: testDir, + collections: [collection.id] + }); + + win.ZoteroPane.recognizeSelected(); + + let addedIDs = await waitForItemEvent('add'); + let modifiedIDs = await waitForItemEvent('modify'); + assert.isTrue(translateStub.calledOnce); + assert.lengthOf(addedIDs, 1); + let item = Zotero.Items.get(addedIDs[0]); + assert.equal(item.getField('title'), 'The Mania of the Nations on the Planet Mars: ISBN Database Edition'); + assert.equal(Zotero.Utilities.cleanISBN(item.getField('ISBN')), isbn); + assert.lengthOf(modifiedIDs, 2); + + // Wait for status to show as complete + let progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; + let completeStr = Zotero.getString("general.finished"); + while (progressWindow.document.getElementById("label").value != completeStr) { + await Zotero.Promise.delay(20); + } + + // The file should have been renamed + assert.equal( + attachment.attachmentFilename, + Zotero.Attachments.getFileBaseNameFromItem(item) + '.epub' + ); + + translateStub.restore(); + }); + + it("should recognize an EPUB without an ISBN and rename the file", async function () { + let testDir = getTestDataDirectory(); + testDir.append('recognizeEPUB_test_DC.epub'); + let collection = await createDataObject('collection'); + let attachment = await Zotero.Attachments.importFromFile({ + file: testDir, + collections: [collection.id] + }); + + win.ZoteroPane.recognizeSelected(); + + let addedIDs = await waitForItemEvent('add'); + let modifiedIDs = await waitForItemEvent('modify'); + assert.lengthOf(addedIDs, 1); + let item = Zotero.Items.get(addedIDs[0]); + assert.equal(item.getField('title'), 'The Mania of the Nations on the Planet Mars and its Terrific Consequences / A Combination of Fun and Wisdom'); + assert.equal(item.getCreators().length, 1); + assert.equal(item.getField('ISBN'), ''); + assert.lengthOf(modifiedIDs, 2); + + // Wait for status to show as complete + let progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; + let completeStr = Zotero.getString("general.finished"); + while (progressWindow.document.getElementById("label").value != completeStr) { + await Zotero.Promise.delay(20); + } + + // The file should have been renamed + assert.equal( + attachment.attachmentFilename, + Zotero.Attachments.getFileBaseNameFromItem(item) + '.epub' + ); + }); + + it("should use metadata from EPUB when search returns item with different ISBN", async function () { + let isbn = '9780656173822'; + let isbnWrong = '9780656173823'; + let translateStub = sinon.stub(Zotero.Translate.Search.prototype, 'translate') + .callsFake(async function () { + assert.equal(this.search.ISBN, isbn); + return [{ + itemType: 'book', + title: 'The Mania of the Nations on the Planet Mars: Bad Metadata Edition', + ISBN: isbnWrong, // Wrong ISBN + attachments: [], + tags: [] + }]; + }); + + let testDir = getTestDataDirectory(); + testDir.append('recognizeEPUB_test_ISBN.epub'); + let collection = await createDataObject('collection'); + await Zotero.Attachments.importFromFile({ + file: testDir, + collections: [collection.id] + }); + + win.ZoteroPane.recognizeSelected(); + + let addedIDs = await waitForItemEvent('add'); + let modifiedIDs = await waitForItemEvent('modify'); + assert.isTrue(translateStub.calledOnce); + assert.lengthOf(addedIDs, 1); + let item = Zotero.Items.get(addedIDs[0]); + assert.equal(item.getField('title'), 'The Mania of the Nations on the Planet Mars and its Terrific Consequences / A Combination of Fun and Wisdom'); + assert.equal(Zotero.Utilities.cleanISBN(item.getField('ISBN')), isbn); + assert.lengthOf(modifiedIDs, 2); + + translateStub.restore(); + }); + + it("should use metadata from EPUB when search fails", async function () { + let isbn = '9780656173822'; + let translateStub = sinon.stub(Zotero.Translate.Search.prototype, 'translate') + .callsFake(async function () { + assert.equal(this.search.ISBN, isbn); + throw new Error('simulated failure'); + }); + + let testDir = getTestDataDirectory(); + testDir.append('recognizeEPUB_test_ISBN.epub'); + let collection = await createDataObject('collection'); + await Zotero.Attachments.importFromFile({ + file: testDir, + collections: [collection.id] + }); + + win.ZoteroPane.recognizeSelected(); + + let addedIDs = await waitForItemEvent('add'); + let modifiedIDs = await waitForItemEvent('modify'); + assert.isTrue(translateStub.calledOnce); + assert.lengthOf(addedIDs, 1); + let item = Zotero.Items.get(addedIDs[0]); + assert.equal(item.getField('title'), 'The Mania of the Nations on the Planet Mars and its Terrific Consequences / A Combination of Fun and Wisdom'); + assert.equal(Zotero.Utilities.cleanISBN(item.getField('ISBN')), isbn); + assert.lengthOf(modifiedIDs, 2); + + translateStub.restore(); + }); + }); +}); \ No newline at end of file diff --git a/test/tests/recognizePDFTest.js b/test/tests/recognizePDFTest.js deleted file mode 100644 index 968bdfc475..0000000000 --- a/test/tests/recognizePDFTest.js +++ /dev/null @@ -1,293 +0,0 @@ -describe("PDF Recognition", function() { - var win; - - before(function* () { - this.timeout(60000); - // Load Zotero pane and install PDF tools - yield Zotero.Promise.all([ - loadZoteroPane().then(w => win = w) - ]); - }); - - beforeEach(function* () { - yield selectLibrary(win); - }); - - afterEach(function() { - for(let win of getWindows("chrome://zotero/content/progressQueueDialog.xhtml")) { - win.close(); - } - Zotero.ProgressQueues.get('recognize').cancel(); - Zotero.RecognizePDF.recognizeStub = null; - Zotero.Prefs.clear('autoRenameFiles.linked'); - }); - - after(function() { - if (win) { - win.close(); - } - }); - - it("should recognize a PDF by DOI and rename the file", async function () { - if (Zotero.automatedTest) this.skip(); // TODO: Mock services - this.timeout(30000); - // Import the PDF - var testdir = getTestDataDirectory(); - testdir.append("recognizePDF_test_DOI.pdf"); - var collection = await createDataObject('collection'); - var attachment = await Zotero.Attachments.importFromFile({ - file: testdir, - collections: [collection.id] - }); - - win.ZoteroPane.recognizeSelected(); - - var addedIDs = await waitForItemEvent("add"); - var modifiedIDs = await waitForItemEvent("modify"); - assert.lengthOf(addedIDs, 1); - var item = Zotero.Items.get(addedIDs[0]); - assert.equal(item.getField("title"), "Shaping the Research Agenda"); - assert.equal(item.getField("libraryCatalog"), "DOI.org (Crossref)"); - assert.lengthOf(modifiedIDs, 2); - - // Wait for status to show as complete - var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; - var completeStr = Zotero.getString("general.finished"); - while (progressWindow.document.getElementById("label").value != completeStr) { - await Zotero.Promise.delay(20); - } - - // The file should have been renamed - assert.equal( - attachment.attachmentFilename, - Zotero.Attachments.getFileBaseNameFromItem(item) + '.pdf' - ); - }); - - it("should recognize a PDF by arXiv ID", async function () { - if (Zotero.automatedTest) this.skip(); // TODO: Mock services - this.timeout(30000); - // Import the PDF - var testdir = getTestDataDirectory(); - testdir.append("recognizePDF_test_arXiv.pdf"); - var attachment = await Zotero.Attachments.importFromFile({ - file: testdir - }); - - // Recognize the PDF - win.ZoteroPane.recognizeSelected(); - - var addedIDs = await waitForItemEvent("add"); - var modifiedIDs = await waitForItemEvent("modify"); - // Item and note - assert.lengthOf(addedIDs, 2); - var item = Zotero.Items.get(addedIDs[0]); - assert.equal(item.getField("title"), "Scaling study of an improved fermion action on quenched lattices"); - assert.lengthOf(modifiedIDs, 1); - - // Wait for status to show as complete - var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; - var completeStr = Zotero.getString("general.finished"); - while (progressWindow.document.getElementById("label").value != completeStr) { - await Zotero.Promise.delay(20); - } - }); - - it("should put new item in same collection", async function () { - if (Zotero.automatedTest) this.skip(); // TODO: Mock services - this.timeout(30000); - // Import the PDF - var testdir = getTestDataDirectory(); - testdir.append("recognizePDF_test_arXiv.pdf"); - var collection = await createDataObject('collection'); - var attachment = await Zotero.Attachments.importFromFile({ - file: testdir, - collections: [collection.id] - }); - - win.ZoteroPane.recognizeSelected(); - - var addedIDs = await waitForItemEvent("add"); - var modifiedIDs = await waitForItemEvent("modify"); - // Item and note - assert.lengthOf(addedIDs, 2); - var item = Zotero.Items.get(addedIDs[0]); - assert.lengthOf(modifiedIDs, 1); - - // Wait for status to show as complete - var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; - var completeStr = Zotero.getString("general.finished"); - while (progressWindow.document.getElementById("label").value != completeStr) { - await Zotero.Promise.delay(20); - } - - assert.isTrue(collection.hasItem(item.id)); - }); - - it("should recognize PDF by arXiv ID and put new item in same collection in group library", async function () { - if (Zotero.automatedTest) this.skip(); // TODO: Mock services - this.timeout(30000); - var testdir = getTestDataDirectory(); - testdir.append("recognizePDF_test_arXiv.pdf"); - var group = await getGroup(); - var collection = await createDataObject('collection', { libraryID: group.libraryID }); - var attachment = await Zotero.Attachments.importFromFile({ - libraryID: group.libraryID, - file: testdir, - collections: [collection.id], - }); - - win.ZoteroPane.recognizeSelected(); - - var addedIDs = await waitForItemEvent("add"); - var modifiedIDs = await waitForItemEvent("modify"); - // Item and note - assert.lengthOf(addedIDs, 2); - var item = Zotero.Items.get(addedIDs[0]); - assert.lengthOf(modifiedIDs, 1); - - // Wait for status to show as complete - var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; - var completeStr = Zotero.getString("general.finished"); - while (progressWindow.document.getElementById("label").value != completeStr) { - await Zotero.Promise.delay(20); - } - - assert.isTrue(collection.hasItem(item.id)); - }); - - it.skip("should recognize PDF by ISBN and put new item in same collection in group library", async function () { - if (Zotero.automatedTest) this.skip(); // TODO: Mock services - this.timeout(30000); - var testdir = getTestDataDirectory(); - testdir.append("recognizePDF_test_ISBN.pdf"); - var group = await getGroup(); - var collection = await createDataObject('collection', { libraryID: group.libraryID }); - var attachment = await Zotero.Attachments.importFromFile({ - libraryID: group.libraryID, - file: testdir, - collections: [collection.id], - }); - - win.ZoteroPane.recognizeSelected(); - - var addedIDs = await waitForItemEvent("add"); - var modifiedIDs = await waitForItemEvent("modify"); - assert.lengthOf(addedIDs, 1); - var item = Zotero.Items.get(addedIDs[0]); - assert.lengthOf(modifiedIDs, 2); - - // Wait for status to show as complete - var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; - var completeStr = Zotero.getString("general.finished"); - while (progressWindow.document.getElementById("label").value != completeStr) { - await Zotero.Promise.delay(20); - } - - assert.isTrue(collection.hasItem(item.id)); - }); - - it("should recognize PDF by title and put new item in same collection in group library", async function () { - if (Zotero.automatedTest) this.skip(); // TODO: Mock services - this.timeout(30000); - var testdir = getTestDataDirectory(); - testdir.append("recognizePDF_test_title.pdf"); - var group = await getGroup(); - var collection = await createDataObject('collection', { libraryID: group.libraryID }); - var attachment = await Zotero.Attachments.importFromFile({ - libraryID: group.libraryID, - file: testdir, - collections: [collection.id], - }); - - win.ZoteroPane.recognizeSelected(); - - var addedIDs = await waitForItemEvent("add"); - var modifiedIDs = await waitForItemEvent("modify"); - assert.lengthOf(addedIDs, 1); - var item = Zotero.Items.get(addedIDs[0]); - assert.lengthOf(modifiedIDs, 2); - - // Wait for status to show as complete - var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; - var completeStr = Zotero.getString("general.finished"); - while (progressWindow.document.getElementById("label").value != completeStr) { - await Zotero.Promise.delay(20); - } - - assert.isTrue(collection.hasItem(item.id)); - }); - - it("should rename a linked file attachment using parent metadata if no existing file attachments and pref enabled", async function () { - Zotero.Prefs.set('autoRenameFiles.linked', true); - var itemTitle = Zotero.Utilities.randomString();; - Zotero.RecognizePDF.recognizeStub = async function () { - return createDataObject('item', { title: itemTitle }); - }; - - // Link to the PDF - var tempDir = await getTempDirectory(); - var tempFile = OS.Path.join(tempDir, 'test.pdf'); - await OS.File.copy(OS.Path.join(getTestDataDirectory().path, 'test.pdf'), tempFile); - var attachment = await Zotero.Attachments.linkFromFile({ - file: tempFile - }); - - win.ZoteroPane.recognizeSelected(); - - var addedIDs = await waitForItemEvent("add"); - var modifiedIDs = await waitForItemEvent("modify"); - assert.lengthOf(addedIDs, 1); - var item = Zotero.Items.get(addedIDs[0]); - assert.equal(item.getField("title"), itemTitle); - assert.lengthOf(modifiedIDs, 2); - - // Wait for status to show as complete - var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; - var completeStr = Zotero.getString("general.finished"); - while (progressWindow.document.getElementById("label").value != completeStr) { - await Zotero.Promise.delay(20); - } - - // The file should have been renamed - assert.equal( - attachment.attachmentFilename, - Zotero.Attachments.getFileBaseNameFromItem(item) + '.pdf' - ); - }); - - it("shouldn't rename a linked file attachment using parent metadata if pref disabled", async function () { - Zotero.Prefs.set('autoRenameFiles.linked', false); - var itemTitle = Zotero.Utilities.randomString();; - Zotero.RecognizePDF.recognizeStub = async function () { - return createDataObject('item', { title: itemTitle }); - }; - - // Link to the PDF - var tempDir = await getTempDirectory(); - var tempFile = OS.Path.join(tempDir, 'test.pdf'); - await OS.File.copy(OS.Path.join(getTestDataDirectory().path, 'test.pdf'), tempFile); - var attachment = await Zotero.Attachments.linkFromFile({ - file: tempFile - }); - - win.ZoteroPane.recognizeSelected(); - - var addedIDs = await waitForItemEvent("add"); - var modifiedIDs = await waitForItemEvent("modify"); - assert.lengthOf(addedIDs, 1); - var item = Zotero.Items.get(addedIDs[0]); - assert.equal(item.getField("title"), itemTitle); - assert.lengthOf(modifiedIDs, 2); - - // Wait for status to show as complete - var progressWindow = getWindows("chrome://zotero/content/progressQueueDialog.xhtml")[0]; - var completeStr = Zotero.getString("general.finished"); - while (progressWindow.document.getElementById("label").value != completeStr) { - await Zotero.Promise.delay(20); - } - - // The file should not have been renamed - assert.equal(attachment.attachmentFilename, 'test.pdf'); - }); -}); \ No newline at end of file