diff --git a/README.md b/README.md
index 89d7ead..c44bc93 100644
--- a/README.md
+++ b/README.md
@@ -1,88 +1,158 @@
# Document Scanner SDK for Node.js
-The package provides Node.js APIs for invoking **Dynamic Web TWAIN Service REST API**. It helps developers to create **desktop** or **server-side** document scanning applications with ease.
-
-## Key Features
-- π¨οΈ βMulti-driver Support
- - TWAIN (32-bit & 64-bit)
- - WIA (Windows Image Acquisition)
- - SANE (Scanner Access Now Easy)
- - ICA (Image Capture Architecture)
- - eSCL (AirScan/Mopria)
-- π βCross-platform Compatibility
- - Windows 7+
- - Linux (x64/ARM64/MIPS64)
- - macOS 10.15+
-
-## Prerequisites
-- Install Dynamic Web TWAIN Service.
- - Windows: [Dynamsoft-Service-Setup.msi](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup.msi)
- - macOS: [Dynamsoft-Service-Setup.pkg](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup.pkg)
- - Linux:
- - [Dynamsoft-Service-Setup.deb](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup.deb)
- - [Dynamsoft-Service-Setup-arm64.deb](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup-arm64.deb)
- - [Dynamsoft-Service-Setup-mips64el.deb](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup-mips64el.deb)
- - [Dynamsoft-Service-Setup.rpm](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup.rpm)
-
-- Request a [free trial license](https://www.dynamsoft.com/customer/license/trialLicense?product=dwt) for Dynamic Web TWAIN Service.
-
-## Dynamic Web TWAIN Service Configuration
-After installing the Dynamic Web TWAIN Service, navigate to `http://127.0.0.1:18625/` in a web browser to configure the host and port settings. The default host IP address is set to `127.0.0.1`. If you wish to make the service accessible over the local network in your office or company, you can update the host setting to a LAN IP address, such as **192.168.8.72**.
+
+The package provides Node.js APIs for invoking the **Dynamic Web TWAIN Service REST API**. It helps developers create **desktop** or **server-side** document scanning and processing applications with ease.
+
+---
+
+## π Key Features
+
+- π¨οΈ **Multi-Driver Support**
+ - TWAIN (32-bit & 64-bit)
+ - WIA (Windows Image Acquisition)
+ - SANE (Scanner Access Now Easy)
+ - ICA (Image Capture Architecture)
+ - eSCL (AirScan/Mopria)
+ - Wi-Fi Direct
+
+- π **Cross-Platform Compatibility**
+ - Windows 7+
+ - macOS 10.15+
+ - Linux (x64 / ARM64 / MIPS64)
+
+---
+
+## βοΈ Prerequisites
+
+### β
Install Dynamic Web TWAIN Service:
+
+- **Windows**: [Dynamsoft-Service-Setup.msi](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup.msi)
+- **macOS**: [Dynamsoft-Service-Setup.pkg](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup.pkg)
+- **Linux**:
+ - [Dynamsoft-Service-Setup.deb](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup.deb)
+ - [Dynamsoft-Service-Setup-arm64.deb](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup-arm64.deb)
+ - [Dynamsoft-Service-Setup-mips64el.deb](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup-mips64el.deb)
+ - [Dynamsoft-Service-Setup.rpm](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup.rpm)
+
+### π Get a License
+
+Request a [free trial license](https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&package=cross-platform) for the Dynamic Web TWAIN Service.
+
+---
+
+## π§© Configuration
+
+After installation, open `http://127.0.0.1:18625/` in your browser to configure the **host** and **port** settings.
+
+> By default, the service is bound to `127.0.0.1`. To access it across the LAN, change the host to your local IP (e.g., `192.168.8.72`).

-## REST API Reference
-By default, the REST API's host address is set to `http://127.0.0.1:18622`.
+---
+
+## π‘ REST API Endpoints
+
+[https://www.dynamsoft.com/web-twain/docs/info/api/restful.html](https://www.dynamsoft.com/web-twain/docs/info/api/restful.html)
+
+## π¦ Node.js APIs
+
+### π Scanner APIs
+
+- `getDevices(host, scannerType)`
+ Get available scanners. Returns an array of devices.
+
+- `createJob(host, parameters)`
+ Create a new scan job. Returns a job object.
-| Method | Endpoint | Description | Parameters | Response |
-|--------|-----------------|-------------------------------|------------------------------------|-------------------------------|
-| GET | `/DWTAPI/Scanners` | Get a list of scanners | None | `200 OK` with scanner list |
-| POST | `/DWTAPI/ScanJobs` | Creates a scan job | `license`, `device`, `config` | `201 Created` with job ID |
-| GET | `/DWTAPI/ScanJobs/:id/NextDocument`| Retrieves a document image | `id`: Job ID | `200 OK` with image stream |
-| DELETE | `/DWTAPI/ScanJobs/:id`| Deletes a scan job | `id`: Job ID | `200 OK` |
+- `checkJob(host, jobId)`
+ Check job status (e.g., running, canceled, etc.)
-## Node.js API
-- `getDevices(host, scannerType)` - Get all available scanners. It returns an array of scanner objects.
-- `scanDocument(host, parameters, timeout)` - Create a scanner job by feeding one or multiple physical documents. It returns the job id.
-- `getImageFile(host, jobId, directory)` - Get one document image by job id. The directory specifies the physical location to save the images. It returns the image path.
-- `getImageFiles(host, jobId, directory)` - Get document images by job id. The directory specifies the physical location to save the images. It returns an array of image paths.
-- `deleteJob(host, jobId)` - Delete a scan job by job id. It can interrupt the scan process.
-- `getImageStreams(host, jobId)` - Get document images by job id. It returns an array of image streams.
+- `deleteJob(host, jobId)`
+ Delete a scan job and terminate scanning.
-## Parameter Configuration
-The parameter configuration is based on [Dynamsoft Web TWAIN documentation](https://www.dynamsoft.com/web-twain/docs/info/api/Interfaces.html#DeviceConfiguration). It controls the behavior of the scanner.
+- `updateJob(host, jobId, parameters)`
+ Update job status (e.g., cancel a running job).
-For example, you can set the resolution to 200 DPI and the pixel type to color:
+- `getScannerCapabilities(host, jobId)`
+ Get scanner capabilities like resolution, color modes.
+
+### πΌοΈ Image Retrieval
+
+- `getImageFile(host, jobId, directory)`
+ Fetch one image and save to local disk.
+
+- `getImageFiles(host, jobId, directory)`
+ Fetch all images for a job and save to local disk.
+
+- `getImageStream(host, jobId)`
+ Fetch one image as a readable stream.
+
+- `getImageStreams(host, jobId)`
+ Fetch all images as streams.
+
+- `getImageInfo(host, jobId)`
+ Retrieve metadata of the next page.
+
+### π Document APIs
+
+- `createDocument(host, parameters)`
+ Create a new empty document (PDF).
+
+- `getDocumentInfo(host, docId)`
+ Get document metadata and structure.
+
+- `deleteDocument(host, docId)`
+ Delete an existing document.
+
+- `getDocumentFile(host, docId, directory)`
+ Download the document and save as a PDF.
+
+- `getDocumentStream(host, docId)`
+ Download document as a stream.
+
+- `insertPage(host, docId, parameters)`
+ Insert a new page into an existing document.
+
+- `deletePage(host, docId, pageId)`
+ Remove a page from an existing document.
+
+---
+
+## βοΈ Scan Job Parameters
+
+The configuration follows [Dynamsoft Web TWAIN DeviceConfiguration](https://www.dynamsoft.com/web-twain/docs/info/api/Interfaces.html#DeviceConfiguration).
```js
let parameters = {
- license: "LICENSE-KEY",
- device: devices[index].device,
-};
-
-parameters.config = {
+ license: "LICENSE-KEY",
+ device: devices[0].device,
+ config: {
IfShowUI: false,
- PixelType: 2, // color
+ PixelType: 2, // Color
Resolution: 200,
IfFeederEnabled: false,
- IfDuplexEnabled: false,
+ IfDuplexEnabled: false
+ }
};
```
-## Examples
-Set the `LICENSE-KEY` before running the following examples.
+---
+
+## π§ͺ Examples
-- [Command-line](https://github.com/yushulx/dynamsoft-service-REST-API/tree/main/examples/command-line)
+Set the `LICENSE-KEY` before running the examples.
- Get all available scanners
+### π₯οΈ Command-line App
- 
+- [Command-line Example](https://github.com/yushulx/dynamsoft-service-REST-API/tree/main/examples/command-line)
- Acquire a Document
-
- 
+ - Discover devices
+ 
-- [Web server](https://github.com/yushulx/dynamsoft-service-REST-API/tree/main/examples/web)
+ - Scan and save documents
+ 
- 
+### π Web Server App
+- [Web Server Example](https://github.com/yushulx/dynamsoft-service-REST-API/tree/main/examples/web)
+ 
diff --git a/examples/REST/app.js b/examples/REST/app.js
index f9e2dc3..1e61da4 100644
--- a/examples/REST/app.js
+++ b/examples/REST/app.js
@@ -20,12 +20,12 @@ app.get('/devices', (req, res) => {
});
});
-app.post('/scandocument', async (req, res) => {
- const json = req.body;
+app.post('/createJob', async (req, res) => {
+ const data = req.body;
let parameters = {
license: "DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==",
- device: json['scan'],
+ device: data['scan'],
};
parameters.config = {
@@ -38,7 +38,8 @@ app.post('/scandocument', async (req, res) => {
IfDuplexEnabled: false,
};
- let jobId = await docscan4nodejs.scanDocument(dynamsoftService, parameters);
+ let json = await docscan4nodejs.createJob(dynamsoftService, parameters);
+ let jobId = json.jobuid;
let filename = await docscan4nodejs.getImageFile(dynamsoftService, jobId, './public');
res.send(JSON.stringify({
diff --git a/examples/REST/public/index.html b/examples/REST/public/index.html
index c904771..c703491 100644
--- a/examples/REST/public/index.html
+++ b/examples/REST/public/index.html
@@ -62,7 +62,7 @@
Document Scanning Sample
async function acquireImage() {
if (devices.length > 0 && selectSources.selectedIndex >= 0) {
- const response = await fetch('/scandocument', {
+ const response = await fetch('/createJob', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
diff --git a/examples/command-line/app.js b/examples/command-line/app.js
index 5dd96d8..65f716f 100644
--- a/examples/command-line/app.js
+++ b/examples/command-line/app.js
@@ -2,9 +2,12 @@ const docscan4nodejs = require("../../index.js")
const readline = require('readline');
let devices = [];
-// let host = 'http://192.168.8.119:18622';
let host = 'http://127.0.0.1:18622';
+docscan4nodejs.getServerInfo(host).then((info) => {
+ console.log('Server info:', info);
+});
+
const questions = `
Please select an operation:
1. Get scanners
@@ -51,6 +54,7 @@ function askQuestion() {
let parameters = {
license: "DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==",
device: devices[index].device,
+ autoRun: true
};
parameters.config = {
@@ -59,23 +63,59 @@ function askQuestion() {
//XferCount: 1,
//PageSize: 1,
Resolution: 200,
- IfFeederEnabled: false,
+ IfFeederEnabled: true,
IfDuplexEnabled: false,
};
- docscan4nodejs.scanDocument(host, parameters).then((jobId) => {
- if (jobId !== '') {
- console.log('job id: ' + jobId);
+ docscan4nodejs.createJob(host, parameters).then((job) => {
+ try {
+ let jobId = job.jobuid;
+
(async () => {
+ // let status = await docscan4nodejs.checkJob(host, jobId);
+ // console.log('Job status:', status);
+
+ // let caps = await docscan4nodejs.getScannerCapabilities(host, jobId);
+ // console.log('Capabilities:', caps);
+
+ // let updateStatus = await docscan4nodejs.updateJob(host, jobId, {
+ // status: docscan4nodejs.JobStatus.RUNNING
+ // });
+ // console.log('Update status:', updateStatus);
+ let doc = await docscan4nodejs.createDocument(host, {});
+ // console.log('Document:', doc);
+
+
let images = await docscan4nodejs.getImageFiles(host, jobId, './');
for (let i = 0; i < images.length; i++) {
console.log('Image ' + i + ': ' + images[i]);
+ let info = await docscan4nodejs.getImageInfo(host, jobId);
+ // console.log('Image info:', info);
+
+ let insertPage = await docscan4nodejs.insertPage(host, doc.uid, { password: '', source: info.url });
+ // console.log('Insert page:', insertPage);
}
- // await docscan4nodejs.deleteJob(host, jobId);
+
+
+ let docinfo = await docscan4nodejs.getDocumentInfo(host, doc.uid);
+ // console.log('Document info:', docinfo);
+
+ // let deletePage = await docscan4nodejs.deletePage(host, doc.uid, insertPage.pages[0].uid);
+ // console.log('Delete page:', deletePage);
+ let docfile = await docscan4nodejs.getDocumentFile(host, doc.uid, './');
+ console.log('Document file:', docfile);
+
+ let deleteDoc = await docscan4nodejs.deleteDocument(host, doc.uid);
+ console.log('Delete document:', deleteDoc);
+
+ await docscan4nodejs.deleteJob(host, jobId);
askQuestion();
})();
}
-
+ catch (error) {
+ console.error('Job creation failed:', error.message);
+ askQuestion();
+ }
});
}
}
diff --git a/examples/web/app.js b/examples/web/app.js
index 4be02f0..934292c 100644
--- a/examples/web/app.js
+++ b/examples/web/app.js
@@ -27,12 +27,12 @@ io.on('connection', (socket) => {
});
socket.on('message', async (message) => {
- let json = JSON.parse(message);
- if (json) {
- if (json['scan']) {
+ let data = JSON.parse(message);
+ if (data) {
+ if (data['scan']) {
let parameters = {
license: "DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==",
- device: json['scan'],
+ device: data['scan'],
};
parameters.config = {
@@ -45,34 +45,35 @@ io.on('connection', (socket) => {
IfDuplexEnabled: false,
};
- let jobId = await docscan4nodejs.scanDocument(host, parameters);
-
- if (jobId !== '') {
- console.log('job id: ' + jobId);
- let streams = await docscan4nodejs.getImageStreams(host, jobId);
- for (let i = 0; i < streams.length; i++) {
- await new Promise((resolve, reject) => {
- try {
- const passThrough = new PassThrough();
- const chunks = [];
-
- streams[i].pipe(passThrough);
-
- passThrough.on('data', (chunk) => {
- chunks.push(chunk);
- });
-
- passThrough.on('end', () => {
- const buffer = Buffer.concat(chunks);
- socket.emit('image', buffer);
- resolve();
- });
- }
- catch (error) {
- reject(error);
- }
- });
- }
+ let json = await docscan4nodejs.createJob(host, parameters);
+ let jobId = json.jobuid;
+
+ let status = await docscan4nodejs.checkJob(host, jobId);
+ console.log('Job status:', status);
+
+ let streams = await docscan4nodejs.getImageStreams(host, jobId);
+ for (let i = 0; i < streams.length; i++) {
+ await new Promise((resolve, reject) => {
+ try {
+ const passThrough = new PassThrough();
+ const chunks = [];
+
+ streams[i].pipe(passThrough);
+
+ passThrough.on('data', (chunk) => {
+ chunks.push(chunk);
+ });
+
+ passThrough.on('end', () => {
+ const buffer = Buffer.concat(chunks);
+ socket.emit('image', buffer);
+ resolve();
+ });
+ }
+ catch (error) {
+ reject(error);
+ }
+ });
}
}
}
diff --git a/index.js b/index.js
index 90d27ab..0c50d09 100644
--- a/index.js
+++ b/index.js
@@ -15,6 +15,11 @@ const ScannerType = {
WIATWAINSCANNER: 0x800
};
+const JobStatus = {
+ RUNNING: 'running',
+ CANCELED: 'canceled',
+};
+
// Unified HTTP/HTTPS request handler
function request(options) {
return new Promise((resolve, reject) => {
@@ -59,7 +64,16 @@ function request(options) {
});
});
- req.on('error', reject);
+ req.on('error', (err) => {
+ if (err.code === 'ECONNRESET') {
+ console.error('ECONNRESET!!!', {
+ url: options.url,
+ retryCount: options.retryCount || 0
+ });
+ }
+ reject(err);
+ });
+
if (options.body) {
req.write(typeof options.body === 'string'
? options.body
@@ -69,10 +83,26 @@ function request(options) {
});
}
+// Get server version information
+async function getServerInfo(host) {
+ try {
+ const response = await request({
+ url: `${host}/api/server/version`,
+ method: 'GET',
+ json: true
+ });
+ return response.data;
+ } catch (error) {
+ return {
+ version: error.message,
+ compatible: false
+ };
+ }
+}
+
// Fetch single image file and save to directory
async function getImageFile(host, jobId, directory) {
- const url = `${host}/DWTAPI/ScanJobs/${jobId}/NextDocument`;
-
+ const url = `${host}/api/device/scanners/jobs/${jobId}/next-page`;
try {
const response = await request({
url,
@@ -91,7 +121,6 @@ async function getImageFile(host, jobId, directory) {
// Handle successful write
writer.on('finish', () => {
- console.log('Saved image to', imagePath);
resolve(filename);
});
@@ -120,7 +149,7 @@ async function getImageFile(host, jobId, directory) {
// Fetch single image stream
async function getImageStream(host, jobId) {
- const url = `${host}/DWTAPI/ScanJobs/${jobId}/NextDocument`;
+ const url = `${host}/api/device/scanners/jobs/${jobId}/next-page`;
try {
const response = await request({
@@ -138,93 +167,364 @@ async function getImageStream(host, jobId) {
return null;
}
-module.exports = {
- // Get available scanning devices
- getDevices: async function (host, scannerType) {
- let url = `${host}/DWTAPI/Scanners`;
- if (scannerType != null) url += `?type=${scannerType}`;
-
- try {
- const response = await request({
- url,
- json: true
- });
+// Get available scanning devices
+async function getDevices(host, scannerType) {
+ let url = `${host}/api/device/scanners`;
+ if (scannerType != null) url += `?type=${scannerType}`;
- if (response.data.length > 0) {
- console.log('Available scanners:', response.data.length);
- return response.data;
- }
- } catch (error) {
- console.error('Device discovery failed:', error.message);
+ try {
+ const response = await request({
+ url,
+ method: 'GET',
+ json: true
+ });
+
+ if (response.data.length > 0) {
+ return response.data;
}
- return [];
- },
-
- // Create new scan job
- scanDocument: async function (host, parameters, timeout = 30) {
- const url = `${host}/DWTAPI/ScanJobs?timeout=${timeout}`;
-
- try {
- const response = await request({
- url,
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Content-Length': Buffer.byteLength(JSON.stringify(parameters))
- },
- body: parameters
- });
+ } catch (error) {
+ console.error('Device discovery failed:', error.message);
+ }
+ return [];
+}
- return response.status === 201 ? response.data : '';
- } catch (error) {
- console.error('Scan job creation failed:', error.message);
- return '';
+// Create new scan job
+async function createJob(host, parameters) {
+ const url = `${host}/api/device/scanners/jobs`;
+
+ try {
+ const response = await request({
+ url,
+ method: 'POST',
+ headers: {
+ 'X-DICS-LICENSE-KEY': parameters.license,
+ 'Content-Type': 'application/json',
+ 'Content-Length': Buffer.byteLength(JSON.stringify(parameters))
+ },
+ json: true,
+ body: parameters
+ });
+ if (response.status !== 201) {
+ console.log('Job created:', response);
}
- },
- // Delete existing scan job
- deleteJob: async function (host, jobId) {
- if (!jobId) return;
+ return response.data;
+ } catch (error) {
+ console.error('Scan job creation failed:', error.message);
+ return '';
+ }
+}
+
+// Retrive the job status
+async function checkJob(host, jobId) {
+ const url = `${host}/api/device/scanners/jobs/${jobId}`;
+
+ try {
+ const response = await request({
+ url,
+ method: 'GET',
+ json: true
+ });
+
+ return response.status === 200 ? response.data : '';
+ } catch (error) {
+ console.error('Scan job creation failed:', error.message);
+ return '';
+ }
+}
+
+// Delete existing scan job
+async function deleteJob(host, jobId) {
+ if (!jobId) return;
+
+ const url = `${host}/api/device/scanners/jobs/${jobId}`;
+ try {
+ await request({
+ url,
+ method: 'DELETE'
+ });
+ } catch (error) {
+ console.error('Job deletion failed:', error.message);
+ }
+}
+
+// Update existing scan job status (e.g. 'running', canceled)
+async function updateJob(host, jobId, parameters) {
+ const url = `${host}/api/device/scanners/jobs/${jobId}`;
+
+ try {
+ const response = await request({
+ url,
+ method: 'PATCH',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Content-Length': Buffer.byteLength(JSON.stringify(parameters))
+ },
+ json: true,
+ body: parameters
+ });
+
+ return response.status === 200 ? response.data : '';
+ } catch (error) {
+ console.error('Scan job creation failed:', error.message);
+ return '';
+ }
+}
+
+// Get scanner capabilities
+async function getScannerCapabilities(host, jobId) {
+ const url = `${host}/api/device/scanners/jobs/${jobId}/scanner/capabilities`;
+
+ try {
+ const response = await request({
+ url,
+ method: 'GET',
+ json: true
+ });
+
+ return response.status === 200 ? response.data : '';
+ }
+ catch (error) {
+ console.error('Scan job creation failed:', error.message);
+ return '';
+ }
+}
+
+// Get multiple image files
+async function getImageFiles(host, jobId, directory) {
+ const images = [];
+ console.log('Starting image download...');
+
+ while (true) {
+ const filename = await getImageFile(host, jobId, directory);
+ if (!filename) break;
+ images.push(filename);
+ }
+ return images;
+}
+
+// Get multiple image streams
+async function getImageStreams(host, jobId) {
+ const streams = [];
+
+ while (true) {
+ const stream = await getImageStream(host, jobId);
+ if (!stream) break;
+ streams.push(stream);
+ }
+ return streams;
+}
+
+// Get image information
+async function getImageInfo(host, jobId) {
+ const url = `${host}/api/device/scanners/jobs/${jobId}/next-page-info`;
+
+ try {
+ const response = await request({
+ url,
+ method: 'GET',
+ json: true
+ });
+
+ return response.status === 200 ? response.data : '';
+ } catch (error) {
+ console.error('Image info fetch failed:', error.message);
+ return '';
+ }
+}
+
+////////////////////////
+// Document functions
+
+// Create a new document
+async function createDocument(host, parameters) {
+ const url = `${host}/api/storage/documents`;
+
+ try {
+ const response = await request({
+ url,
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Content-Length': Buffer.byteLength(JSON.stringify(parameters))
+ },
+ json: true,
+ body: parameters
+ });
+ console.log('Document created:', response);
+ return response.status === 201 ? response.data : '';
+ }
+ catch (error) {
+ console.error('Document creation failed:', error.message);
+ return '';
+ }
+}
+
+// Get document information
+async function getDocumentInfo(host, docId) {
+ const url = `${host}/api/storage/documents/${docId}`;
+
+ try {
+ const response = await request({
+ url,
+ method: 'GET',
+ json: true
+ });
+
+ return response.status === 200 ? response.data : '';
+ }
+ catch (error) {
+ console.error('Document info fetch failed:', error.message);
+ return '';
+ }
+}
+
+// Delete existing document
+async function deleteDocument(host, docId) {
+ const url = `${host}/api/storage/documents/${docId}`;
+
+ try {
+ const response = await request({
+ url,
+ method: 'DELETE'
+ });
+
+ return response.status === 204 ? 'Document deleted' : '';
+ }
+ catch (error) {
+ console.error('Document deletion failed:', error.message);
+ }
+}
+
+// Fetch document file and save to directory
+async function getDocumentFile(host, docId, directory) {
+ const url = `${host}/api/storage/documents/${docId}/content`;
+
+ try {
+ const response = await request({
+ url,
+ method: 'GET',
+ stream: true
+ });
+
+ if (response.status === 200) {
+ return new Promise((resolve, reject) => {
+ const filename = `document_${Date.now()}.pdf`;
+ const imagePath = path.join(directory, filename);
+ const writer = fs.createWriteStream(imagePath);
+
+ // Pipe response stream to file
+ response.stream.pipe(writer);
+
+ // Handle successful write
+ writer.on('finish', () => {
+ resolve(filename);
+ });
+
+ // Handle errors
+ const handleError = (err) => {
+ writer.destroy();
+ reject(err);
+ };
+
+ writer.on('error', handleError);
+ response.stream.on('error', handleError);
+
+ // Handle timeout (30 seconds)
+ const timeout = setTimeout(() => {
+ handleError(new Error('Download timeout'));
+ }, 30000);
- const url = `${host}/DWTAPI/ScanJobs/${jobId}`;
- try {
- await request({
- url,
- method: 'DELETE'
+ writer.on('close', () => clearTimeout(timeout));
});
- } catch (error) {
- console.error('Job deletion failed:', error.message);
}
- },
+ }
+ catch (error) {
+ console.error('Document fetch failed:', error.message);
+ }
+}
- getImageFile,
- getImageStream,
+// Fetch document stream
+async function getDocumentStream(host, docId) {
+ const url = `${host}/api/storage/documents/${docId}/content`;
- // Get multiple image files
- getImageFiles: async function (host, jobId, directory) {
- const images = [];
- console.log('Starting image download...');
+ try {
+ const response = await request({
+ url,
+ method: 'GET',
+ stream: true
+ });
- while (true) {
- const filename = await getImageFile(host, jobId, directory);
- if (!filename) break;
- images.push(filename);
- }
- return images;
- },
-
- // Get multiple image streams
- getImageStreams: async function (host, jobId) {
- const streams = [];
- console.log('Starting stream collection...');
-
- while (true) {
- const stream = await getImageStream(host, jobId);
- if (!stream) break;
- streams.push(stream);
+ if (response.status === 200) {
+ return response.stream;
}
- return streams;
- },
+ } catch (error) {
+ console.error('Stream fetch failed:', error.message);
+ }
+ return null;
+}
+
+// Insert a new page into an existing document
+async function insertPage(host, docId, parameters) {
+ const url = `${host}/api/storage/documents/${docId}/pages`;
+
+ try {
+ const response = await request({
+ url,
+ method: 'POST',
+ headers: {
+ 'X-DICS-DOC-PASSWORD': parameters.password,
+ 'Content-Type': 'application/json',
+ 'Content-Length': Buffer.byteLength(JSON.stringify(parameters))
+ },
+ json: true,
+ body: parameters
+ });
+ return response.status === 200 ? response.data : '';
+ }
+ catch (error) {
+ console.error('Page insertion failed:', error.message);
+ return '';
+ }
+}
- ScannerType
+// Delete an existing page from a document
+async function deletePage(host, docId, pageId) {
+ const url = `${host}/api/storage/documents/${docId}/pages/${pageId}`;
+
+ try {
+ const response = await request({
+ url,
+ method: 'DELETE'
+ });
+
+ return response.status === 204 ? 'Page deleted' : '';
+ }
+ catch (error) {
+ console.error('Page deletion failed:', error.message);
+ }
+}
+
+module.exports = {
+ ScannerType,
+ JobStatus,
+ getServerInfo,
+ getDevices,
+ createJob,
+ deleteJob,
+ updateJob,
+ checkJob,
+ getImageFile,
+ getImageStream,
+ getImageFiles,
+ getImageStreams,
+ getScannerCapabilities,
+ getImageInfo,
+ createDocument,
+ getDocumentInfo,
+ deleteDocument,
+ getDocumentFile,
+ getDocumentStream,
+ insertPage,
+ deletePage
};
\ No newline at end of file