Nest format-specific constructor params (deprecate at top-level)

- `subifd` -> `tiff.subifd`
- `level` -> `openSlide.level`
- `pdfBackground` -> `pdf.background`
This commit is contained in:
Lovell Fuller 2025-06-16 07:51:36 +01:00
parent 8c53d499f7
commit f92540f134
8 changed files with 163 additions and 69 deletions

View File

@ -44,10 +44,6 @@ where the overall height is the `pageHeight` multiplied by the number of `pages`
| [options.ignoreIcc] | <code>number</code> | <code>false</code> | should the embedded ICC profile, if any, be ignored. | | [options.ignoreIcc] | <code>number</code> | <code>false</code> | should the embedded ICC profile, if any, be ignored. |
| [options.pages] | <code>number</code> | <code>1</code> | Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages. | | [options.pages] | <code>number</code> | <code>1</code> | Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages. |
| [options.page] | <code>number</code> | <code>0</code> | Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based. | | [options.page] | <code>number</code> | <code>0</code> | Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based. |
| [options.subifd] | <code>number</code> | <code>-1</code> | subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. |
| [options.level] | <code>number</code> | <code>0</code> | level to extract from a multi-level input (OpenSlide), zero based. |
| [options.pdfBackground] | <code>string</code> \| <code>Object</code> | | Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. |
| [options.jp2Oneshot] | <code>boolean</code> | <code>false</code> | Set to `true` to decode tiled JPEG 2000 images in a single operation, improving compatibility. |
| [options.animated] | <code>boolean</code> | <code>false</code> | Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`. | | [options.animated] | <code>boolean</code> | <code>false</code> | Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`. |
| [options.raw] | <code>Object</code> | | describes raw pixel input image data. See `raw()` for pixel ordering. | | [options.raw] | <code>Object</code> | | describes raw pixel input image data. See `raw()` for pixel ordering. |
| [options.raw.width] | <code>number</code> | | integral number of pixels wide. | | [options.raw.width] | <code>number</code> | | integral number of pixels wide. |
@ -82,6 +78,14 @@ where the overall height is the `pageHeight` multiplied by the number of `pages`
| [options.join.background] | <code>string</code> \| <code>Object</code> | | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. | | [options.join.background] | <code>string</code> \| <code>Object</code> | | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
| [options.join.halign] | <code>string</code> | <code>&quot;&#x27;left&#x27;&quot;</code> | horizontal alignment style for images joined horizontally (`'left'`, `'centre'`, `'center'`, `'right'`). | | [options.join.halign] | <code>string</code> | <code>&quot;&#x27;left&#x27;&quot;</code> | horizontal alignment style for images joined horizontally (`'left'`, `'centre'`, `'center'`, `'right'`). |
| [options.join.valign] | <code>string</code> | <code>&quot;&#x27;top&#x27;&quot;</code> | vertical alignment style for images joined vertically (`'top'`, `'centre'`, `'center'`, `'bottom'`). | | [options.join.valign] | <code>string</code> | <code>&quot;&#x27;top&#x27;&quot;</code> | vertical alignment style for images joined vertically (`'top'`, `'centre'`, `'center'`, `'bottom'`). |
| [options.tiff] | <code>Object</code> | | Describes TIFF specific options. |
| [options.tiff.subifd] | <code>number</code> | <code>-1</code> | Sub Image File Directory to extract for OME-TIFF, defaults to main image. |
| [options.pdf] | <code>Object</code> | | Describes PDF specific options. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. |
| [options.pdf.background] | <code>string</code> \| <code>Object</code> | | Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
| [options.openSlide] | <code>Object</code> | | Describes OpenSlide specific options. Requires the use of a globally-installed libvips compiled with support for OpenSlide. |
| [options.openSlide.level] | <code>number</code> | <code>0</code> | Level to extract from a multi-level input, zero based. |
| [options.jp2] | <code>Object</code> | | Describes JPEG 2000 specific options. Requires the use of a globally-installed libvips compiled with support for OpenJPEG. |
| [options.jp2.oneshot] | <code>boolean</code> | <code>false</code> | Set to `true` to decode tiled JPEG 2000 images in a single operation, improving compatibility. |
**Example** **Example**
```js ```js

