node-hp-scan-to 1.1.0 → 1.2.2

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.
Files changed (42) hide show
  1. package/README.md +74 -6
  2. package/dist/Destination.js +10 -9
  3. package/dist/Destination.js.map +1 -1
  4. package/dist/Event.d.ts +10 -6
  5. package/dist/Event.js +18 -2
  6. package/dist/Event.js.map +1 -1
  7. package/dist/HPApi.d.ts +13 -4
  8. package/dist/HPApi.js +198 -61
  9. package/dist/HPApi.js.map +1 -1
  10. package/dist/Job.d.ts +13 -1
  11. package/dist/Job.js +32 -0
  12. package/dist/Job.js.map +1 -1
  13. package/dist/JpegUtil.d.ts +26 -0
  14. package/dist/JpegUtil.js +238 -0
  15. package/dist/JpegUtil.js.map +1 -0
  16. package/dist/PathHelper.d.ts +5 -0
  17. package/dist/PathHelper.js +79 -0
  18. package/dist/PathHelper.js.map +1 -0
  19. package/dist/ScanContent.d.ts +12 -0
  20. package/dist/ScanContent.js +82 -0
  21. package/dist/ScanContent.js.map +1 -0
  22. package/dist/ScanStatus.d.ts +1 -1
  23. package/dist/ScanStatus.js.map +1 -1
  24. package/dist/WalkupScanToCompDestinations.js +3 -3
  25. package/dist/WalkupScanToCompDestinations.js.map +1 -1
  26. package/dist/WalkupScanToCompEvent.d.ts +10 -0
  27. package/dist/WalkupScanToCompEvent.js +17 -0
  28. package/dist/WalkupScanToCompEvent.js.map +1 -0
  29. package/dist/index.js +406 -102
  30. package/dist/index.js.map +1 -1
  31. package/package.json +26 -13
  32. package/src/Destination.ts +8 -7
  33. package/src/Event.ts +21 -7
  34. package/src/HPApi.ts +117 -43
  35. package/src/Job.ts +39 -1
  36. package/src/JpegUtil.ts +319 -0
  37. package/src/PathHelper.ts +44 -0
  38. package/src/ScanContent.ts +34 -0
  39. package/src/ScanStatus.ts +1 -1
  40. package/src/WalkupScanToCompDestinations.ts +4 -5
  41. package/src/WalkupScanToCompEvent.ts +18 -0
  42. package/src/index.ts +390 -76
