forked from lovell/sharp
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
setup a conversion script to debug sharp source code and fix issue lo…
- Loading branch information
1 parent
5b1f69d
commit d115cc4
Showing
5 changed files
with
142 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,85 @@ | ||
const sharp = require('../lib'); | ||
const icc = require('icc'); | ||
const { resolve: pathResolve } = require('path'); | ||
const assert = require('assert'); | ||
const spawnChild = require('./utils/spawnChild'); | ||
|
||
// Define input and output files | ||
const inFile = pathResolve(__dirname, 'files/sticker.jpg'); | ||
const outFile = pathResolve(__dirname, 'files/sticker-OUT.jpg'); | ||
const inFile = pathResolve(__dirname, 'files/100-0-0-100-fogra.tif'); | ||
const swop = pathResolve(__dirname, 'files/swop.icc'); | ||
|
||
(async function main () { | ||
console.log('HALLO1', inFile); | ||
// Helper to get a pixel value (uses `vips`, install with `brew install vips`) | ||
async function getPixelValues(fileName, x, y) { | ||
const data = await spawnChild( | ||
'vips', | ||
[ | ||
`getpoint`, | ||
`${fileName}`, | ||
`${x}`, // pixel x | ||
`${y}`, // pixel y | ||
], | ||
); | ||
|
||
// Remove linebreaks and trim spaces from output and return (output is a string, e.g. '255 128 0 0`) | ||
return data.replace(/\n/g, '').trim(); | ||
} | ||
|
||
// Get ICC profile from inFile | ||
const metaInFile = await sharp(inFile).metadata(); | ||
assert.ok(metaInFile.icc instanceof Buffer, 'icc not set in inFile'); | ||
const { description: iccDescrInFile } = icc.parse(metaInFile.icc); | ||
// Helper to validate if a pixel equals to specific values (uses `vips`, install with `brew install vips`) | ||
async function validatePixelValues(fileName, expectedPixelValues) { | ||
for (let i = 0; i < 10; i += 1) { | ||
const pixelValues = await getPixelValues(fileName, i, i); | ||
assert.strictEqual(pixelValues, expectedPixelValues); | ||
} | ||
} | ||
|
||
// Convert, keep ICC from inFile | ||
// Main function | ||
(async function main () { | ||
// ############################################################################## | ||
// Invert without color space change (WORKING) | ||
// ############################################################################## | ||
// Source file: 1000x1000 file with CMYK colors 0/100/100/0 in Coated Fogra39 | ||
await validatePixelValues(inFile, '255 0 0 255'); | ||
const outFileFogra = pathResolve(__dirname, 'files/0-100-100-0-fogra.tif'); | ||
await sharp(inFile) | ||
.pipelineColourspace('cmyk') | ||
.toColourspace('cmyk') | ||
.pipelineColourspace('cmyk') | ||
.keepIccProfile() | ||
.toFile(outFile); | ||
.negate() | ||
.toFile(outFileFogra); | ||
await validatePixelValues(outFileFogra, '0 255 255 0'); | ||
// Output file: 1000x1000 file with CMYK colors 100/0/0/100 (inverted) in Coated Fogra39 | ||
|
||
// Extract metadata from outFile, assert icc profile is present | ||
const metaOutFile = await sharp(outFile).metadata(); | ||
assert.ok(metaOutFile.icc instanceof Buffer, 'icc not set in outFile'); | ||
// ############################################################################## | ||
// Invert with color space change in 2 steps (WORKING) | ||
// ############################################################################## | ||
const outFileSwop2 = pathResolve(__dirname, 'files/218-187-153-197-swop.tif'); | ||
await sharp(inFile) | ||
.toColourspace('cmyk') | ||
.pipelineColourspace('cmyk') | ||
.withIccProfile(swop) | ||
.toFile(outFileSwop2); | ||
await validatePixelValues(outFileSwop2, '218 187 153 197'); | ||
// Output file: 1000x1000 file with CMYK colors 218/187/153/197 in U.S. Web Coated Swop V2 | ||
|
||
// Check if description of ICC profile in output file matches description of ICC profile in input file | ||
const { description: iccDescrOutFile } = icc.parse(metaOutFile.icc); | ||
assert.strictEqual(iccDescrOutFile, iccDescrInFile); | ||
const outFileSwopInverted2Steps = pathResolve(__dirname, 'files/37-68-102-58-swop-inverted-2step.tif'); | ||
await sharp(outFileSwop2) | ||
.toColourspace('cmyk') | ||
.pipelineColourspace('cmyk') | ||
.keepIccProfile() | ||
.negate() | ||
.toFile(outFileSwopInverted2Steps); | ||
await validatePixelValues(outFileSwopInverted2Steps, `${255 - 218} ${255 - 187} ${255 - 153} ${255 - 197}`); | ||
// Output file: 1000x1000 file with CMYK colors 37/68/102/58 in U.S. Web Coated Swop V2, so the values are inverted (resulting value = 255 - source value) | ||
|
||
console.log('HALLO3', outFile); | ||
// ############################################################################## | ||
// Invert with color space change in 1 step (NOT WORKING) | ||
// ############################################################################## | ||
const outFileSwopInverted1Step = pathResolve(__dirname, 'files/37-68-102-58-swop-inverted-1step.tif'); | ||
await sharp(inFile) | ||
.toColourspace('cmyk') | ||
.pipelineColourspace('cmyk') | ||
.withIccProfile(swop) | ||
.negate() | ||
.toFile(outFileSwopInverted1Step); | ||
await validatePixelValues(outFileSwopInverted1Step, `${255 - 218} ${255 - 187} ${255 - 153} ${255 - 197}`); | ||
// Output file: 1000x1000 file with CMYK colors 37/68/102/58 in U.S. Web Coated Swop V2, so the values are inverted (resulting value = 255 - source value) | ||
})(); |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
const { spawn } = require('child_process'); | ||
|
||
/** | ||
* Spawns a child process to run a program with the specified arguments and environment variables. | ||
* | ||
* This asynchronous function uses Node.js's `spawn` from the `child_process` module to create a new child process. | ||
* It optionally logs standard output and standard error streams based on the provided options. | ||
* | ||
* @param {string} programPath - The path to the program to execute. | ||
* @param {Array<string>} args - Arguments to pass to the program. | ||
* @param {Object} options - Options for the `spawn` command. Can include a key `env` to set environment variables | ||
* @param {Object} opts - Options for logging standard output and error. Defaults to not logging either. | ||
* @param {boolean} opts.logStdOut - Flag to log standard output. Default is false. | ||
* @param {boolean} opts.logStdErr - Flag to log standard error. Default is false. | ||
* | ||
* @returns {Promise<string>} A promise that resolves with the standard output data from the child process | ||
* or rejects with an error message if the child process exits with a non-zero exit code. | ||
* | ||
* @throws {Error} Throws an error with the standard error output if the child process exits with a non-zero exit code. | ||
* | ||
* @example | ||
* // Example usage: | ||
* spawnChild('path/to/program', ['arg1', 'arg2'], { ENV_VAR: 'value' }, { logStdOut: true, logStdErr: true }) | ||
* .then(output => console.log(output)) | ||
* .catch(error => console.error(error)); | ||
*/ | ||
module.exports = async function spawnChild(programPath, args, options = {}, opts = { | ||
logStdOut: false, | ||
logStdErr: false, | ||
}) { | ||
// Spawn child | ||
const child = spawn( | ||
programPath, | ||
args, | ||
{ | ||
...options, | ||
env: { | ||
...process.env, | ||
...options.env, | ||
}, | ||
}, | ||
); | ||
|
||
// Gather data | ||
let data = ''; | ||
for await (const chunk of child.stdout) { | ||
if (opts.logStdOut) console.log(chunk.toString()); // eslint-disable-line no-console | ||
data += chunk; | ||
} | ||
|
||
// Gather errors | ||
let error = ''; | ||
for await (const chunk of child.stderr) { | ||
if (opts.logStdErr) console.log(chunk.toString()); // eslint-disable-line no-console | ||
error += chunk; | ||
} | ||
|
||
// Await for the program to exist | ||
const exitCode = await new Promise((resolve) => { | ||
child.on('close', resolve); | ||
}); | ||
|
||
// Reject the promise if exit code > 0 (an error occurred) | ||
if (exitCode) { | ||
if (opts.logStdErr) console.log(`subprocess error exit ${exitCode}, ${error}`); // eslint-disable-line no-console | ||
throw new Error(error); | ||
} | ||
|
||
// Return the data | ||
return data; | ||
} |