@crowsgear/escl-protocol-scanner 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,69 @@
1
+ /**
2
+ * eSCL Protocol Client for Node.js
3
+ * HTTP client for communicating with eSCL/AirPrint network scanners
4
+ */
5
+ import { ESCLScanner, ESCLCapabilities } from './types';
6
+ /**
7
+ * eSCL HTTP Client
8
+ * Handles all HTTP communication with eSCL-compatible network scanners
9
+ */
10
+ export declare class ESCLClient {
11
+ private timeout;
12
+ constructor(timeout?: number);
13
+ /**
14
+ * Get scanner capabilities
15
+ * @param scanner Target scanner
16
+ * @param debug Enable debug logging (default: false)
17
+ * @returns Scanner capabilities
18
+ */
19
+ getCapabilities(scanner: ESCLScanner, debug?: boolean): Promise<ESCLCapabilities | null>;
20
+ /**
21
+ * Start scan job
22
+ * @param scanner Target scanner
23
+ * @param dpi Resolution in DPI
24
+ * @param colorMode Color mode (BlackAndWhite1, Grayscale8, RGB24)
25
+ * @param source Scan source (Platen, Feeder)
26
+ * @returns Scan job UUID
27
+ */
28
+ createScanJob(scanner: ESCLScanner, dpi: number, colorMode: string, source: string): Promise<string | null>;
29
+ /**
30
+ * Poll scan job status
31
+ * @param scanner Target scanner
32
+ * @param jobId Scan job ID
33
+ * @returns Job status and image URLs if complete
34
+ */
35
+ getScanJobStatus(scanner: ESCLScanner, jobId: string): Promise<{
36
+ status: 'Processing' | 'Completed' | 'Aborted' | 'Unknown';
37
+ images: string[];
38
+ }>;
39
+ /**
40
+ * Download scanned image
41
+ * @param scanner Target scanner
42
+ * @param imageUrl Relative image URL
43
+ * @returns PNG image data as Buffer
44
+ */
45
+ downloadImage(scanner: ESCLScanner, imageUrl: string): Promise<Buffer | null>;
46
+ /**
47
+ * Build eSCL scan settings XML
48
+ */
49
+ private buildScanSettings;
50
+ /**
51
+ * Parse scanner capabilities from XML response
52
+ * @param xml XML response from scanner
53
+ * @param debug Enable debug logging (default: false)
54
+ */
55
+ private parseCapabilities;
56
+ /**
57
+ * HTTP GET request
58
+ */
59
+ private httpGet;
60
+ /**
61
+ * HTTP GET request (binary)
62
+ */
63
+ private httpGetBinary;
64
+ /**
65
+ * HTTP POST request
66
+ */
67
+ private httpPost;
68
+ }
69
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAgB,MAAM,SAAS,CAAC;AAEtE;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAiB;gBAEpB,OAAO,CAAC,EAAE,MAAM;IAM5B;;;;;OAKG;IACG,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAarG;;;;;;;OAOG;IACG,aAAa,CACjB,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAqBzB;;;;;OAKG;IACG,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QACnE,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC;QAC3D,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;IAyBF;;;;;OAKG;IACG,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUnF;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA8BzB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAqEzB;;OAEG;IACH,OAAO,CAAC,OAAO;IAyBf;;OAEG;IACH,OAAO,CAAC,aAAa;IAyBrB;;OAEG;IACH,OAAO,CAAC,QAAQ;CAmCjB"}
package/dist/client.js ADDED
@@ -0,0 +1,320 @@
1
+ "use strict";
2
+ /**
3
+ * eSCL Protocol Client for Node.js
4
+ * HTTP client for communicating with eSCL/AirPrint network scanners
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.ESCLClient = void 0;
41
+ const http = __importStar(require("http"));
42
+ /**
43
+ * eSCL HTTP Client
44
+ * Handles all HTTP communication with eSCL-compatible network scanners
45
+ */
46
+ class ESCLClient {
47
+ constructor(timeout) {
48
+ this.timeout = 10000;
49
+ if (timeout) {
50
+ this.timeout = timeout;
51
+ }
52
+ }
53
+ /**
54
+ * Get scanner capabilities
55
+ * @param scanner Target scanner
56
+ * @param debug Enable debug logging (default: false)
57
+ * @returns Scanner capabilities
58
+ */
59
+ async getCapabilities(scanner, debug = false) {
60
+ try {
61
+ const response = await this.httpGet(`http://${scanner.host}:${scanner.port}/eSCL/ScannerCapabilities`);
62
+ return this.parseCapabilities(response, debug);
63
+ }
64
+ catch (error) {
65
+ console.error(`Failed to get capabilities for ${scanner.name}:`, error);
66
+ return null;
67
+ }
68
+ }
69
+ /**
70
+ * Start scan job
71
+ * @param scanner Target scanner
72
+ * @param dpi Resolution in DPI
73
+ * @param colorMode Color mode (BlackAndWhite1, Grayscale8, RGB24)
74
+ * @param source Scan source (Platen, Feeder)
75
+ * @returns Scan job UUID
76
+ */
77
+ async createScanJob(scanner, dpi, colorMode, source) {
78
+ try {
79
+ const scanSettings = this.buildScanSettings(dpi, colorMode, source);
80
+ const jobResponse = await this.httpPost(`http://${scanner.host}:${scanner.port}/eSCL/ScanJobs`, scanSettings);
81
+ // Extract job UUID from Location header or response
82
+ const jobMatch = jobResponse.match(/\/eSCL\/ScanJobs\/([a-f0-9-]+)/);
83
+ if (jobMatch) {
84
+ return jobMatch[1];
85
+ }
86
+ return null;
87
+ }
88
+ catch (error) {
89
+ console.error(`Failed to create scan job on ${scanner.name}:`, error);
90
+ return null;
91
+ }
92
+ }
93
+ /**
94
+ * Poll scan job status
95
+ * @param scanner Target scanner
96
+ * @param jobId Scan job ID
97
+ * @returns Job status and image URLs if complete
98
+ */
99
+ async getScanJobStatus(scanner, jobId) {
100
+ try {
101
+ const response = await this.httpGet(`http://${scanner.host}:${scanner.port}/eSCL/ScanJobs/${jobId}`);
102
+ const status = response.match(/<JobState>(\w+)<\/JobState>/)?.[1] || 'Unknown';
103
+ const images = [];
104
+ // Extract image URLs
105
+ const imageMatches = response.matchAll(/<DocumentURI>([^<]+)<\/DocumentURI>/g);
106
+ for (const match of imageMatches) {
107
+ images.push(match[1]);
108
+ }
109
+ return {
110
+ status: status,
111
+ images
112
+ };
113
+ }
114
+ catch (error) {
115
+ console.error(`Failed to get scan job status:`, error);
116
+ return { status: 'Unknown', images: [] };
117
+ }
118
+ }
119
+ /**
120
+ * Download scanned image
121
+ * @param scanner Target scanner
122
+ * @param imageUrl Relative image URL
123
+ * @returns PNG image data as Buffer
124
+ */
125
+ async downloadImage(scanner, imageUrl) {
126
+ try {
127
+ const fullUrl = `http://${scanner.host}:${scanner.port}${imageUrl}`;
128
+ return await this.httpGetBinary(fullUrl);
129
+ }
130
+ catch (error) {
131
+ console.error(`Failed to download image:`, error);
132
+ return null;
133
+ }
134
+ }
135
+ /**
136
+ * Build eSCL scan settings XML
137
+ */
138
+ buildScanSettings(dpi, colorMode, source) {
139
+ // Match the Python implementation which uses proper namespaces
140
+ const ESCL_NS = 'http://schemas.hp.com/imaging/escl/2011/05/03';
141
+ const PWG_NS = 'http://www.pwg.org/schemas/2010/12/sm';
142
+ return `<?xml version="1.0" encoding="UTF-8"?>
143
+ <scan:ScanSettings xmlns:scan="${ESCL_NS}" xmlns:pwg="${PWG_NS}">
144
+ <pwg:Version>2.0</pwg:Version>
145
+ <scan:Intent>Document</scan:Intent>
146
+ <pwg:ScanRegions>
147
+ <pwg:ScanRegion>
148
+ <pwg:ContentRegionUnits>escl:ThreeHundredthsOfInches</pwg:ContentRegionUnits>
149
+ <pwg:XOffset>0</pwg:XOffset>
150
+ <pwg:YOffset>0</pwg:YOffset>
151
+ <pwg:Width>3508</pwg:Width>
152
+ <pwg:Height>4961</pwg:Height>
153
+ </pwg:ScanRegion>
154
+ </pwg:ScanRegions>
155
+ <scan:Justification>
156
+ <pwg:XImagePosition>Center</pwg:XImagePosition>
157
+ <pwg:YImagePosition>Center</pwg:YImagePosition>
158
+ </scan:Justification>
159
+ <pwg:InputSource>${source}</pwg:InputSource>
160
+ <scan:ColorMode>${colorMode}</scan:ColorMode>
161
+ <scan:XResolution>${dpi}</scan:XResolution>
162
+ <scan:YResolution>${dpi}</scan:YResolution>
163
+ <pwg:DocumentFormat>image/jpeg</pwg:DocumentFormat>
164
+ </scan:ScanSettings>`;
165
+ }
166
+ /**
167
+ * Parse scanner capabilities from XML response
168
+ * @param xml XML response from scanner
169
+ * @param debug Enable debug logging (default: false)
170
+ */
171
+ parseCapabilities(xml, debug = false) {
172
+ const resolutions = [];
173
+ const colorModes = [];
174
+ const sources = [];
175
+ // Debug: Print raw XML for inspection (only if debug enabled)
176
+ if (debug) {
177
+ console.log('[eSCL] Capabilities XML response (full):');
178
+ console.log(xml);
179
+ }
180
+ // eSCL uses XML namespaces, need to handle scan: and pwg: prefixes
181
+ // Extract resolutions - look for XResolution inside DiscreteResolution
182
+ const discreteResMatches = xml.matchAll(/<[a-z]*:?DiscreteResolution>[\s\S]*?<[a-z]*:?XResolution>(\d+)<\/[a-z]*:?XResolution>[\s\S]*?<\/[a-z]*:?DiscreteResolution>/g);
183
+ for (const match of discreteResMatches) {
184
+ resolutions.push(parseInt(match[1], 10));
185
+ }
186
+ // Also try simpler pattern for XResolution
187
+ if (resolutions.length === 0) {
188
+ const xResMatches = xml.matchAll(/<[a-z]*:?XResolution>(\d+)<\/[a-z]*:?XResolution>/g);
189
+ for (const match of xResMatches) {
190
+ const res = parseInt(match[1], 10);
191
+ if (!resolutions.includes(res)) {
192
+ resolutions.push(res);
193
+ }
194
+ }
195
+ }
196
+ // Extract color modes - handle namespaced tags
197
+ const colorMatches = xml.matchAll(/<[a-z]*:?ColorMode>(\w+)<\/[a-z]*:?ColorMode>/g);
198
+ for (const match of colorMatches) {
199
+ const mode = match[1];
200
+ if (['BlackAndWhite1', 'Grayscale8', 'RGB24'].includes(mode)) {
201
+ colorModes.push(mode);
202
+ }
203
+ }
204
+ // Extract sources - look for InputSource tags or check for Platen/Adf elements
205
+ const sourceMatches = xml.matchAll(/<[a-z]*:?InputSource>(\w+)<\/[a-z]*:?InputSource>/g);
206
+ for (const match of sourceMatches) {
207
+ const source = match[1];
208
+ if (['Platen', 'Adf', 'Feeder'].includes(source)) {
209
+ sources.push(source);
210
+ }
211
+ }
212
+ // If no InputSource tags found, check for Platen/Adf elements directly
213
+ if (sources.length === 0) {
214
+ if (/<[a-z]*:?Platen[\s>]/.test(xml)) {
215
+ sources.push('Platen');
216
+ }
217
+ if (/<[a-z]*:?Adf[\s>]/.test(xml)) {
218
+ sources.push('Adf');
219
+ }
220
+ }
221
+ // Remove duplicates from resolutions
222
+ const uniqueResolutions = Array.from(new Set(resolutions)).sort((a, b) => a - b);
223
+ const uniqueColorModes = Array.from(new Set(colorModes));
224
+ const uniqueSources = Array.from(new Set(sources));
225
+ if (debug) {
226
+ console.log('[eSCL] Parsed capabilities:', { resolutions: uniqueResolutions, colorModes: uniqueColorModes, sources: uniqueSources });
227
+ }
228
+ return { resolutions: uniqueResolutions, colorModes: uniqueColorModes, sources: uniqueSources };
229
+ }
230
+ /**
231
+ * HTTP GET request
232
+ */
233
+ httpGet(url) {
234
+ return new Promise((resolve, reject) => {
235
+ const req = http.get(url, { timeout: this.timeout }, (res) => {
236
+ let data = '';
237
+ res.on('data', (chunk) => {
238
+ data += chunk;
239
+ });
240
+ res.on('end', () => {
241
+ if (res.statusCode === 200) {
242
+ resolve(data);
243
+ }
244
+ else {
245
+ reject(new Error(`HTTP ${res.statusCode}`));
246
+ }
247
+ });
248
+ });
249
+ req.on('timeout', () => {
250
+ req.destroy();
251
+ reject(new Error('Request timeout'));
252
+ });
253
+ req.on('error', reject);
254
+ });
255
+ }
256
+ /**
257
+ * HTTP GET request (binary)
258
+ */
259
+ httpGetBinary(url) {
260
+ return new Promise((resolve, reject) => {
261
+ const req = http.get(url, { timeout: this.timeout }, (res) => {
262
+ const chunks = [];
263
+ res.on('data', (chunk) => {
264
+ chunks.push(chunk);
265
+ });
266
+ res.on('end', () => {
267
+ if (res.statusCode === 200) {
268
+ resolve(Buffer.concat(chunks));
269
+ }
270
+ else {
271
+ reject(new Error(`HTTP ${res.statusCode}`));
272
+ }
273
+ });
274
+ });
275
+ req.on('timeout', () => {
276
+ req.destroy();
277
+ reject(new Error('Request timeout'));
278
+ });
279
+ req.on('error', reject);
280
+ });
281
+ }
282
+ /**
283
+ * HTTP POST request
284
+ */
285
+ httpPost(url, body) {
286
+ return new Promise((resolve, reject) => {
287
+ const options = {
288
+ method: 'POST',
289
+ headers: {
290
+ 'Content-Type': 'application/xml',
291
+ 'Content-Length': Buffer.byteLength(body)
292
+ },
293
+ timeout: this.timeout
294
+ };
295
+ const req = http.request(url, options, (res) => {
296
+ let data = '';
297
+ res.on('data', (chunk) => {
298
+ data += chunk;
299
+ });
300
+ res.on('end', () => {
301
+ if (res.statusCode === 201 || res.statusCode === 200) {
302
+ resolve(data || res.headers.location || '');
303
+ }
304
+ else {
305
+ reject(new Error(`HTTP ${res.statusCode}`));
306
+ }
307
+ });
308
+ });
309
+ req.on('timeout', () => {
310
+ req.destroy();
311
+ reject(new Error('Request timeout'));
312
+ });
313
+ req.on('error', reject);
314
+ req.write(body);
315
+ req.end();
316
+ });
317
+ }
318
+ }
319
+ exports.ESCLClient = ESCLClient;
320
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * eSCL Scanner Discovery via Python Subprocess
3
+ * Uses Python's zeroconf library for mDNS discovery
4
+ */
5
+ import { ESCLScanner, DiscoveryResponse } from './types';
6
+ /**
7
+ * eSCL Scanner Discovery Service Options
8
+ */
9
+ export interface ESCLDiscoveryOptions {
10
+ /** Custom Python executable path (e.g., /path/to/venv/bin/python3) */
11
+ pythonPath?: string;
12
+ }
13
+ /**
14
+ * eSCL Scanner Discovery Service
15
+ * Spawns Python subprocess to handle mDNS discovery using zeroconf
16
+ */
17
+ export declare class ESCLDiscovery {
18
+ private pythonProcess;
19
+ private discovered;
20
+ private listeners;
21
+ private timeout;
22
+ private processReady;
23
+ private pythonPath;
24
+ constructor(timeout?: number, options?: ESCLDiscoveryOptions);
25
+ /**
26
+ * Validate Python path exists and is executable
27
+ * @throws Error if Python path is invalid
28
+ */
29
+ private validatePythonPath;
30
+ /**
31
+ * Start discovering scanners
32
+ * @param timeout Optional timeout in milliseconds (default: 5000ms)
33
+ * @returns Promise resolving with discovery response containing success status and scanner data
34
+ */
35
+ startDiscovery(timeout?: number): Promise<DiscoveryResponse>;
36
+ /**
37
+ * Stop active discovery and cleanup subprocess
38
+ */
39
+ stopDiscovery(): void;
40
+ /**
41
+ * Get currently discovered scanners
42
+ */
43
+ getScanners(): ESCLScanner[];
44
+ /**
45
+ * Subscribe to scanner discovery updates
46
+ */
47
+ onScannerDiscovered(callback: (scanners: ESCLScanner[]) => void): void;
48
+ /**
49
+ * Unsubscribe from scanner discovery updates
50
+ */
51
+ offScannerDiscovered(callback: (scanners: ESCLScanner[]) => void): void;
52
+ /**
53
+ * Start Python subprocess for eSCL operations
54
+ */
55
+ private startPythonService;
56
+ /**
57
+ * Cleanup Python subprocess
58
+ */
59
+ private cleanup;
60
+ /**
61
+ * Notify all listeners of scanner changes
62
+ */
63
+ private notifyListeners;
64
+ }
65
+ /**
66
+ * Convenience function for quick scanner discovery
67
+ * @param timeout Discovery timeout in milliseconds (default: 5000)
68
+ * @returns Discovery response with success status and scanner data
69
+ */
70
+ export declare function discoverScanners(timeout?: number): Promise<DiscoveryResponse>;
71
+ //# sourceMappingURL=discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAE,WAAW,EAA6B,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEpF;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,UAAU,CAAuC;IACzD,OAAO,CAAC,SAAS,CAAqD;IACtE,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,UAAU,CAAqB;gBAE3B,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB;IAS5D;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAuB1B;;;;OAIG;IACG,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAoDlE;;OAEG;IACH,aAAa,IAAI,IAAI;IAIrB;;OAEG;IACH,WAAW,IAAI,WAAW,EAAE;IAI5B;;OAEG;IACH,mBAAmB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,IAAI,GAAG,IAAI;IAItE;;OAEG;IACH,oBAAoB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,IAAI,GAAG,IAAI;IAIvE;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA6D1B;;OAEG;IACH,OAAO,CAAC,OAAO;IA2Bf;;OAEG;IACH,OAAO,CAAC,eAAe;CAMxB;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,GAAE,MAAa,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAGzF"}