@@ -0,0 +1,319 @@
1
+ let debug = false;
2
+
3
+ const start_of_Frame_0 = "FFC0";
4
+ const define_number_of_lines = "FFDC";
5
+
6
+ export default class JpegUtil {
7
+ static setDebug(dbg: boolean) {
8
+ debug = dbg;
9
+ }
10
+
11
+ private static logDebug(msg: any) {
12
+ if (debug) {
13
+ console.log(msg);
14
+ }
15
+ }
16
+
17
+ private static numToHex(s: number) {
18
+ return s.toString(16).padStart(2, "0").toUpperCase();
19
+ }
20
+
21
+ static GetJpgSize(buffer: Buffer): { height: number; width: number } | null {
22
+ let size: { height: number; width: number } | null = null;
23
+ this.parse(buffer, {
24
+ [start_of_Frame_0]: (start: number, length: number) => {
25
+ // read the the "Start of frame" marker which contains the file size
26
+ if (6 < length) {
27
+ const pHeight = buffer[start + 3] * 256 + buffer[start + 4];
28
+ const pWidth = buffer[start + 5] * 256 + buffer[start + 6];
29
+
30
+ size = { height: pHeight, width: pWidth };
31
+ }
32
+
33
+ // stop processing
34
+ return true;
35
+ },
36
+ });
37
+ return size;
38
+ }
39
+
40
+ static setJpgSize(
41
+ buffer: Buffer,
42
+ size: { height: number; width: number }
43
+ ): boolean {
44
+ let sizeWritten = false;
45
+ const parsingSucceed = this.parse(buffer, {
46
+ [start_of_Frame_0]: (start: number, length: number) => {
47
+ // read the the "Start of frame" marker which contains the file size
48
+
49
+ // write the picture size
50
+ if (6 < length) {
51
+ const height = Buffer.from([0x00, 0x00]);
52
+ height.writeInt16BE(size.height);
53
+ buffer[start + 3] = height[0];
54
+ buffer[start + 4] = height[1];
55
+
56
+ const width = Buffer.from([0x00, 0x00]);
57
+ width.writeInt16BE(size.width);
58
+ buffer[start + 5] = width[0];
59
+ buffer[start + 6] = width[1];
60
+
61
+ sizeWritten = true;
62
+ }
63
+
64
+ // stop processing
65
+ return true;
66
+ },
67
+ });
68
+
69
+ if (!parsingSucceed) {
70
+ return false;
71
+ }
72
+
73
+ return sizeWritten;
74
+ }
75
+
76
+ static setJpgHeight(buffer: Buffer, height: number): boolean {
77
+ let heightWritten = false;
78
+ const parsingSucceed = this.parse(buffer, {
79
+ [start_of_Frame_0]: (start: number, length: number) => {
80
+ // read the the "Start of frame" marker which contains the file size
81
+
82
+ // write the picture size
83
+ heightWritten = this.writeHeightInStartOfFrame(
84
+ buffer,
85
+ start,
86
+ length,
87
+ height
88
+ );
89
+
90
+ // stop processing
91
+ return true;
92
+ },
93
+ });
94
+
95
+ if (!parsingSucceed) {
96
+ return false;
97
+ }
98
+
99
+ return heightWritten;
100
+ }
101
+
102
+ static fixSizeWithDNL(buffer: Buffer): number | null {
103
+ let numberOfLine: number | null = null;
104
+ let startOfStartOfFrame: number | null = null;
105
+ let lengthOfStartOfFrame: number | null = null;
106
+ this.parse(buffer, {
107
+ [define_number_of_lines]: (start: number, length: number) => {
108
+ numberOfLine = this.readNumberOfLineFromDNL(buffer, start, length);
109
+ return false; // don't stop
110
+ },
111
+ [start_of_Frame_0]: (start: number, length: number) => {
112
+ startOfStartOfFrame = start;
113
+ lengthOfStartOfFrame = length;
114
+ return false; // don't stop
115
+ },
116
+ });
117
+
118
+ if (numberOfLine == null) {
119
+ this.logDebug("DNL marker not found impossible to fix height");
120
+ return null;
121
+ }
122
+
123
+ if (startOfStartOfFrame == null || lengthOfStartOfFrame == null) {
124
+ this.logDebug(
125
+ "Start of frame 0 not found, either jpeg parsing is broken either the stream is corrupted"
126
+ );
127
+ return null;
128
+ }
129
+
130
+ if (
131
+ this.writeHeightInStartOfFrame(
132
+ buffer,
133
+ startOfStartOfFrame,
134
+ lengthOfStartOfFrame,
135
+ numberOfLine
136
+ )
137
+ ) {
138
+ return numberOfLine;
139
+ }
140
+ return null;
141
+ }
142
+
143
+ private static writeHeightInStartOfFrame(
144
+ buffer: Buffer,
145
+ startOfStartOfFrame: number,
146
+ lengthOfStartOfFrame: number,
147
+ numberOfLine: number
148
+ ) : boolean {
149
+ // write the picture height
150
+ if (6 < lengthOfStartOfFrame) {
151
+ const heightBuffer = Buffer.from([0x00, 0x00]);
152
+ heightBuffer.writeInt16BE(numberOfLine);
153
+ buffer[startOfStartOfFrame + 3] = heightBuffer[0];
154
+ buffer[startOfStartOfFrame + 4] = heightBuffer[1];
155
+
156
+ return true;
157
+ }
158
+
159
+ return false;
160
+ }
161
+
162
+ static readNumberOfLineFromDNL(
163
+ buffer: Buffer,
164
+ start: number,
165
+ length: number
166
+ ): number | null {
167
+ let numberOfLine: number | null = null;
168
+
169
+ // read the number of line
170
+ if (3 < length) {
171
+ numberOfLine = buffer[start + 2] * 256 + buffer[start + 3];
172
+ }
173
+
174
+ return numberOfLine;
175
+ }
176
+ static parse(
177
+ buffer: Buffer,
178
+ markerHandler: { [key: string]: (start: number, length: number) => boolean }
179
+ ): boolean {
180
+ let i: number = 0;
181
+
182
+ if (!this.isSOIHeader(i, buffer)) {
183
+ this.logDebug("Not a valid SOI header");
184
+ return false;
185
+ }
186
+
187
+ i += 4;
188
+
189
+ // Check for valid JPEG header (null terminated JFIF)
190
+ if (!this.isValidJpegHeader(i, buffer)) {
191
+ this.logDebug("Not a valid JFIF string");
192
+ return false;
193
+ }
194
+
195
+ return this.parseMarker(buffer, i, markerHandler);
196
+ }
197
+
198
+ private static isValidJpegHeader(i: number, buffer: Buffer) {
199
+ return (
200
+ i + 6 < buffer.length &&
201
+ buffer[i + 2] == "J".charCodeAt(0) &&
202
+ buffer[i + 3] == "F".charCodeAt(0) &&
203
+ buffer[i + 4] == "I".charCodeAt(0) &&
204
+ buffer[i + 5] == "F".charCodeAt(0) &&
205
+ buffer[i + 6] == 0x00
206
+ );
207
+ }
208
+
209
+ private static isSOIHeader(i: number, buffer: Buffer) {
210
+ return (
211
+ i + 3 < buffer.length &&
212
+ buffer[i] == 0xff &&
213
+ buffer[i + 1] == 0xd8 &&
214
+ buffer[i + 2] == 0xff &&
215
+ buffer[i + 3] == 0xe0
216
+ );
217
+ }
218
+
219
+ private static getBlockLength(
220
+ buffer: Buffer,
221
+ i: number,
222
+ marker: string
223
+ ): number | null {
224
+ if (
225
+ marker === "FFDA" ||
226
+ marker === "FFD0" ||
227
+ marker === "FFD1" ||
228
+ marker === "FFD2" ||
229
+ marker === "FFD3" ||
230
+ marker === "FFD4" ||
231
+ marker === "FFD5" ||
232
+ marker === "FFD6" ||
233
+ marker === "FFD7"
234
+ ) {
235
+ return JpegUtil.findCurrentBlockSize(buffer, i + 2, marker);
236
+ } else {
237
+ // read the new block length
238
+ const blockLength = buffer[i + 2] * 256 + buffer[i + 3];
239
+
240
+ this.logDebug(`block size for ${marker} is ${blockLength}`);
241
+ return blockLength;
242
+ }
243
+ }
244
+
245
+ private static findCurrentBlockSize(
246
+ buffer: Buffer,
247
+ i: number,
248
+ current_marker: string
249
+ ): number | null {
250
+ for (let j = 0; i + j < buffer.length; j++) {
251
+ if (buffer[i + j] === 0xff) {
252
+ if (i + j + 1 < buffer.length) {
253
+ if (buffer[i + j + 1] == 0x00) {
254
+ // it's ok just continue
255
+ } else {
256
+ // we've just found the end of the Start of Scan
257
+ return j;
258
+ }
259
+ } else {
260
+ this.logDebug(
261
+ `Premature end of stream reach while searching for the block size inside marker ${current_marker}`
262
+ );
263
+ return null;
264
+ }
265
+ }
266
+ }
267
+ return null;
268
+ }
269
+
270
+ private static parseMarker(buffer: Buffer, i: number, markerHandler: { [key: string]: (start: number, length: number) => boolean }) : boolean {
271
+ let marker = "";
272
+
273
+ //Retrieve the block length of the first block since the first block will not contain the size of file
274
+ let blockLength = buffer[i] * 256 + buffer[i + 1];
275
+
276
+ //Increase the file index to get to the next block
277
+ i += blockLength;
278
+ while (i < buffer.length) {
279
+ if (buffer[i] != 0xff) {
280
+ this.logDebug(
281
+ "We should be at the begining of the next block, but got: " +
282
+ buffer[i]
283
+ );
284
+ return false;
285
+ }
286
+
287
+ if (i + 1 >= buffer.length) {
288
+ this.logDebug("End of stream prematurely found in marker: " + marker);
289
+ return false;
290
+ }
291
+
292
+ if (buffer[i + 1] == 0x00) {
293
+ this.logDebug(`Bad marker at ${i} 0x00 just after marker ${marker}`);
294
+ return false;
295
+ }
296
+
297
+ marker = this.numToHex(buffer[i]) + this.numToHex(buffer[i + 1]);
298
+
299
+ const foundBlockLength = this.getBlockLength(buffer, i, marker);
300
+ if (foundBlockLength == null) {
301
+ this.logDebug(
302
+ `Was not able to determine block size for marker ${marker}`
303
+ );
304
+ return false;
305
+ }
306
+ blockLength = foundBlockLength;
307
+
308
+ const handler = markerHandler[marker];
309
+ if (handler != null && handler(i + 2, blockLength)) {
310
+ return true;
311
+ }
312
+
313
+ i = i + 2 + blockLength;
314
+ }
315
+
316
+ this.logDebug("End of payload reached");
317
+ return true;
318
+ }
319
+ }
@@ -0,0 +1,44 @@
1
+ import path from "path";
2
+ import dateformat from "dateformat";
3
+ import fs from "fs/promises";
4
+ import os from "os";
5
+
6
+ export default class PathHelper {
7
+ static getFileForPage(
8
+ folder: string,
9
+ scanCount: number,
10
+ currentPageNumber: number,
11
+ filePattern : string | undefined,
12
+ extension: string
13
+ ): string {
14
+
15
+ if (filePattern) {
16
+ return path.join(folder, `${dateformat(new Date(), filePattern)}.${extension}`);
17
+ }
18
+
19
+ return path.join(folder, `scan${scanCount}_page${currentPageNumber}.${extension}`);
20
+ }
21
+
22
+ static getFileForScan(
23
+ folder: string,
24
+ scanCount: number,
25
+ filePattern : string | undefined,
26
+ extension: string
27
+ ): string {
28
+
29
+ if (filePattern) {
30
+ return path.join(folder, `${dateformat(new Date(), filePattern)}.${extension}`);
31
+ }
32
+
33
+ return path.join(folder, `scan${scanCount}.${extension}`);
34
+ }
35
+
36
+ static async getOutputFolder(folder: string | undefined) {
37
+ if (!folder) {
38
+ folder = await fs.mkdtemp(
39
+ path.join(os.tmpdir(), "scan-to-pc")
40
+ );
41
+ }
42
+ return folder;
43
+ }
44
+ }
@@ -0,0 +1,34 @@
1
+ import fs from "fs/promises";
2
+
3
+ import { jsPDF } from "jspdf";
4
+
5
+ export interface ScanContent {
6
+ elements: ScanPage[];
7
+ }
8
+ export interface ScanPage {
9
+ path: string;
10
+ pageNumber: number;
11
+ width: number;
12
+ height: number;
13
+ xResolution: number;
14
+ yResolution: number;
15
+ }
16
+
17
+ export async function createPdfFrom(scanContent: ScanContent, destination: string) {
18
+ let doc: jsPDF | null = null;
19
+ for (const element of scanContent.elements) {
20
+ const widthInInches = element.width / element.xResolution;
21
+ const heightInInches = element.height / element.yResolution;
22
+ const format = [widthInInches, heightInInches];
23
+
24
+ if (doc == null) {
25
+ doc = new jsPDF({ unit: "in", floatPrecision: 3, format });
26
+ } else {
27
+ doc.addPage(format);
28
+ }
29
+
30
+ const imageByteBuffer = await fs.readFile(element.path);
31
+ doc.addImage(imageByteBuffer, "JPEG", 0, 0, widthInInches, heightInInches);
32
+ }
33
+ doc?.save(destination);
34
+ }
package/src/ScanStatus.ts CHANGED
@@ -30,7 +30,7 @@ export default class ScanStatus {
30
30
  return this.adfState === "Loaded";
31
31
  }
