commit a9ca6e0857928d8d2fe9de451567e5ff3027f64d
parent aa7c1b6fd62637e5fa3e92a3dde5cc77bd132124
Author: Dan Stillman <dstillman@zotero.org>
Date: Wed, 1 Apr 2015 02:03:57 -0400
Support unmodified Xpdf binaries
Use stdout redirection scripts for pdfinfo and, on Windows, a script to
run pdftotext hidden, which together allow for all unmodified binaries
(including, probably, symlinked system ones, though I didn't test that).
On Windows, using a .vbs does cause a brief wait cursor. The stock
pdfinfo needs the redirection script anyway, so that's unavoidable, but
on the async branch I think we'll be able to switch to pdf.js for the
page count, at which point maybe I'll try to remember how I modified the
Windows binaries to be hidden and use a modified version of pdftotext to
avoid VBScript. (We use the stock pdftotext elsewhere already.)
Diffstat:
6 files changed, 266 insertions(+), 132 deletions(-)
diff --git a/chrome/content/zotero/preferences/preferences_search.js b/chrome/content/zotero/preferences/preferences_search.js
@@ -162,139 +162,121 @@ Zotero_Preferences.Search = {
* if a newer version is available
*/
checkPDFToolsDownloadVersion: function () {
- var url = Zotero.Fulltext.pdfToolsDownloadBaseURL
- + Zotero.platform.replace(' ', '-') + '.latest';
+ var url = Zotero.Fulltext.pdfToolsDownloadBaseURL + 'latest.json';
// Find latest version for this platform
var self = this;
var sent = Zotero.HTTP.doGet(url, function (xmlhttp) {
try {
- if (xmlhttp.status == 200) {
- var converterIsRegistered = Zotero.Fulltext.pdfConverterIsRegistered();
- var infoIsRegistered = Zotero.Fulltext.pdfInfoIsRegistered();
- var bothRegistered = converterIsRegistered && infoIsRegistered;
+ if (xmlhttp.status != 200) {
+ throw new Error("Unexpected response code " + xmlhttp.status);
+ }
+
+ var platform = Zotero.platform.replace(/\s/g, '-');
+ var json = JSON.parse(xmlhttp.responseText);
+ var latestVersion = json[platform] || json['default'];
+
+ Zotero.debug("Latest PDF tools version for " + platform + " is " + latestVersion);
+
+ var converterIsRegistered = Zotero.Fulltext.pdfConverterIsRegistered();
+ var infoIsRegistered = Zotero.Fulltext.pdfInfoIsRegistered();
+ var bothRegistered = converterIsRegistered && infoIsRegistered;
+
+ // Install if not installed, version unknown, outdated, or
+ // Xpdf 3.02/3.04 (to upgrade to Poppler),
+ var converterVersionAvailable = (!converterIsRegistered ||
+ Zotero.Fulltext.pdfConverterVersion == 'UNKNOWN'
+ || latestVersion > Zotero.Fulltext.pdfConverterVersion
+ || (latestVersion != '3.02' && Zotero.Fulltext.pdfConverterVersion == '3.02')
+ || (latestVersion != '3.02' && latestVersion != '3.04' && Zotero.Fulltext.pdfConverterVersion == '3.04'));
+ var infoVersionAvailable = (!infoIsRegistered ||
+ Zotero.Fulltext.pdfInfoVersion == 'UNKNOWN'
+ || latestVersion > Zotero.Fulltext.pdfInfoVersion
+ || (latestVersion != '3.02' && Zotero.Fulltext.pdfInfoVersion == '3.02')
+ || (latestVersion != '3.02' && latestVersion != '3.04' && Zotero.Fulltext.pdfInfoVersion == '3.04'));
+ var bothAvailable = converterVersionAvailable && infoVersionAvailable;
+
+ // Up to date -- disable update button
+ if (!converterVersionAvailable && !infoVersionAvailable) {
+ var button = document.getElementById('pdftools-update-button');
+ button.setAttribute('label', Zotero.getString('zotero.preferences.update.upToDate'));
+ button.setAttribute('disabled', true);
+ }
+ // New version available -- display update prompt
+ else {
+ var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
+ createInstance(Components.interfaces.nsIPromptService);
+ var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
- var converterVersion = xmlhttp.responseText.split(/\s/)[0];
- var infoVersion = xmlhttp.responseText.split(/\s/)[1];
+ var msg = Zotero.getString('zotero.preferences.search.pdf.available'
+ + ((converterIsRegistered || infoIsRegistered) ? 'Updates' : 'Downloads'),
+ [Zotero.platform, 'zotero.org']) + '\n\n';
- // Install if not installed, version unknown, Xpdf 3.02 (to upgrade to Poppler),
- // or outdated
- var converterVersionAvailable = converterVersion &&
- (!converterIsRegistered ||
- Zotero.Fulltext.pdfConverterVersion == 'UNKNOWN'
- || (converterVersion != '3.02' && Zotero.Fulltext.pdfConverterVersion == '3.02')
- || converterVersion > Zotero.Fulltext.pdfConverterVersion);
- var infoVersionAvailable = infoVersion &&
- (!infoIsRegistered ||
- Zotero.Fulltext.pdfInfoVersion == 'UNKNOWN'
- || (infoVersion != '3.02' && Zotero.Fulltext.pdfInfoVersion == '3.02')
- || infoVersion > Zotero.Fulltext.pdfInfoVersion);
- var bothAvailable = converterVersionAvailable && infoVersionAvailable;
+ if (converterVersionAvailable) {
+ let tvp = Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform',
+ [Zotero.Fulltext.pdfConverterName, latestVersion]);
+ msg += '- ' + tvp + '\n';
+ }
+ if (infoVersionAvailable) {
+ let tvp = Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform',
+ [Zotero.Fulltext.pdfInfoName, latestVersion]);
+ msg += '- ' + tvp + '\n';
+ }
+ msg += '\n';
+ msg += Zotero.getString('zotero.preferences.search.pdf.zoteroCanInstallVersion'
+ + (bothAvailable ? 's' : ''));
- /*
- Zotero.debug(converterIsRegistered);
- Zotero.debug(infoIsRegistered);
- Zotero.debug(converterVersion);
- Zotero.debug(infoVersion);
- Zotero.debug(Zotero.Fulltext.pdfConverterVersion);
- Zotero.debug(Zotero.Fulltext.pdfInfoVersion);
- Zotero.debug(converterVersionAvailable);
- Zotero.debug(infoVersionAvailable);
- */
+ var index = ps.confirmEx(null,
+ converterIsRegistered ?
+ Zotero.getString('general.updateAvailable') : '',
+ msg,
+ buttonFlags,
+ converterIsRegistered ?
+ Zotero.getString('general.upgrade') :
+ Zotero.getString('general.install'),
+ null, null, null, {});
- // Up to date -- disable update button
- if (!converterVersionAvailable && !infoVersionAvailable) {
- var button = document.getElementById('pdftools-update-button');
- button.setAttribute('label', Zotero.getString('zotero.preferences.update.upToDate'));
- button.setAttribute('disabled', true);
- }
- // New version available -- display update prompt
- else {
- var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
- createInstance(Components.interfaces.nsIPromptService);
- var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
- + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
-
- var msg = Zotero.getString('zotero.preferences.search.pdf.available'
- + ((converterIsRegistered || infoIsRegistered) ? 'Updates' : 'Downloads'),
- [Zotero.platform, 'zotero.org']) + '\n\n';
+ if (index == 0) {
+ document.getElementById('pdftools-update-button').disabled = true;
+ var str = Zotero.getString('zotero.preferences.search.pdf.downloading');
+ document.getElementById('pdftools-update-button').setAttribute('label', str);
- if (converterVersionAvailable) {
- let tvp = Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform',
- [Zotero.Fulltext.pdfConverterName, converterVersion]);
- msg += '- ' + tvp + '\n';
- }
- if (infoVersionAvailable) {
- let tvp = Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform',
- [Zotero.Fulltext.pdfInfoName, infoVersion]);
- msg += '- ' + tvp + '\n';
- }
- msg += '\n';
- msg += Zotero.getString('zotero.preferences.search.pdf.zoteroCanInstallVersion'
- + (bothAvailable ? 's' : ''));
-
- var index = ps.confirmEx(null,
- converterIsRegistered ?
- Zotero.getString('general.updateAvailable') : '',
- msg,
- buttonFlags,
- converterIsRegistered ?
- Zotero.getString('general.upgrade') :
- Zotero.getString('general.install'),
- null, null, null, {});
-
- if (index == 0) {
- var installVersions = {
- converter: converterVersionAvailable ?
- converterVersion : null,
- info: infoVersionAvailable ?
- infoVersion : null
- };
-
- document.getElementById('pdftools-update-button').disabled = true;
- var str = Zotero.getString('zotero.preferences.search.pdf.downloading');
- document.getElementById('pdftools-update-button').setAttribute('label', str);
-
- if (converterVersionAvailable && infoVersionAvailable) {
- Zotero.Fulltext.downloadPDFTool('converter', converterVersion, function (success) {
- if (!success) {
- self.onPDFToolsDownloadError("Error downloading pdftotext");
- return;
- }
- Zotero.Fulltext.downloadPDFTool('info', infoVersion, function (success) {
- if (!success) {
- self.onPDFToolsDownloadError("Error downloading pdfinfo");
- return;
- }
- self.updatePDFToolsStatus();
- });
- });
- }
- else if (converterVersionAvailable) {
- Zotero.Fulltext.downloadPDFTool('converter', converterVersion, function (success) {
- if (!success) {
- self.onPDFToolsDownloadError("Error downloading pdftotext");
- return;
- }
- self.updatePDFToolsStatus();
- });
- }
- else {
- Zotero.Fulltext.downloadPDFTool('info', infoVersion, function (success) {
+ if (converterVersionAvailable && infoVersionAvailable) {
+ Zotero.Fulltext.downloadPDFTool('converter', latestVersion, function (success) {
+ if (!success) {
+ self.onPDFToolsDownloadError("Error downloading pdftotext");
+ return;
+ }
+ Zotero.Fulltext.downloadPDFTool('info', latestVersion, function (success) {
if (!success) {
self.onPDFToolsDownloadError("Error downloading pdfinfo");
return;
}
self.updatePDFToolsStatus();
});
- }
+ });
+ }
+ else if (converterVersionAvailable) {
+ Zotero.Fulltext.downloadPDFTool('converter', latestVersion, function (success) {
+ if (!success) {
+ self.onPDFToolsDownloadError("Error downloading pdftotext");
+ return;
+ }
+ self.updatePDFToolsStatus();
+ });
+ }
+ else {
+ Zotero.Fulltext.downloadPDFTool('info', latestVersion, function (success) {
+ if (!success) {
+ self.onPDFToolsDownloadError("Error downloading pdfinfo");
+ return;
+ }
+ self.updatePDFToolsStatus();
+ });
}
}
}
- // Version not found for platform
- else if (xmlhttp.status == 404) {
- self.onPDFToolsDownloadError(404);
- }
}
catch (e) {
self.onPDFToolsDownloadError(e);
diff --git a/chrome/content/zotero/xpcom/fulltext.js b/chrome/content/zotero/xpcom/fulltext.js
@@ -55,7 +55,7 @@ Zotero.Fulltext = new function(){
//this.clearItemContent = clearItemContent;
this.purgeUnusedWords = purgeUnusedWords;
- this.__defineGetter__("pdfToolsDownloadBaseURL", function() { return 'http://www.zotero.org/download/xpdf/'; });
+ this.__defineGetter__("pdfToolsDownloadBaseURL", function() { return 'https://www.zotero.org/download/xpdf/'; });
this.__defineGetter__("pdfToolsName", function() { return 'Xpdf'; });
this.__defineGetter__("pdfToolsURL", function() { return 'http://www.foolabs.com/xpdf/'; });
this.__defineGetter__("pdfConverterName", function() { return 'pdftotext'; });
@@ -82,9 +82,11 @@ Zotero.Fulltext = new function(){
var _pdfConverterVersion = null;
var _pdfConverterFileName = null;
+ var _pdfConverterScript = null; // nsIFile of hidden window script on Windows
var _pdfConverter = null; // nsIFile to executable
var _pdfInfoVersion = null;
var _pdfInfoFileName = null;
+ var _pdfInfoScript = null; // nsIFile of redirection script
var _pdfInfo = null; // nsIFile to executable
var _idleObserverIsRegistered = false;
@@ -179,7 +181,7 @@ Zotero.Fulltext = new function(){
var fileName = this.pdfInfoFileName;
}
- var spec = this.pdfToolsDownloadBaseURL + fileName + '-' + version;
+ var spec = this.pdfToolsDownloadBaseURL + version + "/" + fileName;
var uri = ioService.newURI(spec, null, null);
var file = Zotero.getTempDirectory();
@@ -202,15 +204,43 @@ Zotero.Fulltext = new function(){
Zotero.File.putContentsAsync(file, is)
.then(function () {
+ var scriptExt = _getScriptExtension();
+ // On Windows, write out script to hide pdftotext console window
+ if (tool == 'converter') {
+ if (Zotero.isWin) {
+ var content = Zotero.File.getContentsFromURL('resource://zotero/hide.' + scriptExt);
+ var scriptFile = Zotero.getTempDirectory();
+ scriptFile.append('pdftotext.' + scriptExt);
+ Zotero.File.putContents(scriptFile, content);
+ }
+ }
+ // Write out output redirection script for pdfinfo
+ else if (tool == 'info') {
+ var content = Zotero.File.getContentsFromURL('resource://zotero/redirect.' + scriptExt);
+ var scriptFile = Zotero.getTempDirectory();
+ scriptFile.append('pdfinfo.' + scriptExt);
+ Zotero.File.putContents(scriptFile, content);
+ }
+
// Set permissions to 755
if (Zotero.isMac) {
file.permissions = 33261;
+ if (scriptFile) {
+ scriptFile.permissions = 33261;
+ }
}
else if (Zotero.isLinux) {
file.permissions = 493;
+ if (scriptFile) {
+ scriptFile.permissions = 493;
+ }
}
var destDir = Zotero.getZoteroDirectory()
+ // Move redirect script and executable into data dir
+ if (scriptFile) {
+ scriptFile.moveTo(destDir, null);
+ }
file.moveTo(destDir, null);
// Write the version number to a file
@@ -223,6 +253,11 @@ Zotero.Fulltext = new function(){
if (callback) {
callback(true);
}
+ })
+ .catch(function (e) {
+ Zotero.debug(e, 1);
+ Components.utils.reportError(e);
+ callback(false);
});
});
}
@@ -274,10 +309,42 @@ Zotero.Fulltext = new function(){
return false;
}
+ // If scripts exist, use those instead
+ switch (tool) {
+ case 'converter':
+ if (Zotero.isWin) {
+ var script = Zotero.getZoteroDirectory();
+ script.append('pdftotext.' + _getScriptExtension())
+ if (script.exists()) {
+ Zotero.debug(script.leafName + " registered");
+ _pdfConverterScript = script;
+ }
+ }
+ break;
+
+ case 'info':
+ var script = Zotero.getZoteroDirectory();
+ script.append('pdfinfo.' + _getScriptExtension())
+ // The redirection script is necessary to run pdfinfo
+ if (!script.exists()) {
+ Zotero.debug(script.leafName + " not found -- PDF statistics disabled");
+ return false;
+ }
+ Zotero.debug(toolName + " redirection script registered");
+ _pdfInfoScript = script;
+ break;
+ }
+
var versionFile = exec.parent;
versionFile.append(fileName + '.version');
if (versionFile.exists()) {
- var version = Zotero.File.getSample(versionFile).split(/[\r\n\s]/)[0];
+ try {
+ var version = Zotero.File.getSample(versionFile).split(/[\r\n\s]/)[0];
+ }
+ catch (e) {
+ Zotero.debug(e, 1);
+ Components.utils.reportError(e);
+ }
}
if (!version) {
var version = 'UNKNOWN';
@@ -295,7 +362,7 @@ Zotero.Fulltext = new function(){
break;
}
- Zotero.debug(toolName + ' version ' + version + ' registered at ' + exec.path);
+ Zotero.debug(toolName + ' version ' + version + ' registered');
return true;
}
@@ -529,16 +596,19 @@ Zotero.Fulltext = new function(){
}
cacheFile.append(this.pdfConverterCacheFile);
- if (_pdfInfo) {
+ if (_pdfInfoScript) {
var infoFile = cacheFile.parent;
infoFile.append(this.pdfInfoCacheFile);
- Zotero.debug('Running pdfinfo "' + file.path + '" "' + infoFile.path + '"');
- var proc = Components.classes["@mozilla.org/process/util;1"].
- createInstance(Components.interfaces.nsIProcess);
- proc.init(_pdfInfo);
+ var args = [_pdfInfo.path, file.path, infoFile.path];
+
+ Zotero.debug("Running " + _pdfInfoScript.path + ' '
+ + args.map(arg => "'" + arg + "'").join(' '));
+
+ var proc = Components.classes["@mozilla.org/process/util;1"]
+ .createInstance(Components.interfaces.nsIProcess);
+ proc.init(_pdfInfoScript);
- var args = [file.path, infoFile.path];
try {
proc.runw(true, args, args.length);
var totalPages = this.getTotalPagesFromFile(itemID);
@@ -551,15 +621,12 @@ Zotero.Fulltext = new function(){
Zotero.debug(this.pdfInfoName + " is not available");
}
- Zotero.debug('Running pdftotext -enc UTF-8 -nopgbrk '
- + (allPages ? '' : '-l ' + maxPages) + ' "' + file.path + '" "'
- + cacheFile.path + '"');
-
- var proc = Components.classes["@mozilla.org/process/util;1"].
- createInstance(Components.interfaces.nsIProcess);
- proc.init(_pdfConverter);
+ var args = []
+ if (_pdfConverterScript) {
+ args.push(_pdfConverter.path);
+ }
+ args.push('-enc', 'UTF-8', '-nopgbrk');
- var args = ['-enc', 'UTF-8', '-nopgbrk'];
if (allPages) {
if (totalPages) {
var pagesIndexed = totalPages;
@@ -570,6 +637,20 @@ Zotero.Fulltext = new function(){
var pagesIndexed = Math.min(maxPages, totalPages);
}
args.push(file.path, cacheFile.path);
+
+ var proc = Components.classes["@mozilla.org/process/util;1"]
+ .createInstance(Components.interfaces.nsIProcess);
+ if (_pdfConverterScript) {
+ Zotero.debug("Running " + _pdfConverterScript.path + ' '
+ + args.map(arg => "'" + arg + "'").join(' '));
+ proc.init(_pdfConverterScript);
+ }
+ else {
+ Zotero.debug("Running " + _pdfConverter.path + ' '
+ + args.map(arg => "'" + arg + "'").join(' '));
+ proc.init(_pdfConverter);
+ }
+
try {
proc.runw(true, args, args.length);
}
@@ -1679,4 +1760,9 @@ Zotero.Fulltext = new function(){
return w;
});
}
+
+ function _getScriptExtension() {
+ return Zotero.isWin ? 'vbs' : 'sh';
+ }
+
}
diff --git a/chrome/content/zotero/xpcom/schema.js b/chrome/content/zotero/xpcom/schema.js
@@ -1590,6 +1590,9 @@ Zotero.Schema = new function(){
var styleUpdates = xmlhttp.responseXML.getElementsByTagName('style');
var updatePDFTools = function () {
+ // No updates for PPC
+ if (Zotero.platform == 'MacPPC') return;
+
let pdfToolsUpdates = xmlhttp.responseXML.getElementsByTagName('pdftools');
if (pdfToolsUpdates.length) {
let availableVersion = pdfToolsUpdates[0].getAttribute('version');
diff --git a/resource/hide.vbs b/resource/hide.vbs
@@ -0,0 +1,24 @@
+Option Explicit
+
+Dim WshShell, fso, exe, args, I
+
+Set WshShell = Wscript.CreateObject("Wscript.Shell")
+Set fso = WScript.CreateObject("Scripting.FileSystemObject")
+
+If WScript.Arguments.Count = 0 Then
+ WScript.Echo "Usage: hidden.vbs program.exe [args...]"
+ WScript.Quit 1
+End If
+
+exe = WScript.Arguments(0)
+If Not(fso.FileExists(exe)) Then
+ WScript.Echo "Executable not found: " & exe
+ WScript.Quit 1
+End If
+
+args = ""
+For I = 1 to WScript.Arguments.Count - 1
+ args = args & " " & chr(34) & WScript.Arguments(I) & chr(34)
+Next
+
+WshShell.Run exe & args, 0, true
diff --git a/resource/redirect.sh b/resource/redirect.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then
+ echo "Usage: $0 cmd source output.txt"
+ exit 1
+fi
+"$1" "$2" > "$3"
diff --git a/resource/redirect.vbs b/resource/redirect.vbs
@@ -0,0 +1,33 @@
+Option Explicit
+
+Dim WshShell
+Dim fso
+Dim exe, src, dest
+
+Set WshShell = Wscript.CreateObject("Wscript.Shell")
+Set fso = WScript.CreateObject("Scripting.FileSystemObject")
+
+If Not(Wscript.Arguments.Count = 3) Then
+ Wscript.Echo "Usage: redirect.vbs <.exe file> <source file> <text file>"
+ WScript.Quit 1
+End If
+
+exe = WScript.Arguments(0)
+src = WScript.Arguments(1)
+dest = WScript.Arguments(2)
+If Not(fso.FileExists(exe)) Then
+ WScript.Echo "Executable not found: " & exe
+ WScript.Quit 1
+End If
+
+If Not(fso.FileExists(src)) Then
+ WScript.Echo "Source file not found: " & src
+ WScript.Quit 1
+End If
+
+If Not(fso.FolderExists(Left(dest, InstrRev(dest, "\")))) Then
+ WScript.Echo "Destination folder not found: " & Left(dest, InstrRev(dest, "\"))
+ WScript.Quit 1
+End If
+
+WshShell.Run "%comspec% /c " & exe & " " & chr(34) & src & chr(34) & " > " & chr(34) & dest & chr(34), 0, true