Spaces:
Running
Running
Update image-processing-logic.js
Browse files- image-processing-logic.js +35 -10
image-processing-logic.js
CHANGED
|
@@ -1,12 +1,24 @@
|
|
| 1 |
const sharp = require('sharp');
|
| 2 |
const stream = require('stream');
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
class ImageProcessor {
|
| 5 |
/**
|
| 6 |
* Processes an image to CMYK TIFF format from a readable stream.
|
|
|
|
|
|
|
|
|
|
| 7 |
* @param {stream.Readable} input - The incoming stream of image data.
|
| 8 |
-
* @param {Object
|
| 9 |
-
* @returns {stream.Readable} - A readable stream of the processed CMYK TIFF image.
|
| 10 |
*/
|
| 11 |
processImage(input, options = {}) {
|
| 12 |
return new Promise((resolve, reject) => {
|
|
@@ -24,15 +36,28 @@ class ImageProcessor {
|
|
| 24 |
sharpInstance.on('error', (error) => {
|
| 25 |
reject(new Error(`Image processing failed: ${error.message}`));
|
| 26 |
});
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
sharpInstance = sharpInstance
|
| 30 |
.toColorspace('cmyk')
|
| 31 |
.tiff({
|
| 32 |
-
compression: 'deflate',
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
});
|
| 37 |
|
| 38 |
// Create transform stream for error handling
|
|
@@ -70,7 +95,6 @@ const compressionLevel = Math.max(1, Math.min(9, Math.round(parseInt(quality) /
|
|
| 70 |
validateOptions(options) {
|
| 71 |
const { quality } = options;
|
| 72 |
|
| 73 |
-
// Validate quality exists and is valid
|
| 74 |
if (quality && isNaN(quality)) {
|
| 75 |
throw new Error('Quality must be a number');
|
| 76 |
}
|
|
@@ -85,7 +109,8 @@ const compressionLevel = Math.max(1, Math.min(9, Math.round(parseInt(quality) /
|
|
| 85 |
}
|
| 86 |
|
| 87 |
/**
|
| 88 |
-
* Validates the input content type
|
|
|
|
| 89 |
* @param {string} contentType - Request content type
|
| 90 |
*/
|
| 91 |
validateContentType(contentType) {
|
|
|
|
| 1 |
const sharp = require('sharp');
|
| 2 |
const stream = require('stream');
|
| 3 |
|
| 4 |
+
/**
|
| 5 |
+
* PRODUCTION OPTIMIZATION:
|
| 6 |
+
* Force sharp to use 1 thread per image operation.
|
| 7 |
+
* This is the recommended setting for web servers handling many concurrent requests.
|
| 8 |
+
* It prevents thread starvation and memory fragmentation by letting Node's libuv
|
| 9 |
+
* thread pool (UV_THREADPOOL_SIZE) handle concurrency at the request level instead.
|
| 10 |
+
*/
|
| 11 |
+
sharp.concurrency(1);
|
| 12 |
+
|
| 13 |
class ImageProcessor {
|
| 14 |
/**
|
| 15 |
* Processes an image to CMYK TIFF format from a readable stream.
|
| 16 |
+
* Accepts PNG, JPEG, WebP, AVIF, and TIFF inputs.
|
| 17 |
+
* Outputs the highest-quality CMYK TIFF suitable for professional printing.
|
| 18 |
+
*
|
| 19 |
* @param {stream.Readable} input - The incoming stream of image data.
|
| 20 |
+
* @param {Object} options - Processing options including quality.
|
| 21 |
+
* @returns {Promise<stream.Readable>} - A readable stream of the processed CMYK TIFF image.
|
| 22 |
*/
|
| 23 |
processImage(input, options = {}) {
|
| 24 |
return new Promise((resolve, reject) => {
|
|
|
|
| 36 |
sharpInstance.on('error', (error) => {
|
| 37 |
reject(new Error(`Image processing failed: ${error.message}`));
|
| 38 |
});
|
| 39 |
+
|
| 40 |
+
const compressionLevel = Math.max(1, Math.min(9, Math.round(parseInt(quality) / 100 * 9)));
|
| 41 |
+
|
| 42 |
+
/**
|
| 43 |
+
* CMYK conversion pipeline:
|
| 44 |
+
* - toColorspace('cmyk'): Converts from any input colorspace to CMYK.
|
| 45 |
+
* - compression: 'deflate' provides lossless PNG-style compression.
|
| 46 |
+
* - quality: 100 ensures no lossy degradation.
|
| 47 |
+
* - predictor: 'horizontal' improves deflate compression ratios on photographic data.
|
| 48 |
+
*
|
| 49 |
+
* NOTE: The input can be WebP (lossy or lossless). Sharp's libvips will decode
|
| 50 |
+
* WebP to full uncompressed pixel data before conversion, so there is ZERO quality
|
| 51 |
+
* loss from the transport format — the TIFF output quality depends only on the
|
| 52 |
+
* original render resolution (600 DPI), not the transport encoding.
|
| 53 |
+
*/
|
| 54 |
sharpInstance = sharpInstance
|
| 55 |
.toColorspace('cmyk')
|
| 56 |
.tiff({
|
| 57 |
+
compression: 'deflate',
|
| 58 |
+
quality: 100,
|
| 59 |
+
level: compressionLevel,
|
| 60 |
+
predictor: 'horizontal'
|
| 61 |
});
|
| 62 |
|
| 63 |
// Create transform stream for error handling
|
|
|
|
| 95 |
validateOptions(options) {
|
| 96 |
const { quality } = options;
|
| 97 |
|
|
|
|
| 98 |
if (quality && isNaN(quality)) {
|
| 99 |
throw new Error('Quality must be a number');
|
| 100 |
}
|
|
|
|
| 109 |
}
|
| 110 |
|
| 111 |
/**
|
| 112 |
+
* Validates the input content type.
|
| 113 |
+
* Now accepts image/webp for optimized frontend transfers.
|
| 114 |
* @param {string} contentType - Request content type
|
| 115 |
*/
|
| 116 |
validateContentType(contentType) {
|