32
32
 
33
- getInputSource(): string {
33
+ getInputSource(): "Adf" | "Platen" {
34
34
  return this.isLoaded() ? "Adf" : "Platen";
35
35
  }
36
36
  }
@@ -17,15 +17,14 @@ export default class WalkupScanToCompDestinations {
17
17
  }
18
18
 
19
19
  get destinations(): WalkupScanToCompDestination[] {
20
- let WalkupScanToCompDestinations = this.data[
21
- "wus:WalkupScanToCompDestinations"
22
- ];
20
+ let walkupScanToCompDestinations =
21
+ this.data["wus:WalkupScanToCompDestinations"];
23
22
  if (
24
- WalkupScanToCompDestinations.hasOwnProperty(
23
+ walkupScanToCompDestinations.hasOwnProperty(
25
24
  "wus:WalkupScanToCompDestination"
26
25
  )
27
26
  ) {
28
- return WalkupScanToCompDestinations[
27
+ return walkupScanToCompDestinations[
29
28
  "wus:WalkupScanToCompDestination"
30
29
  ].map((x) => new WalkupScanToCompDestination(x));
31
30
  } else {
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ export interface WalkupScanToCompEventData {
4
+ "wus:WalkupScanToCompEvent": {
5
+ 'wus:WalkupScanToCompEventType': string[]
6
+ }
7
+ }
8
+
9
+ export default class WalkupScanToCompEvent {
10
+ private readonly data: WalkupScanToCompEventData;
11
+ constructor(data: WalkupScanToCompEventData) {
12
+ this.data = data;
13
+ }
14
+
15
+ get eventType(): string {
16
+ return this.data["wus:WalkupScanToCompEvent"]["wus:WalkupScanToCompEventType"][0];
17
+ }
18
+ }