From 436a164e009b154e463d5430e4ffe318270f7380 Mon Sep 17 00:00:00 2001 From: sbs20 Date: Sat, 18 Feb 2023 18:25:03 +0000 Subject: [PATCH] Fix `scanimage -A` parsing with `[advanced]` Issue #564 There are cases where `scanimage -A` outputs an additional square bracketed item on a parameter line such as: ``` --contrast -127..127 [0] [advanced] ``` The old regex only assumed this happened once - which was good enough for 6 years or so. It seems that it can happen twice. The regex has been updated to have a non-greedy match for the first one (`default`) and there is a subsequent optional non-capturing group for the second, which for want of a better name is referred here as `meta`. It turns out that this pattern has been seen before with `[read-only]` but that was benign since it was treated as a default to ignore. For that reason, the `enabled` flag has to be altered too. Associated unit test added. --- packages/server/src/classes/feature.js | 8 ++--- packages/server/test/device.test.js | 35 ++++++++++++++++++ .../server/test/resource/scanimage-a13.txt | 36 +++++++++++++++++++ 3 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 packages/server/test/resource/scanimage-a13.txt diff --git a/packages/server/src/classes/feature.js b/packages/server/src/classes/feature.js index 1ef9c524..2e95f792 100644 --- a/packages/server/src/classes/feature.js +++ b/packages/server/src/classes/feature.js @@ -28,10 +28,6 @@ module.exports = class Feature { this.load(); } - get enabled() { - return !['inactive', 'read-only'].includes(this.default); - } - asRange() { this.default = round(Number(this.default)); const range = /(.*?)(?:\s|$)/g.exec(this.parameters); @@ -68,10 +64,12 @@ module.exports = class Feature { } load() { - const match = /^\s*([-]{1,2}[-a-zA-Z0-9]+) ?(.*) \[(.*)\]$/g.exec(this.text); + const match = /^\s*([-]{1,2}[-a-zA-Z0-9]+) ?(.*?) \[(.*?)\](?: \[(.*?)\])?$/g.exec(this.text); this.name = match[1]; this.default = match[3]; this.parameters = match[2]; + this.meta = match[4]; + this.enabled = this.default !== 'inactive' && this.meta !== 'read-only'; this.parameters = this.parameters.replace(/^auto\|/, ''); if (this.enabled) { diff --git a/packages/server/test/device.test.js b/packages/server/test/device.test.js index 269ed2b2..163708b2 100644 --- a/packages/server/test/device.test.js +++ b/packages/server/test/device.test.js @@ -258,4 +258,39 @@ describe('Device', () => { assert.strictEqual(device.features['-y'].default, 431.8); }); + it('scanimage-a13.txt', () => { + const file = FileInfo.create('test/resource/scanimage-a13.txt'); + const device = Device.from(file.toText()); + + assert.strictEqual(device.id, 'hpaio:/usb/PSC_1600_series?serial=MY4C3C30Z5L0'); + assert.deepStrictEqual(device.features['--mode'].options, ['Lineart', 'Gray', 'Color']); + assert.strictEqual(device.features['--mode'].default, 'Color'); + assert.deepStrictEqual(device.features['--resolution'].options, [75, 100, 150, 200, 300, 600, 1200]); + assert.strictEqual(device.features['--resolution'].default, 75); + assert.strictEqual(device.features['--contrast'].limits[0], -127); + assert.strictEqual(device.features['--contrast'].limits[1], 127); + assert.strictEqual(device.features['--contrast'].interval, 1); + assert.strictEqual(device.features['--contrast'].default, 0); + assert.strictEqual(device.features['--contrast'].meta, 'advanced'); + assert.strictEqual(device.features['--brightness'].limits[0], -127); + assert.strictEqual(device.features['--brightness'].limits[1], 127); + assert.strictEqual(device.features['--brightness'].interval, 1); + assert.strictEqual(device.features['--brightness'].default, 0); + assert.strictEqual(device.features['--brightness'].meta, 'advanced'); + assert.deepStrictEqual(device.features['--source'].options, ['Flatbed']); + assert.strictEqual(device.features['--source'].default, 'Flatbed'); + assert.strictEqual(device.features['--source'].meta, 'advanced'); + assert.strictEqual(device.features['-l'].limits[0], 0); + assert.strictEqual(device.features['-l'].limits[1], 215.9); + assert.strictEqual(device.features['-l'].default, 0); + assert.strictEqual(device.features['-t'].limits[0], 0); + assert.strictEqual(device.features['-t'].limits[1], 296.9); + assert.strictEqual(device.features['-t'].default, 0); + assert.strictEqual(device.features['-x'].limits[0], 0); + assert.strictEqual(device.features['-x'].limits[1], 215.9); + assert.strictEqual(device.features['-x'].default, 215.9); + assert.strictEqual(device.features['-y'].limits[0], 0); + assert.strictEqual(device.features['-y'].limits[1], 296.9); + assert.strictEqual(device.features['-y'].default, 296.9); + }); }); diff --git a/packages/server/test/resource/scanimage-a13.txt b/packages/server/test/resource/scanimage-a13.txt new file mode 100644 index 00000000..9d4bbcd8 --- /dev/null +++ b/packages/server/test/resource/scanimage-a13.txt @@ -0,0 +1,36 @@ + +All options specific to device `hpaio:/usb/PSC_1600_series?serial=MY4C3C30Z5L0': + Scan mode: + --mode Lineart|Gray|Color [Color] + Selects the scan mode (e.g., lineart, monochrome, or color). + --resolution 75|100|150|200|300|600|1200dpi [75] + Sets the resolution of the scanned image. + Advanced: + --contrast -127..127 [0] [advanced] + Controls the contrast of the acquired image. + --brightness -127..127 [0] [advanced] + Controls the brightness of the acquired image. + --compression None|JPEG [JPEG] [advanced] + Selects the scanner compression method for faster scans, possibly at + the expense of image quality. + --jpeg-quality 0..100 [10] [advanced] + Sets the scanner JPEG compression factor. Larger numbers mean better + compression, and smaller numbers mean better image quality. + --batch-scan[=(yes|no)] [no] [advanced] + Enables continuous scanning with automatic document feeder (ADF). + --source Flatbed [Flatbed] [advanced] + Selects the scan source (such as a document-feeder). + --duplex[=(yes|no)] [inactive] + Enables scanning on both sides of the page. + Geometry: + --length-measurement Unknown|Approximate|Padded [Padded] [advanced] + Selects how the scanned image length is measured and reported, which + is impossible to know in advance for scrollfed scans. + -l 0..215.9mm [0] + Top-left x position of scan area. + -t 0..296.926mm [0] + Top-left y position of scan area. + -x 0..215.9mm [215.9] + Width of scan-area. + -y 0..296.926mm [296.926] + Height of scan-area.