www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

openPDF.js (8527B)


      1 /*
      2     ***** BEGIN LICENSE BLOCK *****
      3     
      4     Copyright © 2018 Center for History and New Media
      5                      George Mason University, Fairfax, Virginia, USA
      6                      https://zotero.org
      7     
      8     This file is part of Zotero.
      9     
     10     Zotero is free software: you can redistribute it and/or modify
     11     it under the terms of the GNU Affero General Public License as published by
     12     the Free Software Foundation, either version 3 of the License, or
     13     (at your option) any later version.
     14     
     15     Zotero is distributed in the hope that it will be useful,
     16     but WITHOUT ANY WARRANTY; without even the implied warranty of
     17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     18     GNU Affero General Public License for more details.
     19     
     20     You should have received a copy of the GNU Affero General Public License
     21     along with Zotero.  If not, see <http://www.gnu.org/licenses/>.
     22     
     23     ***** END LICENSE BLOCK *****
     24 */
     25 
     26 Zotero.OpenPDF = {
     27 	openToPage: async function (path, page) {
     28 		var handler = Zotero.Prefs.get("fileHandler.pdf");
     29 		var opened = false;
     30 		if (Zotero.isMac) {
     31 			if (handler.includes('Preview')) {
     32 				this._openWithPreview(path, page);
     33 			}
     34 			else if (handler.includes('Skim')) {
     35 				this._openWithSkim(path, page);
     36 			}
     37 			else if (handler.includes('PDF Expert')) {
     38 				this._openWithPDFExpert(path, page);
     39 			}
     40 			else {
     41 				// Try to detect default app
     42 				handler = this._getPDFHandlerName();
     43 				Zotero.debug(`Handler is ${handler}`);
     44 				if (handler && handler == 'Skim') {
     45 					this._openWithSkim(path, page);
     46 				}
     47 				else if (handler && handler == 'PDF Expert') {
     48 					this._openWithPDFExpert(path, page);
     49 				}
     50 				// Fall back to Preview
     51 				else {
     52 					this._openWithPreview(path, page);
     53 				}
     54 			}
     55 			opened = true;
     56 		}
     57 		else if (Zotero.isWin) {
     58 			handler = handler || this._getPDFHandlerWindows();
     59 			// Include flags to open the PDF on a given page in various apps
     60 			//
     61 			// Adobe Acrobat: http://partners.adobe.com/public/developer/en/acrobat/PDFOpenParameters.pdf
     62 			// PDF-XChange: http://help.tracker-software.com/eu/default.aspx?pageid=PDFXView25:command_line_options
     63 			let args = ['/A', 'page=' + page, path];
     64 			Zotero.Utilities.Internal.exec(handler, args);
     65 			opened = true;
     66 		}
     67 		else if (Zotero.isLinux) {
     68 			if (handler.includes('evince') || handler.includes('okular')) {
     69 				this._openWithEvinceOrOkular(handler, path, page);
     70 				opened = true;
     71 			}
     72 			else {
     73 				let handler = await this._getPDFHandlerLinux();
     74 				if (handler.includes('evince') || handler.includes('okular')) {
     75 					this._openWithEvinceOrOkular(handler, path, page);
     76 					opened = true;
     77 				}
     78 				// Fall back to okular and then evince if unknown handler
     79 				else if (await OS.File.exists('/usr/bin/okular')) {
     80 					this._openWithEvinceOrOkular('/usr/bin/okular', path, page);
     81 					opened = true;
     82 				}
     83 				else if (await OS.File.exists('/usr/bin/evince')) {
     84 					this._openWithEvinceOrOkular('/usr/bin/evince', path, page);
     85 					opened = true;
     86 				}
     87 				else {
     88 					Zotero.debug("No handler found");
     89 				}
     90 			}
     91 		}
     92 		return opened;
     93 	},
     94 	
     95 	_getPDFHandlerName: function () {
     96 		var handlerService = Cc["@mozilla.org/uriloader/handler-service;1"]
     97 			.getService(Ci.nsIHandlerService);
     98 		var handlers = handlerService.enumerate();
     99 		var handler;
    100 		while (handlers.hasMoreElements()) {
    101 			let handlerInfo = handlers.getNext().QueryInterface(Ci.nsIHandlerInfo);
    102 			if (handlerInfo.type == 'application/pdf') {
    103 				handler = handlerInfo;
    104 				break;
    105 			}
    106 		}
    107 		if (!handler) {
    108 			// We can't get the name of the system default handler unless we add an entry
    109 			Zotero.debug("Default handler not found -- adding default entry");
    110 			let mimeService = Components.classes["@mozilla.org/mime;1"]
    111 				.getService(Components.interfaces.nsIMIMEService);
    112 			let mimeInfo = mimeService.getFromTypeAndExtension("application/pdf", "");
    113 			mimeInfo.preferredAction = 4;
    114 			mimeInfo.alwaysAskBeforeHandling = false;
    115 			handlerService.store(mimeInfo);
    116 			
    117 			// And once we do that, we can get the name (but not the path, unfortunately)
    118 			let handlers = handlerService.enumerate();
    119 			while (handlers.hasMoreElements()) {
    120 				let handlerInfo = handlers.getNext().QueryInterface(Ci.nsIHandlerInfo);
    121 				if (handlerInfo.type == 'application/pdf') {
    122 					handler = handlerInfo;
    123 					break;
    124 				}
    125 			}
    126 		}
    127 		if (handler) {
    128 			Zotero.debug(`Default handler is ${handler.defaultDescription}`);
    129 			return handler.defaultDescription;
    130 		}
    131 		return false;
    132 	},
    133 	
    134 	//
    135 	// Mac
    136 	//
    137 	_openWithPreview: async function (filePath, page) {
    138 		await Zotero.Utilities.Internal.exec('/usr/bin/open', ['-a', 'Preview', filePath]);
    139 		// Go to page using AppleScript
    140 		let args = [
    141 			'-e', 'tell app "Preview" to activate',
    142 			'-e', 'tell app "System Events" to keystroke "g" using {option down, command down}',
    143 			'-e', `tell app "System Events" to keystroke "${page}"`,
    144 			'-e', 'tell app "System Events" to keystroke return'
    145 		];
    146 		await Zotero.Utilities.Internal.exec('/usr/bin/osascript', args);
    147 	},
    148 	
    149 	_openWithSkim: async function (filePath, page) {
    150 		// Escape double-quotes in path
    151 		var quoteRE = /"/g;
    152 		filePath = filePath.replace(quoteRE, '\\"');
    153 		let filename = OS.Path.basename(filePath).replace(quoteRE, '\\"');
    154 		let args = [
    155 			'-e', 'tell app "Skim" to activate',
    156 			'-e', `tell app "Skim" to open "${filePath}"`
    157 		];
    158 		args.push('-e', `tell document "${filename}" of application "Skim" to go to page ${page}`);
    159 		await Zotero.Utilities.Internal.exec('/usr/bin/osascript', args);
    160 	},
    161 	
    162 	_openWithPDFExpert: async function (filePath, page) {
    163 		await Zotero.Utilities.Internal.exec('/usr/bin/open', ['-a', 'PDF Expert', filePath]);
    164 		// Go to page using AppleScript (same as Preview)
    165 		let args = [
    166 			'-e', 'tell app "PDF Expert" to activate',
    167 			'-e', 'tell app "System Events" to keystroke "g" using {option down, command down}',
    168 			'-e', `tell app "System Events" to keystroke "${page}"`,
    169 			'-e', 'tell app "System Events" to keystroke return'
    170 		];
    171 		await Zotero.Utilities.Internal.exec('/usr/bin/osascript', args);
    172 	},
    173 	
    174 	//
    175 	// Windows
    176 	//
    177 	/**
    178 	 * Get path to default pdf reader application on windows
    179 	 * @return {string} Path to default pdf reader application
    180 	 *
    181 	 * From getPDFReader() in ZotFile (GPL)
    182 	 * https://github.com/jlegewie/zotfile/blob/master/chrome/content/zotfile/utils.js
    183 	 */
    184 	_getPDFHandlerWindows: function () {
    185 		var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
    186 			.createInstance(Components.interfaces.nsIWindowsRegKey);
    187 		// Get handler for PDFs
    188 		var tryKeys = [
    189 			{
    190 				root: wrk.ROOT_KEY_CURRENT_USER,
    191 				path: 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.pdf\\UserChoice',
    192 				value: 'Progid'
    193 			},
    194 			{
    195 				root: wrk.ROOT_KEY_CLASSES_ROOT,
    196 				path: '.pdf',
    197 				value: ''
    198 			}
    199 		];
    200 		var progId;
    201 		for (let i = 0; !progId && i < tryKeys.length; i++) {
    202 			try {
    203 				wrk.open(
    204 					tryKeys[i].root,
    205 					tryKeys[i].path,
    206 					wrk.ACCESS_READ
    207 				);
    208 				progId = wrk.readStringValue(tryKeys[i].value);
    209 			}
    210 			catch (e) {}
    211 		}
    212 		
    213 		if (!progId) {
    214 			wrk.close();
    215 			return;
    216 		}
    217 		
    218 		// Get version specific handler, if it exists
    219 		try {
    220 			wrk.open(
    221 				wrk.ROOT_KEY_CLASSES_ROOT,
    222 				progId + '\\CurVer',
    223 				wrk.ACCESS_READ
    224 			);
    225 			progId = wrk.readStringValue('') || progId;
    226 		}
    227 		catch (e) {}
    228 		
    229 		// Get command
    230 		var success = false;
    231 		tryKeys = [
    232 			progId + '\\shell\\Read\\command',
    233 			progId + '\\shell\\Open\\command'
    234 		];
    235 		for (let i = 0; !success && i < tryKeys.length; i++) {
    236 			try {
    237 				wrk.open(
    238 					wrk.ROOT_KEY_CLASSES_ROOT,
    239 					tryKeys[i],
    240 					wrk.ACCESS_READ
    241 				);
    242 				success = true;
    243 			}
    244 			catch (e) {}
    245 		}
    246 		
    247 		if (!success) {
    248 			wrk.close();
    249 			return;
    250 		}
    251 		
    252 		var command = wrk.readStringValue('').match(/^(?:".+?"|[^"]\S+)/);
    253 		
    254 		wrk.close();
    255 		
    256 		if (!command) return;
    257 		return command[0].replace(/"/g, '');
    258 	},
    259 	
    260 	//
    261 	// Linux
    262 	//
    263 	_getPDFHandlerLinux: async function () {
    264 		var name = this._getPDFHandlerName();
    265 		switch (name.toLowerCase()) {
    266 		case 'okular':
    267 			return `/usr/bin/${name}`;
    268 		
    269 		// It's "Document Viewer" on stock Ubuntu
    270 		case 'document viewer':
    271 		case 'evince':
    272 			return `/usr/bin/evince`;
    273 		}
    274 		
    275 		// TODO: Try to get default from mimeapps.list, etc., in case system default is okular
    276 		// or evince somewhere other than /usr/bin
    277 		var homeDir = OS.Constants.Path.homeDir;
    278 		
    279 		return false;
    280 		
    281 	},
    282 	
    283 	_openWithEvinceOrOkular: function (appPath, filePath, page) {
    284 		var args = ['-p', page, filePath];
    285 		Zotero.Utilities.Internal.exec(appPath, args);
    286 	}
    287 }