diff --git a/packages/server/package-lock.json b/packages/server/package-lock.json index 03d30a2a..bf945109 100644 --- a/packages/server/package-lock.json +++ b/packages/server/package-lock.json @@ -16,6 +16,7 @@ "loglevel": "^1.7.0", "loglevel-plugin-prefix": "^0.8.4", "mv": "^2.1.1", + "semver": "^7.3.8", "swagger-jsdoc": "^6.2.5", "swagger-ui-express": "^4.6.0" }, @@ -4384,7 +4385,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -6626,10 +6626,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -8184,8 +8183,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { "version": "2.0.0-1", @@ -11952,7 +11950,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -13694,10 +13691,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "requires": { "lru-cache": "^6.0.0" } @@ -14991,8 +14987,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { "version": "2.0.0-1", diff --git a/packages/server/package.json b/packages/server/package.json index 99419c06..901a9513 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -37,6 +37,7 @@ "loglevel": "^1.7.0", "loglevel-plugin-prefix": "^0.8.4", "mv": "^2.1.1", + "semver": "^7.3.8", "swagger-jsdoc": "^6.2.5", "swagger-ui-express": "^4.6.0" }, diff --git a/packages/server/src/process.js b/packages/server/src/process.js index 59be648a..3094f86a 100644 --- a/packages/server/src/process.js +++ b/packages/server/src/process.js @@ -1,10 +1,21 @@ const util = require('util'); const log = require('loglevel').getLogger('Process'); const exec = util.promisify(require('child_process').exec); +const execSync = require('child_process').execSync; const spawn = require('child_process').spawn; const extend = require('./util').extend; const Process = { + + /** + * @param {string} cmd + * @returns {string} + */ + executeSync(cmd, options) { + const stdout = execSync(cmd, options); + return Buffer.from(stdout).toString().trim(); + }, + /** * @param {string} cmd * @returns {Promise.} diff --git a/packages/server/src/scanimage.js b/packages/server/src/scanimage.js index 65bb3c3d..2bcd2148 100644 --- a/packages/server/src/scanimage.js +++ b/packages/server/src/scanimage.js @@ -4,12 +4,32 @@ const CmdBuilder = require('./command-builder'); /** @type {Configuration} */ const Config = require('./config'); const Constants = require('./constants'); +const Process = require('./process'); +const semver = require('semver'); class Scanimage { + get version() { + if (this._version === undefined) { + const result = Process.executeSync(`${Config.scanimage} -V`); + this._version = /.*backend version (.*)/.exec(result)[1]; + } + return this._version; + } + + get supportsOutputFlag() { + return semver.satisfies(this.version, '>=1.0.28'); + } +} + +class ScanimageCommand { + constructor() { + this.scanimage = new Scanimage(); + } + /** * @returns {string} */ - static devices() { + devices() { return new CmdBuilder(Config.scanimage) .arg('-L') .build(); @@ -19,7 +39,7 @@ class Scanimage { * @param {string} deviceId * @returns {string} */ - static features(deviceId) { + features(deviceId) { return new CmdBuilder(Config.scanimage) .arg('-d', deviceId) .arg('-A') @@ -30,7 +50,7 @@ class Scanimage { * @param {number} page * @returns {string} */ - static filename(page) { + filename(page) { const number = `000${page}`.slice(-4); return `${Config.tempDirectory}/${Constants.TEMP_FILESTEM}-0-${number}.tif`; } @@ -39,7 +59,7 @@ class Scanimage { * @param {ScanRequest} request * @returns {string} */ - static scan(request) { + scan(request) { log.debug(JSON.stringify(request)); const params = request.params; const cmdBuilder = new CmdBuilder(Config.scanimage); @@ -83,19 +103,24 @@ class Scanimage { if (params.mode === 'Lineart' && params.dynamicLineart === false) { cmdBuilder.arg('--disable-dynamic-lineart=yes'); } - if ([Constants.BATCH_AUTO, Constants.BATCH_COLLATE_STANDARD, Constants.BATCH_COLLATE_REVERSE] - .includes(request.batch)) { + if ([Constants.BATCH_AUTO, Constants.BATCH_COLLATE_STANDARD, Constants.BATCH_COLLATE_REVERSE].includes(request.batch)) { const pattern = `${Config.tempDirectory}/${Constants.TEMP_FILESTEM}-${request.index}-%04d.tif`; cmdBuilder.arg(`--batch=${pattern}`); } else { - if ('isPreview' in params && params.isPreview) { - cmdBuilder.arg(`-o ${Config.previewDirectory}/preview.tif`); + const outputFile = 'isPreview' in params && params.isPreview + ? `${Config.previewDirectory}/preview.tif` + : this.filename(request.index); + + if (this.scanimage.supportsOutputFlag) { + cmdBuilder.arg('-o', outputFile); } else { - cmdBuilder.arg(`-o ${Scanimage.filename(request.index)}`); + cmdBuilder.arg(`> '${outputFile}'`); } } return cmdBuilder.build(); } } -module.exports = Scanimage; +const scanimageCommand = new ScanimageCommand(); + +module.exports = scanimageCommand; diff --git a/packages/server/test/config.test.js b/packages/server/test/config.test.js new file mode 100644 index 00000000..9e74282b --- /dev/null +++ b/packages/server/test/config.test.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +const assert = require('assert'); +const Config = require('../src/config'); + +describe('Config', () => { + it.skip('scanimageVersion', () => { + assert.strictEqual(Config.scanimageVersion, '1.0.31'); + }); +}); diff --git a/packages/server/test/scanimage-test.js b/packages/server/test/scanimage-test.js new file mode 100644 index 00000000..d482a458 --- /dev/null +++ b/packages/server/test/scanimage-test.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +const assert = require('assert'); +const Scanimage = require('../src/scanimage'); + +const requestScan = { + params: { + deviceId: 'deviceId', + resolution: '150', + format: 'TIF', + isPreview: false + } +}; + +const requestPreview = { + params: { + deviceId: 'deviceId', + resolution: '150', + format: 'TIF', + isPreview: true + } +}; + +function commandFor(version, request) { + const temp = Scanimage.scanimage._version; + Scanimage.scanimage._version = version; + const command = Scanimage.scan(request); + Scanimage.scanimage._version = temp; + return command; +} + +describe('ScanimageCommand', () => { + it('scanimageVersion:1.0.27:scan', () => { + const command = commandFor('1.0.27', requestScan); + assert.ok(command.match(/.*scanimage.* > 'data\/temp\/~tmp-scan-0-ined.tif'/)); + }); + + it('scanimageVersion:1.0.27:preview', () => { + const command = commandFor('1.0.27', requestPreview); + assert.ok(command.match(/.*scanimage.* > 'data\/preview\/preview.tif'/)); + }); + + it('scanimageVersion:1.0.28:scan', () => { + const command = commandFor('1.0.28', requestScan); + assert.ok(command.match(/.*scanimage.* -o 'data\/temp\/~tmp-scan-0-ined.tif'/)); + }); + + it('scanimageVersion:1.0.28:preview', () => { + const command = commandFor('1.0.28', requestPreview); + assert.ok(command.match(/.*scanimage.* -o 'data\/preview\/preview.tif'/)); + }); + + it('scanimageVersion:1.0.31:scan', () => { + const command = commandFor('1.0.31', requestScan); + assert.ok(command.match(/.*scanimage.* -o 'data\/temp\/~tmp-scan-0-ined.tif'/)); + }); + + it('scanimageVersion:1.0.31:preview', () => { + const command = commandFor('1.0.31', requestPreview); + assert.ok(command.match(/.*scanimage.* -o 'data\/preview\/preview.tif'/)); + }); +});