View File

@ -12,6 +12,8 @@ Requires libvips v8.17.0
* Add "Magic Kernel Sharp" (no relation) to resizing kernels. * Add "Magic Kernel Sharp" (no relation) to resizing kernels.
* Deprecate top-level, format-specific constructor parameters, e.g. `subifd` becomes `tiff.subifd`.
* Expose `keepDuplicateFrames` GIF output parameter. * Expose `keepDuplicateFrames` GIF output parameter.
* Expose JPEG 2000 `oneshot` decoder option. * Expose JPEG 2000 `oneshot` decoder option.

View File

@ -153,10 +153,6 @@ const debuglog = util.debuglog('sharp');
* @param {number} [options.ignoreIcc=false] - should the embedded ICC profile, if any, be ignored. * @param {number} [options.ignoreIcc=false] - should the embedded ICC profile, if any, be ignored.
* @param {number} [options.pages=1] - Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages. * @param {number} [options.pages=1] - Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages.
* @param {number} [options.page=0] - Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based. * @param {number} [options.page=0] - Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based.
* @param {number} [options.subifd=-1] - subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image.
* @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
* @param {string|Object} [options.pdfBackground] - Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick.
* @param {boolean} [options.jp2Oneshot=false] - Set to `true` to decode tiled JPEG 2000 images in a single operation, improving compatibility.
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`. * @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`.
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering. * @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
* @param {number} [options.raw.width] - integral number of pixels wide. * @param {number} [options.raw.width] - integral number of pixels wide.
@ -192,7 +188,14 @@ const debuglog = util.debuglog('sharp');
* @param {string|Object} [options.join.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. * @param {string|Object} [options.join.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @param {string} [options.join.halign='left'] - horizontal alignment style for images joined horizontally (`'left'`, `'centre'`, `'center'`, `'right'`). * @param {string} [options.join.halign='left'] - horizontal alignment style for images joined horizontally (`'left'`, `'centre'`, `'center'`, `'right'`).
* @param {string} [options.join.valign='top'] - vertical alignment style for images joined vertically (`'top'`, `'centre'`, `'center'`, `'bottom'`). * @param {string} [options.join.valign='top'] - vertical alignment style for images joined vertically (`'top'`, `'centre'`, `'center'`, `'bottom'`).
* * @param {Object} [options.tiff] - Describes TIFF specific options.
* @param {number} [options.tiff.subifd=-1] - Sub Image File Directory to extract for OME-TIFF, defaults to main image.
* @param {Object} [options.pdf] - Describes PDF specific options. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick.
* @param {string|Object} [options.pdf.background] - Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @param {Object} [options.openSlide] - Describes OpenSlide specific options. Requires the use of a globally-installed libvips compiled with support for OpenSlide.
* @param {number} [options.openSlide.level=0] - Level to extract from a multi-level input, zero based.
* @param {Object} [options.jp2] - Describes JPEG 2000 specific options. Requires the use of a globally-installed libvips compiled with support for OpenJPEG.
* @param {boolean} [options.jp2.oneshot=false] - Set to `true` to decode tiled JPEG 2000 images in a single operation, improving compatibility.
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */

38
lib/index.d.ts vendored
View File

@ -1003,14 +1003,20 @@ declare namespace sharp {
pages?: number | undefined; pages?: number | undefined;
/** Page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based. (optional, default 0) */ /** Page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based. (optional, default 0) */
page?: number | undefined; page?: number | undefined;
/** subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. (optional, default -1) */ /** TIFF specific input options */
tiff?: TiffInputOptions | undefined;
/** PDF specific input options */
pdf?: PdfInputOptions | undefined;
/** OpenSlide specific input options */
openSlide?: OpenSlideInputOptions | undefined;
/** JPEG 2000 specific input options */
jp2?: Jp2InputOptions | undefined;
/** Deprecated: use tiff.subifd instead */
subifd?: number | undefined; subifd?: number | undefined;
/** Level to extract from a multi-level input (OpenSlide), zero based. (optional, default 0) */ /** Deprecated: use pdf.background instead */
level?: number | undefined;
/** Background colour to use when PDF is partially transparent. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. */
pdfBackground?: Colour | Color | undefined; pdfBackground?: Colour | Color | undefined;
/** Set to `true` to load JPEG 2000 images using [oneshot mode](https://github.com/libvips/libvips/issues/4205) */ /** Deprecated: use openSlide.level instead */
jp2Oneshot?: boolean | undefined; level?: number | undefined;
/** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default false) */ /** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default false) */
animated?: boolean | undefined; animated?: boolean | undefined;
/** Describes raw pixel input image data. See raw() for pixel ordering. */ /** Describes raw pixel input image data. See raw() for pixel ordering. */
@ -1116,6 +1122,26 @@ declare namespace sharp {
valign?: VerticalAlignment | undefined; valign?: VerticalAlignment | undefined;
} }
interface TiffInputOptions {
/** Sub Image File Directory to extract, defaults to main image. Use -1 for all subifds. */
subifd?: number | undefined;
}
interface PdfInputOptions {
/** Background colour to use when PDF is partially transparent. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. */
background?: Colour | Color | undefined;
}
interface OpenSlideInputOptions {
/** Level to extract from a multi-level input, zero based. (optional, default 0) */
level?: number | undefined;
}
interface Jp2InputOptions {
/** Set to `true` to load JPEG 2000 images using [oneshot mode](https://github.com/libvips/libvips/issues/4205) */
oneshot?: boolean | undefined;
}
interface ExifDir { interface ExifDir {
[k: string]: string; [k: string]: string;
} }

View File

@ -230,32 +230,49 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
throw is.invalidParameterError('page', 'integer between 0 and 100000', inputOptions.page); throw is.invalidParameterError('page', 'integer between 0 and 100000', inputOptions.page);
} }
} }
// Multi-level input (OpenSlide) // OpenSlide specific options
if (is.defined(inputOptions.level)) { if (is.object(inputOptions.openSlide) && is.defined(inputOptions.openSlide.level)) {
if (is.integer(inputOptions.openSlide.level) && is.inRange(inputOptions.openSlide.level, 0, 256)) {
inputDescriptor.level = inputOptions.openSlide.level;
} else {
throw is.invalidParameterError('openSlide.level', 'integer between 0 and 256', inputOptions.openSlide.level);
}
} else if (is.defined(inputOptions.level)) {
// Deprecated
if (is.integer(inputOptions.level) && is.inRange(inputOptions.level, 0, 256)) { if (is.integer(inputOptions.level) && is.inRange(inputOptions.level, 0, 256)) {
inputDescriptor.level = inputOptions.level; inputDescriptor.level = inputOptions.level;
} else { } else {
throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level); throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
} }
} }
// Sub Image File Directory (TIFF) // TIFF specific options
if (is.defined(inputOptions.subifd)) { if (is.object(inputOptions.tiff) && is.defined(inputOptions.tiff.subifd)) {
if (is.integer(inputOptions.tiff.subifd) && is.inRange(inputOptions.tiff.subifd, -1, 100000)) {
inputDescriptor.subifd = inputOptions.tiff.subifd;
} else {
throw is.invalidParameterError('tiff.subifd', 'integer between -1 and 100000', inputOptions.tiff.subifd);
}
} else if (is.defined(inputOptions.subifd)) {
// Deprecated
if (is.integer(inputOptions.subifd) && is.inRange(inputOptions.subifd, -1, 100000)) { if (is.integer(inputOptions.subifd) && is.inRange(inputOptions.subifd, -1, 100000)) {
inputDescriptor.subifd = inputOptions.subifd; inputDescriptor.subifd = inputOptions.subifd;
} else { } else {
throw is.invalidParameterError('subifd', 'integer between -1 and 100000', inputOptions.subifd); throw is.invalidParameterError('subifd', 'integer between -1 and 100000', inputOptions.subifd);
} }
} }
// PDF background colour // PDF specific options
if (is.defined(inputOptions.pdfBackground)) { if (is.object(inputOptions.pdf) && is.defined(inputOptions.pdf.background)) {
inputDescriptor.pdfBackground = this._getBackgroundColourOption(inputOptions.pdf.background);
} else if (is.defined(inputOptions.pdfBackground)) {
// Deprecated
inputDescriptor.pdfBackground = this._getBackgroundColourOption(inputOptions.pdfBackground); inputDescriptor.pdfBackground = this._getBackgroundColourOption(inputOptions.pdfBackground);
} }
// JP2 oneshot // JPEG 2000 specific options
if (is.defined(inputOptions.jp2Oneshot)) { if (is.object(inputOptions.jp2) && is.defined(inputOptions.jp2.oneshot)) {
if (is.bool(inputOptions.jp2Oneshot)) { if (is.bool(inputOptions.jp2.oneshot)) {
inputDescriptor.jp2Oneshot = inputOptions.jp2Oneshot; inputDescriptor.jp2Oneshot = inputOptions.jp2.oneshot;
} else { } else {
throw is.invalidParameterError('jp2Oneshot', 'boolean', inputOptions.jp2Oneshot); throw is.invalidParameterError('jp2.oneshot', 'boolean', inputOptions.jp2.oneshot);
} }
} }
// Create new image // Create new image

View File

@ -435,9 +435,6 @@ sharp('input.jpg').clahe({ width: 10, height: 10, maxSlope: 5 }).toFile('outfile
// Support `unlimited` input option // Support `unlimited` input option
sharp('input.png', { unlimited: true }).resize(320, 240).toFile('outfile.png'); sharp('input.png', { unlimited: true }).resize(320, 240).toFile('outfile.png');
// Support `subifd` input option for tiffs
sharp('input.tiff', { subifd: 3 }).resize(320, 240).toFile('outfile.png');
// Support creating with noise // Support creating with noise
sharp({ sharp({
create: { create: {
@ -720,13 +717,19 @@ sharp(input).composite([
} }
]) ])
// Support format-specific input options
const colour: sharp.Colour = '#fff'; const colour: sharp.Colour = '#fff';
const color: sharp.Color = '#fff'; const color: sharp.Color = '#fff';
sharp({ pdfBackground: colour }); sharp({ pdf: { background: colour } });
sharp({ pdfBackground: color }); sharp({ pdf: { background: color } });
sharp({ pdfBackground: colour }); // Deprecated
sharp({ jp2Oneshot: true }); sharp({ pdfBackground: color }); // Deprecated
sharp({ jp2Oneshot: false }); sharp({ tiff: { subifd: 3 } });
sharp({ subifd: 3 }); // Deprecated
sharp({ openSlide: { level: 0 } });
sharp({ level: 0 }); // Deprecated
sharp({ jp2: { oneshot: true } });
sharp({ jp2: { oneshot: false } });
sharp({ autoOrient: true }); sharp({ autoOrient: true });
sharp({ autoOrient: false }); sharp({ autoOrient: false });

View File

@ -867,52 +867,91 @@ describe('Input/output', function () {
sharp({ pages: '1' }); sharp({ pages: '1' });
}, /Expected integer between -1 and 100000 for pages but received 1 of type string/); }, /Expected integer between -1 and 100000 for pages but received 1 of type string/);
}); });
it('Valid level property', function () { it('Valid openSlide.level property', function () {
sharp({ openSlide: { level: 1 } });
sharp({ level: 1 }); sharp({ level: 1 });
}); });
it('Invalid level property (string) throws', function () { it('Invalid openSlide.level property (string) throws', function () {
assert.throws(function () { assert.throws(
sharp({ level: '1' }); () => sharp({ openSlide: { level: '1' } }),
}, /Expected integer between 0 and 256 for level but received 1 of type string/); /Expected integer between 0 and 256 for openSlide.level but received 1 of type string/
);
assert.throws(
() => sharp({ level: '1' }),
/Expected integer between 0 and 256 for level but received 1 of type string/
);
}); });
it('Invalid level property (negative) throws', function () { it('Invalid openSlide.level property (negative) throws', function () {
assert.throws(function () { assert.throws(
sharp({ level: -1 }); () => sharp({ openSlide: { level: -1 } }),
}, /Expected integer between 0 and 256 for level but received -1 of type number/); /Expected integer between 0 and 256 for openSlide\.level but received -1 of type number/
);
assert.throws(
() => sharp({ level: -1 }),
/Expected integer between 0 and 256 for level but received -1 of type number/
);
}); });
it('Valid subifd property', function () { it('Valid tiff.subifd property', function () {
sharp({ tiff: { subifd: 1 } });
sharp({ subifd: 1 }); sharp({ subifd: 1 });
}); });
it('Invalid subifd property (string) throws', function () { it('Invalid tiff.subifd property (string) throws', function () {
assert.throws(function () { assert.throws(
sharp({ subifd: '1' }); () => sharp({ tiff: { subifd: '1' } }),
}, /Expected integer between -1 and 100000 for subifd but received 1 of type string/); /Expected integer between -1 and 100000 for tiff\.subifd but received 1 of type string/
);
assert.throws(
() => sharp({ subifd: '1' }),
/Expected integer between -1 and 100000 for subifd but received 1 of type string/
);
}); });
it('Invalid subifd property (float) throws', function () { it('Invalid tiff.subifd property (float) throws', function () {
assert.throws(function () { assert.throws(
sharp({ subifd: 1.2 }); () => sharp({ tiff: { subifd: 1.2 } }),
}, /Expected integer between -1 and 100000 for subifd but received 1.2 of type number/); /Expected integer between -1 and 100000 for tiff\.subifd but received 1.2 of type number/
);
assert.throws(
() => sharp({ subifd: 1.2 }),
/Expected integer between -1 and 100000 for subifd but received 1.2 of type number/
);
}); });
it('Valid pdfBackground property (string)', function () { it('Valid pdf.background property (string)', function () {
sharp({ pdf: { background: '#00ff00' } });
sharp({ pdfBackground: '#00ff00' }); sharp({ pdfBackground: '#00ff00' });
}); });
it('Valid pdfBackground property (object)', function () { it('Valid pdf.background property (object)', function () {
sharp({ pdf: { background: { r: 0, g: 255, b: 0 } } });
sharp({ pdfBackground: { r: 0, g: 255, b: 0 } }); sharp({ pdfBackground: { r: 0, g: 255, b: 0 } });
}); });
it('Invalid pdfBackground property (string) throws', function () { it('Invalid pdf.background property (string) throws', function () {
assert.throws(function () { assert.throws(
sharp({ pdfBackground: '00ff00' }); () => sharp({ pdf: { background: '00ff00' } }),
}, /Unable to parse color from string/); /Unable to parse color from string/
);
assert.throws(
() => sharp({ pdfBackground: '00ff00' }),
/Unable to parse color from string/
);
}); });
it('Invalid pdfBackground property (number) throws', function () { it('Invalid pdf.background property (number) throws', function () {
assert.throws(function () { assert.throws(
sharp({ pdfBackground: 255 }); () => sharp({ pdf: { background: 255 } }),
}, /Expected object or string for background/); /Expected object or string for background/
);
assert.throws(
() => sharp({ pdf: { background: 255 } }),
/Expected object or string for background/
);
}); });
it('Invalid pdfBackground property (object)', function () { it('Invalid pdf.background property (object)', function () {
assert.throws(function () { assert.throws(
sharp({ pdfBackground: { red: 0, green: 255, blue: 0 } }); () => sharp({ pdf: { background: { red: 0, green: 255, blue: 0 } } }),
}, /Unable to parse color from object/); /Unable to parse color from object/
);
assert.throws(
() => sharp({ pdfBackground: { red: 0, green: 255, blue: 0 } }),
/Unable to parse color from object/
);
}); });
}); });

View File

@ -117,14 +117,14 @@ describe('JP2 output', () => {
it('valid JP2 oneshot value does not throw error', () => { it('valid JP2 oneshot value does not throw error', () => {
assert.doesNotThrow( assert.doesNotThrow(
() => sharp(fixtures.inputJp2TileParts, { jp2Oneshot: true }) () => sharp({ jp2: { oneshot: true } })
); );
}); });
it('invalid JP2 oneshot value throws error', () => { it('invalid JP2 oneshot value throws error', () => {
assert.throws( assert.throws(
() => sharp(fixtures.inputJp2TileParts, { jp2Oneshot: 'fail' }), () => sharp({ jp2: { oneshot: 'fail' } }),
/Expected boolean for jp2Oneshot but received fail of type string/ /Expected boolean for jp2.oneshot but received fail of type string/
); );
}); });
}); });