Compare commits

...

18 Commits

Author SHA1 Message Date
Lovell Fuller
7c1551bdc9 Tests: expect ranges for flaky text test 2023-11-04 17:22:08 +00:00
Lovell Fuller
bdcf02695f Prerelease v0.33.0-alpha.10 2023-11-04 15:54:19 +00:00
Lovell Fuller
d206fd5392 Tests: allow range for text image dimensions 2023-11-04 14:20:53 +00:00
Lovell Fuller
0bd1715f36 Options for trim op must be an Object, add lineArt #2363 2023-11-04 14:09:50 +00:00
Lovell Fuller
2e7c60675b Upgrade to libvips 8.15.0-rc2 2023-11-04 11:36:18 +00:00
Lovell Fuller
7caaa8e22b Tests: add debug to flaky text test 2023-11-03 11:42:57 +00:00
Lovell Fuller
839c92bf3d Docs: changelog and credit for #3812 2023-11-03 11:30:42 +00:00
Dennis Beatty
28aa176957 Add support for miniswhite when using TIFF output 2023-11-03 11:27:49 +00:00
Lovell Fuller
0f24f0f214 Add infrastructure (CI, npm) for sharp-linux-s390x package 2023-11-02 14:41:59 +00:00
Lovell Fuller
655da113c8 Ensure multi-page extract remains sequential #3837 2023-11-01 13:54:33 +00:00
Lovell Fuller
159b986cdd Tests: remove use of SVG fixture from median tests 2023-10-31 21:32:33 +00:00
Lovell Fuller
069d1786f5 The value of library_dirs is now common for all platforms 2023-10-20 11:15:23 +01:00
Lovell Fuller
f43829a16a Use Call semantics for C++ to JS callbacks
Reverts 4ec883e as no longer required
2023-10-19 16:14:21 +01:00
Lovell Fuller
65e61ad001 Tests: ensure SVG is well-formed, detect lack of pango 2023-10-19 16:08:34 +01:00
Lovell Fuller
0107a4de81 Docs: compatible with Node-API v9 runtimes 2023-10-14 15:46:02 +01:00
Lovell Fuller
337ce7b1c2 Docs: changelog entry for #3823 2023-10-14 09:19:53 +01:00
Lovell Fuller
3719f79065 Docs: simply language around failOn options 2023-10-14 09:19:28 +01:00
Lovell Fuller
8d1747aa6a Add rpath to match Deno package cache layout 2023-10-13 18:54:37 +01:00
45 changed files with 389 additions and 283 deletions

View File

@@ -121,27 +121,42 @@ jobs:
github-runner-qemu: github-runner-qemu:
permissions: permissions:
contents: write contents: write
name: linux-arm - Node.js 18 - prebuild name: ${{ matrix.platform }} - Node.js ${{ matrix.nodejs_version_major }} - prebuild
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
include:
- platform: linux-arm
run_on_arch: armv6
nodejs_arch: armv6l
nodejs_hostname: unofficial-builds.nodejs.org
nodejs_version: "18.17.0"
nodejs_version_major: 18
- platform: linux-s390x
run_on_arch: s390x
nodejs_arch: s390x
nodejs_hostname: nodejs.org
nodejs_version: "18.17.0"
nodejs_version_major: 18
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v2 - uses: uraimo/run-on-arch-action@v2
with: with:
arch: armv6 arch: ${{ matrix.run_on_arch }}
distro: buster distro: buster
env: | env: |
nodejs_version: "18.17.0"
prebuild_upload: "${{ startsWith(github.ref, 'refs/tags/') && secrets.GITHUB_TOKEN || '' }}" prebuild_upload: "${{ startsWith(github.ref, 'refs/tags/') && secrets.GITHUB_TOKEN || '' }}"
run: | run: |
apt-get update apt-get update
apt-get install -y fontconfig fonts-noto-core g++ git libatomic1 make python3 xz-utils apt-get install -y curl g++ git libatomic1 make python3 xz-utils
mkdir /opt/nodejs mkdir /opt/nodejs
curl --silent https://unofficial-builds.nodejs.org/download/release/v${nodejs_version}/node-v${nodejs_version}-linux-armv6l.tar.xz | tar xJC /opt/nodejs --strip-components=1 curl --silent https://${{ matrix.nodejs_hostname }}/download/release/v${{ matrix.nodejs_version}}/node-v${{ matrix.nodejs_version}}-linux-${{ matrix.nodejs_arch }}.tar.xz | tar xJC /opt/nodejs --strip-components=1
export PATH=$PATH:/opt/nodejs/bin export PATH=$PATH:/opt/nodejs/bin
npm install --build-from-source npm install --build-from-source
npx mocha --no-config --spec=test/unit/io.js npx mocha --no-config --spec=test/unit/io.js --timeout=30000
npm run package-from-local-build npm run package-from-local-build
npm pkg set "optionalDependencies.@img/sharp-linux-arm=file:./npm/linux-arm" npm pkg set "optionalDependencies.@img/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}"
npm run clean npm run clean
npm install --ignore-scripts npm install --ignore-scripts
npx mocha --no-config --spec=test/unit/io.js --timeout=30000 npx mocha --no-config --spec=test/unit/io.js --timeout=30000

View File

@@ -1,6 +1,6 @@
{ {
"runtime": "napi", "runtime": "napi",
"include-regex": "(sharp-.+\\.node|libvips-.+\\.dll|libglib-.+\\.dll|libgobject-.+\\.dll)", "include-regex": "(sharp-.+\\.node|libvips-.+\\.dll)",
"prerelease": true, "prerelease": true,
"strip": true "strip": true
} }

View File

@@ -2,10 +2,14 @@
<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right"> <img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
The typical use case for this high speed Node.js module The typical use case for this high speed Node-API module
is to convert large images in common formats to is to convert large images in common formats to
smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions. smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions.
It can be used with all JavaScript runtimes
that provide support for Node-API v9, including
Node.js >= 18.17.0, Deno and Bun.
Resizing an image is typically 4x-5x faster than using the Resizing an image is typically 4x-5x faster than using the
quickest ImageMagick and GraphicsMagick settings quickest ImageMagick and GraphicsMagick settings
due to its use of [libvips](https://github.com/libvips/libvips). due to its use of [libvips](https://github.com/libvips/libvips).
@@ -16,7 +20,7 @@ Lanczos resampling ensures quality is not sacrificed for speed.
As well as image resizing, operations such as As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available. rotation, extraction, compositing and gamma correction are available.
Most modern macOS, Windows and Linux systems running Node.js >= 18.17.0 Most modern macOS, Windows and Linux systems
do not require any additional install or runtime dependencies. do not require any additional install or runtime dependencies.
## Documentation ## Documentation

View File

@@ -2,10 +2,14 @@
<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right"> <img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
The typical use case for this high speed Node.js module The typical use case for this high speed Node-API module
is to convert large images in common formats to is to convert large images in common formats to
smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions. smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions.
It can be used with all JavaScript runtimes
that provide support for Node-API v9, including
Node.js >= 18.17.0, Deno and Bun.
Resizing an image is typically 4x-5x faster than using the Resizing an image is typically 4x-5x faster than using the
quickest ImageMagick and GraphicsMagick settings quickest ImageMagick and GraphicsMagick settings
due to its use of [libvips](https://github.com/libvips/libvips). due to its use of [libvips](https://github.com/libvips/libvips).
@@ -16,7 +20,7 @@ Lanczos resampling ensures quality is not sacrificed for speed.
As well as image resizing, operations such as As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available. rotation, extraction, compositing and gamma correction are available.
Most modern macOS, Windows and Linux systems running Node.js >= 14.15.0 Most modern macOS, Windows and Linux systems
do not require any additional install or runtime dependencies. do not require any additional install or runtime dependencies.
### Formats ### Formats

View File

@@ -26,7 +26,7 @@ Implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_st
| --- | --- | --- | --- | | --- | --- | --- | --- |
| [input] | <code>Buffer</code> \| <code>ArrayBuffer</code> \| <code>Uint8Array</code> \| <code>Uint8ClampedArray</code> \| <code>Int8Array</code> \| <code>Uint16Array</code> \| <code>Int16Array</code> \| <code>Uint32Array</code> \| <code>Int32Array</code> \| <code>Float32Array</code> \| <code>Float64Array</code> \| <code>string</code> | | if present, can be a Buffer / ArrayBuffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or a TypedArray containing raw pixel image data, or a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file. JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present. | | [input] | <code>Buffer</code> \| <code>ArrayBuffer</code> \| <code>Uint8Array</code> \| <code>Uint8ClampedArray</code> \| <code>Int8Array</code> \| <code>Uint16Array</code> \| <code>Int16Array</code> \| <code>Uint32Array</code> \| <code>Int32Array</code> \| <code>Float32Array</code> \| <code>Float64Array</code> \| <code>string</code> | | if present, can be a Buffer / ArrayBuffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or a TypedArray containing raw pixel image data, or a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file. JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present. |
| [options] | <code>Object</code> | | if present, is an Object with optional attributes. | | [options] | <code>Object</code> | | if present, is an Object with optional attributes. |
| [options.failOn] | <code>string</code> | <code>&quot;&#x27;warning&#x27;&quot;</code> | when to abort processing of invalid pixel data, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), higher levels imply lower levels, invalid metadata will always abort. | | [options.failOn] | <code>string</code> | <code>&quot;&#x27;warning&#x27;&quot;</code> | When to abort processing of invalid pixel data, one of (in order of sensitivity, least to most): 'none', 'truncated', 'error', 'warning'. Higher levels imply lower levels. Invalid metadata will always abort. |
| [options.limitInputPixels] | <code>number</code> \| <code>boolean</code> | <code>268402689</code> | Do not process input images where the number of pixels (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted. An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). | | [options.limitInputPixels] | <code>number</code> \| <code>boolean</code> | <code>268402689</code> | Do not process input images where the number of pixels (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted. An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). |
| [options.unlimited] | <code>boolean</code> | <code>false</code> | Set this to `true` to remove safety features that help prevent memory exhaustion (JPEG, PNG, SVG, HEIF). | | [options.unlimited] | <code>boolean</code> | <code>false</code> | Set this to `true` to remove safety features that help prevent memory exhaustion (JPEG, PNG, SVG, HEIF). |
| [options.sequentialRead] | <code>boolean</code> | <code>true</code> | Set this to `false` to use random access rather than sequential read. Some operations will do this automatically. | | [options.sequentialRead] | <code>boolean</code> | <code>true</code> | Set this to `false` to use random access rather than sequential read. Some operations will do this automatically. |

View File

@@ -466,6 +466,7 @@ instead of providing `xres` and `yres` in pixels/mm.
| [options.yres] | <code>number</code> | <code>1.0</code> | vertical resolution in pixels/mm | | [options.yres] | <code>number</code> | <code>1.0</code> | vertical resolution in pixels/mm |
| [options.resolutionUnit] | <code>string</code> | <code>&quot;&#x27;inch&#x27;&quot;</code> | resolution unit options: inch, cm | | [options.resolutionUnit] | <code>string</code> | <code>&quot;&#x27;inch&#x27;&quot;</code> | resolution unit options: inch, cm |
| [options.bitdepth] | <code>number</code> | <code>8</code> | reduce bitdepth to 1, 2 or 4 bit | | [options.bitdepth] | <code>number</code> | <code>8</code> | reduce bitdepth to 1, 2 or 4 bit |
| [options.miniswhite] | <code>boolean</code> | <code>false</code> | write 1-bit images as miniswhite |
**Example** **Example**
```js ```js

View File

@@ -251,7 +251,7 @@ sharp(input)
## trim ## trim
> trim(trim) ⇒ <code>Sharp</code> > trim([options]) ⇒ <code>Sharp</code>
Trim pixels from all edges that contain values similar to the given background colour, which defaults to that of the top-left pixel. Trim pixels from all edges that contain values similar to the given background colour, which defaults to that of the top-left pixel.
@@ -259,8 +259,7 @@ Images with an alpha channel will use the combined bounding box of alpha and non
If the result of this operation would trim an image to nothing then no change is made. If the result of this operation would trim an image to nothing then no change is made.
The `info` response Object, obtained from callback of `.toFile()` or `.toBuffer()`, The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
will contain `trimOffsetLeft` and `trimOffsetTop` properties.
**Throws**: **Throws**:
@@ -270,46 +269,44 @@ will contain `trimOffsetLeft` and `trimOffsetTop` properties.
| Param | Type | Default | Description | | Param | Type | Default | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| trim | <code>string</code> \| <code>number</code> \| <code>Object</code> | | the specific background colour to trim, the threshold for doing so or an Object with both. | | [options] | <code>Object</code> | | |
| [trim.background] | <code>string</code> \| <code>Object</code> | <code>&quot;&#x27;top-left pixel&#x27;&quot;</code> | background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel. | | [options.background] | <code>string</code> \| <code>Object</code> | <code>&quot;&#x27;top-left pixel&#x27;&quot;</code> | Background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel. |
| [trim.threshold] | <code>number</code> | <code>10</code> | the allowed difference from the above colour, a positive number. | | [options.threshold] | <code>number</code> | <code>10</code> | Allowed difference from the above colour, a positive number. |
| [options.lineArt] | <code>boolean</code> | <code>false</code> | Does the input more closely resemble line art (e.g. vector) rather than being photographic? |
**Example** **Example**
```js ```js
// Trim pixels with a colour similar to that of the top-left pixel. // Trim pixels with a colour similar to that of the top-left pixel.
sharp(input) await sharp(input)
.trim() .trim()
.toFile(output, function(err, info) { .toFile(output);
...
});
``` ```
**Example** **Example**
```js ```js
// Trim pixels with the exact same colour as that of the top-left pixel. // Trim pixels with the exact same colour as that of the top-left pixel.
sharp(input) await sharp(input)
.trim(0) .trim({
.toFile(output, function(err, info) { threshold: 0
... })
}); .toFile(output);
``` ```
**Example** **Example**
```js ```js
// Trim only pixels with a similar colour to red. // Assume input is line art and trim only pixels with a similar colour to red.
sharp(input) const output = await sharp(input)
.trim("#FF0000") .trim({
.toFile(output, function(err, info) { background: "#FF0000",
... lineArt: true
}); })
.toBuffer();
``` ```
**Example** **Example**
```js ```js
// Trim all "yellow-ish" pixels, being more lenient with the higher threshold. // Trim all "yellow-ish" pixels, being more lenient with the higher threshold.
sharp(input) const output = await sharp(input)
.trim({ .trim({
background: "yellow", background: "yellow",
threshold: 42, threshold: 42,
}) })
.toFile(output, function(err, info) { .toBuffer();
...
});
``` ```

View File

@@ -2,7 +2,7 @@
## v0.33 - *gauge* ## v0.33 - *gauge*
Requires libvips v8.14.5 Requires libvips v8.15.0
### v0.33.0 - TBD ### v0.33.0 - TBD
@@ -14,6 +14,9 @@ Requires libvips v8.14.5
* Remove `sharp.vendor`. * Remove `sharp.vendor`.
* Options for `trim` operation must be an Object, add new `lineArt` option.
[#2363](https://github.com/lovell/sharp/issues/2363)
* Ensure all `Error` objects contain a `stack` property. * Ensure all `Error` objects contain a `stack` property.
[#3653](https://github.com/lovell/sharp/issues/3653) [#3653](https://github.com/lovell/sharp/issues/3653)
@@ -23,6 +26,17 @@ Requires libvips v8.14.5
* Ensure correct interpretation of 16-bit raw input. * Ensure correct interpretation of 16-bit raw input.
[#3808](https://github.com/lovell/sharp/issues/3808) [#3808](https://github.com/lovell/sharp/issues/3808)
* Add support for `miniswhite` when using TIFF output.
[#3812](https://github.com/lovell/sharp/pull/3812)
[@dnsbty](https://github.com/dnsbty)
* TypeScript: add missing definition for `withMetadata` boolean.
[#3823](https://github.com/lovell/sharp/pull/3823)
[@uhthomas](https://github.com/uhthomas)
* Ensure multi-page extract remains sequential.
[#3837](https://github.com/lovell/sharp/issues/3837)
## v0.32 - *flow* ## v0.32 - *flow*
Requires libvips v8.14.5 Requires libvips v8.14.5

View File

@@ -275,3 +275,6 @@ GitHub: https://github.com/LachlanNewman
Name: BJJ Name: BJJ
GitHub: https://github.com/bianjunjie1981 GitHub: https://github.com/bianjunjie1981
Name: Dennis Beatty
GitHub: https://github.com/dnsbty

View File

@@ -1,5 +1,7 @@
# Installation # Installation
Works with your choice of JavaScript package manager.
```sh ```sh
npm install sharp npm install sharp
``` ```
@@ -9,14 +11,20 @@ pnpm add sharp
``` ```
```sh ```sh
yarn add sharp yarn add sharp # v3 recommended, Plug'n'Play unsupported
``` ```
Yarn Plug'n'Play is unsupported. ```sh
bun add sharp
```
```sh
deno run --allow-ffi ...
```
## Prerequisites ## Prerequisites
* Node.js >= 18.17.0 * Node-API v9 compatible runtime e.g. Node.js >= 18.17.0
## Prebuilt binaries ## Prebuilt binaries
@@ -24,9 +32,10 @@ Ready-compiled sharp and libvips binaries are provided for use on the most commo
* macOS x64 (>= 10.13) * macOS x64 (>= 10.13)
* macOS ARM64 * macOS ARM64
* Linux x64 (glibc >= 2.26, musl >= 1.2.2, CPU with SSE4.2)
* Linux ARM64 (glibc >= 2.26, musl >= 1.2.2)
* Linux ARM (glibc >= 2.28) * Linux ARM (glibc >= 2.28)
* Linux ARM64 (glibc >= 2.26, musl >= 1.2.2)
* Linux s390x (glibc >= 2.28)
* Linux x64 (glibc >= 2.26, musl >= 1.2.2, CPU with SSE4.2)
* Windows x64 * Windows x64
* Windows x86 * Windows x86

File diff suppressed because one or more lines are too long

View File

@@ -121,7 +121,7 @@ const debuglog = util.debuglog('sharp');
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file. * a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present. * JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
* @param {Object} [options] - if present, is an Object with optional attributes. * @param {Object} [options] - if present, is an Object with optional attributes.
* @param {string} [options.failOn='warning'] - when to abort processing of invalid pixel data, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), higher levels imply lower levels, invalid metadata will always abort. * @param {string} [options.failOn='warning'] - When to abort processing of invalid pixel data, one of (in order of sensitivity, least to most): 'none', 'truncated', 'error', 'warning'. Higher levels imply lower levels. Invalid metadata will always abort.
* @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels * @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted. * (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). * An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
@@ -230,7 +230,8 @@ const Sharp = function (input, options) {
threshold: 0, threshold: 0,
thresholdGrayscale: true, thresholdGrayscale: true,
trimBackground: [], trimBackground: [],
trimThreshold: 0, trimThreshold: -1,
trimLineArt: false,
gamma: 0, gamma: 0,
gammaOut: 0, gammaOut: 0,
greyscale: false, greyscale: false,
@@ -305,6 +306,7 @@ const Sharp = function (input, options) {
tiffCompression: 'jpeg', tiffCompression: 'jpeg',
tiffPredictor: 'horizontal', tiffPredictor: 'horizontal',
tiffPyramid: false, tiffPyramid: false,
tiffMiniswhite: false,
tiffBitdepth: 8, tiffBitdepth: 8,
tiffTile: false, tiffTile: false,
tiffTileHeight: 256, tiffTileHeight: 256,

12
lib/index.d.ts vendored
View File

@@ -847,11 +847,11 @@ declare namespace sharp {
* Trim pixels from all edges that contain values similar to the given background colour, which defaults to that of the top-left pixel. * Trim pixels from all edges that contain values similar to the given background colour, which defaults to that of the top-left pixel.
* Images with an alpha channel will use the combined bounding box of alpha and non-alpha channels. * Images with an alpha channel will use the combined bounding box of alpha and non-alpha channels.
* The info response Object will contain trimOffsetLeft and trimOffsetTop properties. * The info response Object will contain trimOffsetLeft and trimOffsetTop properties.
* @param trim The specific background colour to trim, the threshold for doing so or an Object with both. * @param options trim options
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
* @returns A sharp instance that can be used to chain operations * @returns A sharp instance that can be used to chain operations
*/ */
trim(trim?: string | number | TrimOptions): Sharp; trim(options?: TrimOptions): Sharp;
//#endregion //#endregion
} }
@@ -1234,6 +1234,8 @@ declare namespace sharp {
yres?: number | undefined; yres?: number | undefined;
/** Reduce bitdepth to 1, 2 or 4 bit (optional, default 8) */ /** Reduce bitdepth to 1, 2 or 4 bit (optional, default 8) */
bitdepth?: 1 | 2 | 4 | 8 | undefined; bitdepth?: 1 | 2 | 4 | 8 | undefined;
/** Write 1-bit images as miniswhite (optional, default false) */
miniswhite?: boolean | undefined;
/** Resolution unit options: inch, cm (optional, default 'inch') */ /** Resolution unit options: inch, cm (optional, default 'inch') */
resolutionUnit?: 'inch' | 'cm' | undefined; resolutionUnit?: 'inch' | 'cm' | undefined;
} }
@@ -1340,10 +1342,12 @@ declare namespace sharp {
} }
interface TrimOptions { interface TrimOptions {
/** background colour, parsed by the color module, defaults to that of the top-left pixel. (optional) */ /** Background colour, parsed by the color module, defaults to that of the top-left pixel. (optional) */
background?: Color | undefined; background?: Color | undefined;
/** the allowed difference from the above colour, a positive number. (optional, default `10`) */ /** Allowed difference from the above colour, a positive number. (optional, default 10) */
threshold?: number | undefined; threshold?: number | undefined;
/** Does the input more closely resemble line art (e.g. vector) rather than being photographic? (optional, default false) */
lineArt?: boolean | undefined;
} }
interface RawOptions { interface RawOptions {

View File

@@ -782,6 +782,7 @@ function trySetAnimationOptions (source, target) {
* @param {number} [options.yres=1.0] - vertical resolution in pixels/mm * @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
* @param {string} [options.resolutionUnit='inch'] - resolution unit options: inch, cm * @param {string} [options.resolutionUnit='inch'] - resolution unit options: inch, cm
* @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit * @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
* @param {boolean} [options.miniswhite=false] - write 1-bit images as miniswhite
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid options * @throws {Error} Invalid options
*/ */
@@ -819,6 +820,10 @@ function tiff (options) {
throw is.invalidParameterError('tileHeight', 'integer greater than zero', options.tileHeight); throw is.invalidParameterError('tileHeight', 'integer greater than zero', options.tileHeight);
} }
} }
// miniswhite
if (is.defined(options.miniswhite)) {
this._setBooleanOption('tiffMiniswhite', options.miniswhite);
}
// pyramid // pyramid
if (is.defined(options.pyramid)) { if (is.defined(options.pyramid)) {
this._setBooleanOption('tiffPyramid', options.pyramid); this._setBooleanOption('tiffPyramid', options.pyramid);

View File

@@ -494,70 +494,67 @@ function extract (options) {
* *
* If the result of this operation would trim an image to nothing then no change is made. * If the result of this operation would trim an image to nothing then no change is made.
* *
* The `info` response Object, obtained from callback of `.toFile()` or `.toBuffer()`, * The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
* will contain `trimOffsetLeft` and `trimOffsetTop` properties.
* *
* @example * @example
* // Trim pixels with a colour similar to that of the top-left pixel. * // Trim pixels with a colour similar to that of the top-left pixel.
* sharp(input) * await sharp(input)
* .trim() * .trim()
* .toFile(output, function(err, info) { * .toFile(output);
* ... *
* });
* @example * @example
* // Trim pixels with the exact same colour as that of the top-left pixel. * // Trim pixels with the exact same colour as that of the top-left pixel.
* sharp(input) * await sharp(input)
* .trim(0) * .trim({
* .toFile(output, function(err, info) { * threshold: 0
* ... * })
* }); * .toFile(output);
*
* @example * @example
* // Trim only pixels with a similar colour to red. * // Assume input is line art and trim only pixels with a similar colour to red.
* sharp(input) * const output = await sharp(input)
* .trim("#FF0000") * .trim({
* .toFile(output, function(err, info) { * background: "#FF0000",
* ... * lineArt: true
* }); * })
* .toBuffer();
*
* @example * @example
* // Trim all "yellow-ish" pixels, being more lenient with the higher threshold. * // Trim all "yellow-ish" pixels, being more lenient with the higher threshold.
* sharp(input) * const output = await sharp(input)
* .trim({ * .trim({
* background: "yellow", * background: "yellow",
* threshold: 42, * threshold: 42,
* }) * })
* .toFile(output, function(err, info) { * .toBuffer();
* ...
* });
* *
* @param {string|number|Object} trim - the specific background colour to trim, the threshold for doing so or an Object with both. * @param {Object} [options]
* @param {string|Object} [trim.background='top-left pixel'] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel. * @param {string|Object} [options.background='top-left pixel'] - Background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel.
* @param {number} [trim.threshold=10] - the allowed difference from the above colour, a positive number. * @param {number} [options.threshold=10] - Allowed difference from the above colour, a positive number.
* @param {boolean} [options.lineArt=false] - Does the input more closely resemble line art (e.g. vector) rather than being photographic?
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
function trim (trim) { function trim (options) {
if (!is.defined(trim)) { this.options.trimThreshold = 10;
this.options.trimThreshold = 10; if (is.defined(options)) {
} else if (is.string(trim)) { if (is.object(options)) {
this._setBackgroundColourOption('trimBackground', trim); if (is.defined(options.background)) {
this.options.trimThreshold = 10; this._setBackgroundColourOption('trimBackground', options.background);
} else if (is.number(trim)) { }
if (trim >= 0) { if (is.defined(options.threshold)) {
this.options.trimThreshold = trim; if (is.number(options.threshold) && options.threshold >= 0) {
this.options.trimThreshold = options.threshold;
} else {
throw is.invalidParameterError('threshold', 'positive number', options.threshold);
}
}
if (is.defined(options.lineArt)) {
this._setBooleanOption('trimLineArt', options.lineArt);
}
} else { } else {
throw is.invalidParameterError('threshold', 'positive number', trim); throw is.invalidParameterError('trim', 'object', options);
} }
} else if (is.object(trim)) {
this._setBackgroundColourOption('trimBackground', trim.background);
if (!is.defined(trim.threshold)) {
this.options.trimThreshold = 10;
} else if (is.number(trim.threshold) && trim.threshold >= 0) {
this.options.trimThreshold = trim.threshold;
} else {
throw is.invalidParameterError('threshold', 'positive number', trim);
}
} else {
throw is.invalidParameterError('trim', 'string, number or object', trim);
} }
if (isRotationExpected(this.options)) { if (isRotationExpected(this.options)) {
this.options.rotateBeforePreExtract = true; this.options.rotateBeforePreExtract = true;

View File

@@ -1,7 +1,7 @@
{ {
"name": "@img/sharp-darwin-arm64", "name": "@img/sharp-darwin-arm64",
"version": "0.33.0-alpha.9", "version": "0.33.0-alpha.10",
"description": "Prebuilt sharp for use with macOS ARM64", "description": "Prebuilt sharp for use with macOS 64-bit ARM",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
"repository": { "repository": {
@@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-darwin-arm64": "0.0.1" "@img/sharp-libvips-darwin-arm64": "0.0.3"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-darwin-x64", "name": "@img/sharp-darwin-x64",
"version": "0.33.0-alpha.9", "version": "0.33.0-alpha.10",
"description": "Prebuilt sharp for use with macOS x64", "description": "Prebuilt sharp for use with macOS x64",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-darwin-x64": "0.0.1" "@img/sharp-libvips-darwin-x64": "0.0.3"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linux-arm", "name": "@img/sharp-linux-arm",
"version": "0.33.0-alpha.9", "version": "0.33.0-alpha.10",
"description": "Prebuilt sharp for use with Linux (glibc) ARM (32-bit)", "description": "Prebuilt sharp for use with Linux (glibc) ARM (32-bit)",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-linux-arm": "0.0.1" "@img/sharp-libvips-linux-arm": "0.0.3"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -1,7 +1,7 @@
{ {
"name": "@img/sharp-linux-arm64", "name": "@img/sharp-linux-arm64",
"version": "0.33.0-alpha.9", "version": "0.33.0-alpha.10",
"description": "Prebuilt sharp for use with Linux (glibc) ARM64", "description": "Prebuilt sharp for use with Linux (glibc) 64-bit ARM",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
"repository": { "repository": {
@@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-linux-arm64": "0.0.1" "@img/sharp-libvips-linux-arm64": "0.0.3"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -0,0 +1,47 @@
{
"name": "@img/sharp-linux-s390x",
"version": "0.33.0-alpha.10",
"description": "Prebuilt sharp for use with Linux (glibc) s390x",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
"type": "git",
"url": "git+https://github.com/lovell/sharp.git",
"directory": "npm/linux-s390x"
},
"license": "Apache-2.0",
"funding": {
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@img/sharp-libvips-linux-s390x": "0.0.3"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-linux-s390x.node",
"./package": "./package.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
"npm": ">=9.6.5",
"yarn": ">=3.2.0",
"pnpm": ">=7.1.0",
"glibc": ">=2.28"
},
"os": [
"linux"
],
"libc": [
"glibc"
],
"cpu": [
"s390x"
]
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linux-x64", "name": "@img/sharp-linux-x64",
"version": "0.33.0-alpha.9", "version": "0.33.0-alpha.10",
"description": "Prebuilt sharp for use with Linux (glibc) x64", "description": "Prebuilt sharp for use with Linux (glibc) x64",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-linux-x64": "0.0.1" "@img/sharp-libvips-linux-x64": "0.0.3"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -1,7 +1,7 @@
{ {
"name": "@img/sharp-linuxmusl-arm64", "name": "@img/sharp-linuxmusl-arm64",
"version": "0.33.0-alpha.9", "version": "0.33.0-alpha.10",
"description": "Prebuilt sharp for use with Linux (musl) ARM64", "description": "Prebuilt sharp for use with Linux (musl) 64-bit ARM",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
"repository": { "repository": {
@@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-linuxmusl-arm64": "0.0.1" "@img/sharp-libvips-linuxmusl-arm64": "0.0.3"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linuxmusl-x64", "name": "@img/sharp-linuxmusl-x64",
"version": "0.33.0-alpha.9", "version": "0.33.0-alpha.10",
"description": "Prebuilt sharp for use with Linux (musl) x64", "description": "Prebuilt sharp for use with Linux (musl) x64",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-linuxmusl-x64": "0.0.1" "@img/sharp-libvips-linuxmusl-x64": "0.0.3"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -1,15 +1,16 @@
{ {
"name": "@img/sharp", "name": "@img/sharp",
"version": "0.33.0-alpha.9", "version": "0.33.0-alpha.10",
"private": "true", "private": "true",
"workspaces": [ "workspaces": [
"darwin-x64",
"darwin-arm64", "darwin-arm64",
"darwin-x64",
"linux-arm", "linux-arm",
"linux-arm64", "linux-arm64",
"linux-s390x",
"linux-x64",
"linuxmusl-arm64", "linuxmusl-arm64",
"linuxmusl-x64", "linuxmusl-x64",
"linux-x64",
"win32-ia32", "win32-ia32",
"win32-x64" "win32-x64"
] ]

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-win32-ia32", "name": "@img/sharp-win32-ia32",
"version": "0.33.0-alpha.9", "version": "0.33.0-alpha.10",
"description": "Prebuilt sharp for use with Windows x86 (32-bit)", "description": "Prebuilt sharp for use with Windows x86 (32-bit)",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-win32-x64", "name": "@img/sharp-win32-x64",
"version": "0.33.0-alpha.9", "version": "0.33.0-alpha.10",
"description": "Prebuilt sharp for use with Windows x64", "description": "Prebuilt sharp for use with Windows x64",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",

View File

@@ -1,7 +1,7 @@
{ {
"name": "sharp", "name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images", "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
"version": "0.33.0-alpha.9", "version": "0.33.0-alpha.10",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp", "homepage": "https://github.com/lovell/sharp",
"contributors": [ "contributors": [
@@ -86,7 +86,8 @@
"Ankur Parihar <ankur.github@gmail.com>", "Ankur Parihar <ankur.github@gmail.com>",
"Brahim Ait elhaj <brahima@gmail.com>", "Brahim Ait elhaj <brahima@gmail.com>",
"Mart Jansink <m.jansink@gmail.com>", "Mart Jansink <m.jansink@gmail.com>",
"Lachlan Newman <lachnewman007@gmail.com>" "Lachlan Newman <lachnewman007@gmail.com>",
"Dennis Beatty <dennis@dcbeatty.com>"
], ],
"scripts": { "scripts": {
"install": "node install/check", "install": "node install/check",
@@ -139,27 +140,29 @@
"semver": "^7.5.4" "semver": "^7.5.4"
}, },
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-darwin-arm64": "0.33.0-alpha.9", "@img/sharp-darwin-arm64": "0.33.0-alpha.10",
"@img/sharp-darwin-x64": "0.33.0-alpha.9", "@img/sharp-darwin-x64": "0.33.0-alpha.10",
"@img/sharp-libvips-darwin-arm64": "0.0.1", "@img/sharp-libvips-darwin-arm64": "0.0.3",
"@img/sharp-libvips-darwin-x64": "0.0.1", "@img/sharp-libvips-darwin-x64": "0.0.3",
"@img/sharp-libvips-linux-arm": "0.0.1", "@img/sharp-libvips-linux-arm": "0.0.3",
"@img/sharp-libvips-linux-arm64": "0.0.1", "@img/sharp-libvips-linux-arm64": "0.0.3",
"@img/sharp-libvips-linux-x64": "0.0.1", "@img/sharp-libvips-linux-s390x": "0.0.3",
"@img/sharp-libvips-linuxmusl-arm64": "0.0.1", "@img/sharp-libvips-linux-x64": "0.0.3",
"@img/sharp-libvips-linuxmusl-x64": "0.0.1", "@img/sharp-libvips-linuxmusl-arm64": "0.0.3",
"@img/sharp-linux-arm": "0.33.0-alpha.9", "@img/sharp-libvips-linuxmusl-x64": "0.0.3",
"@img/sharp-linux-arm64": "0.33.0-alpha.9", "@img/sharp-linux-arm": "0.33.0-alpha.10",
"@img/sharp-linux-x64": "0.33.0-alpha.9", "@img/sharp-linux-arm64": "0.33.0-alpha.10",
"@img/sharp-linuxmusl-arm64": "0.33.0-alpha.9", "@img/sharp-linux-s390x": "0.33.0-alpha.10",
"@img/sharp-linuxmusl-x64": "0.33.0-alpha.9", "@img/sharp-linux-x64": "0.33.0-alpha.10",
"@img/sharp-win32-ia32": "0.33.0-alpha.9", "@img/sharp-linuxmusl-arm64": "0.33.0-alpha.10",
"@img/sharp-win32-x64": "0.33.0-alpha.9" "@img/sharp-linuxmusl-x64": "0.33.0-alpha.10",
"@img/sharp-win32-ia32": "0.33.0-alpha.10",
"@img/sharp-win32-x64": "0.33.0-alpha.10"
}, },
"devDependencies": { "devDependencies": {
"@img/sharp-libvips-dev": "0.0.1", "@img/sharp-libvips-dev": "0.0.3",
"@img/sharp-libvips-win32-ia32": "0.0.1", "@img/sharp-libvips-win32-ia32": "0.0.3",
"@img/sharp-libvips-win32-x64": "0.0.1", "@img/sharp-libvips-win32-x64": "0.0.3",
"@types/node": "*", "@types/node": "*",
"async": "^3.2.4", "async": "^3.2.4",
"cc": "^3.0.1", "cc": "^3.0.1",
@@ -179,7 +182,7 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0", "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
"libvips": ">=8.14.5" "libvips": ">=8.15.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"

View File

@@ -5,6 +5,7 @@
'variables': { 'variables': {
'vips_version': '<!(node -p "require(\'../lib/libvips\').minimumLibvipsVersion")', 'vips_version': '<!(node -p "require(\'../lib/libvips\').minimumLibvipsVersion")',
'platform_and_arch': '<!(node -p "require(\'../lib/libvips\').buildPlatformArch()")', 'platform_and_arch': '<!(node -p "require(\'../lib/libvips\').buildPlatformArch()")',
'sharp_libvips_version': '<!(node -p "require(\'../package.json\').optionalDependencies[\'@img/sharp-libvips-<(platform_and_arch)\']")',
'sharp_libvips_include_dir': '<!(node -p "require(\'../lib/libvips\').buildSharpLibvipsIncludeDir()")', 'sharp_libvips_include_dir': '<!(node -p "require(\'../lib/libvips\').buildSharpLibvipsIncludeDir()")',
'sharp_libvips_cplusplus_dir': '<!(node -p "require(\'../lib/libvips\').buildSharpLibvipsCPlusPlusDir()")', 'sharp_libvips_cplusplus_dir': '<!(node -p "require(\'../lib/libvips\').buildSharpLibvipsCPlusPlusDir()")',
'sharp_libvips_lib_dir': '<!(node -p "require(\'../lib/libvips\').buildSharpLibvipsLibDir()")' 'sharp_libvips_lib_dir': '<!(node -p "require(\'../lib/libvips\').buildSharpLibvipsLibDir()")'
@@ -32,11 +33,11 @@
'<(sharp_libvips_lib_dir)/glib-2.0/include' '<(sharp_libvips_lib_dir)/glib-2.0/include'
], ],
'link_settings': { 'link_settings': {
'library_dirs': ['<(sharp_libvips_lib_dir)'], 'library_dirs': [
'<(sharp_libvips_lib_dir)'
],
'libraries': [ 'libraries': [
'libvips.lib', 'libvips.lib'
'libglib-2.0.lib',
'libgobject-2.0.lib'
], ],
}, },
'configurations': { 'configurations': {
@@ -129,6 +130,9 @@
'<(sharp_libvips_include_dir)/glib-2.0', '<(sharp_libvips_include_dir)/glib-2.0',
'<(sharp_libvips_lib_dir)/glib-2.0/include' '<(sharp_libvips_lib_dir)/glib-2.0/include'
], ],
'library_dirs': [
'<(sharp_libvips_lib_dir)'
],
'conditions': [ 'conditions': [
['OS == "win"', { ['OS == "win"', {
'defines': [ 'defines': [
@@ -136,19 +140,13 @@
'_FILE_OFFSET_BITS=64' '_FILE_OFFSET_BITS=64'
], ],
'link_settings': { 'link_settings': {
'library_dirs': ['<(sharp_libvips_lib_dir)'],
'libraries': [ 'libraries': [
'libvips.lib', 'libvips.lib'
'libglib-2.0.lib',
'libgobject-2.0.lib'
] ]
} }
}], }],
['OS == "mac"', { ['OS == "mac"', {
'link_settings': { 'link_settings': {
'library_dirs': [
'<(sharp_libvips_lib_dir)'
],
'libraries': [ 'libraries': [
'libvips-cpp.42.dylib' 'libvips-cpp.42.dylib'
] ]
@@ -157,6 +155,7 @@
'OTHER_LDFLAGS': [ 'OTHER_LDFLAGS': [
# Ensure runtime linking is relative to sharp.node # Ensure runtime linking is relative to sharp.node
'-Wl,-rpath,\'@loader_path/../../sharp-libvips-<(platform_and_arch)/lib\'', '-Wl,-rpath,\'@loader_path/../../sharp-libvips-<(platform_and_arch)/lib\'',
'-Wl,-rpath,\'@loader_path/../../../<(sharp_libvips_version)/sharp-libvips-<(platform_and_arch)/lib\'',
'-Wl,-rpath,\'@loader_path/../../node_modules/@img/sharp-libvips-<(platform_and_arch)/lib\'', '-Wl,-rpath,\'@loader_path/../../node_modules/@img/sharp-libvips-<(platform_and_arch)/lib\'',
'-Wl,-rpath,\'@loader_path/../../../node_modules/@img/sharp-libvips-<(platform_and_arch)/lib\'' '-Wl,-rpath,\'@loader_path/../../../node_modules/@img/sharp-libvips-<(platform_and_arch)/lib\''
] ]
@@ -167,9 +166,6 @@
'_GLIBCXX_USE_CXX11_ABI=1' '_GLIBCXX_USE_CXX11_ABI=1'
], ],
'link_settings': { 'link_settings': {
'library_dirs': [
'<(sharp_libvips_lib_dir)'
],
'libraries': [ 'libraries': [
'-l:libvips-cpp.so.42' '-l:libvips-cpp.so.42'
], ],
@@ -178,6 +174,7 @@
'-Wl,-s', '-Wl,-s',
'-Wl,--disable-new-dtags', '-Wl,--disable-new-dtags',
'-Wl,-rpath=\'$$ORIGIN/../../sharp-libvips-<(platform_and_arch)/lib\'', '-Wl,-rpath=\'$$ORIGIN/../../sharp-libvips-<(platform_and_arch)/lib\'',
'-Wl,-rpath=\'$$ORIGIN/../../../<(sharp_libvips_version)/sharp-libvips-<(platform_and_arch)/lib\'',
'-Wl,-rpath=\'$$ORIGIN/../../node_modules/@img/sharp-libvips-<(platform_and_arch)/lib\'', '-Wl,-rpath=\'$$ORIGIN/../../node_modules/@img/sharp-libvips-<(platform_and_arch)/lib\'',
'-Wl,-rpath=\'$$ORIGIN/../../../node_modules/@img/sharp-libvips-<(platform_and_arch)/lib\'' '-Wl,-rpath=\'$$ORIGIN/../../../node_modules/@img/sharp-libvips-<(platform_and_arch)/lib\''
] ]
@@ -256,9 +253,7 @@
'copies': [{ 'copies': [{
'destination': 'build/Release', 'destination': 'build/Release',
'files': [ 'files': [
'<(sharp_libvips_lib_dir)/libvips-42.dll', '<(sharp_libvips_lib_dir)/libvips-42.dll'
'<(sharp_libvips_lib_dir)/libglib-2.0-0.dll',
'<(sharp_libvips_lib_dir)/libgobject-2.0-0.dll'
] ]
}] }]
}] }]

View File

@@ -15,9 +15,9 @@
// Verify platform and compiler compatibility // Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8) || \ #if (VIPS_MAJOR_VERSION < 8) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 14) || \ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 15) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 14 && VIPS_MICRO_VERSION < 5) (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 15 && VIPS_MICRO_VERSION < 0)
#error "libvips version 8.14.5+ is required - please see https://sharp.pixelplumbing.com/install" #error "libvips version 8.15.0+ is required - please see https://sharp.pixelplumbing.com/install"
#endif #endif
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6))) #if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))

View File

@@ -145,7 +145,7 @@ class MetadataWorker : public Napi::AsyncWorker {
// Handle warnings // Handle warnings
std::string warning = sharp::VipsWarningPop(); std::string warning = sharp::VipsWarningPop();
while (!warning.empty()) { while (!warning.empty()) {
debuglog.MakeCallback(Receiver().Value(), { Napi::String::New(env, warning) }); debuglog.Call(Receiver().Value(), { Napi::String::New(env, warning) });
warning = sharp::VipsWarningPop(); warning = sharp::VipsWarningPop();
} }
@@ -246,9 +246,9 @@ class MetadataWorker : public Napi::AsyncWorker {
Napi::Buffer<char>::NewOrCopy(env, baton->tifftagPhotoshop, Napi::Buffer<char>::NewOrCopy(env, baton->tifftagPhotoshop,
baton->tifftagPhotoshopLength, sharp::FreeCallback)); baton->tifftagPhotoshopLength, sharp::FreeCallback));
} }
Callback().MakeCallback(Receiver().Value(), { env.Null(), info }); Callback().Call(Receiver().Value(), { env.Null(), info });
} else { } else {
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() }); Callback().Call(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
} }
delete baton->input; delete baton->input;

View File

@@ -141,7 +141,7 @@ namespace sharp {
return image.conv(blur); return image.conv(blur);
} else { } else {
// Slower, accurate Gaussian blur // Slower, accurate Gaussian blur
return image.gaussblur(sigma); return StaySequential(image, VIPS_ACCESS_SEQUENTIAL).gaussblur(sigma);
} }
} }
@@ -265,7 +265,7 @@ namespace sharp {
/* /*
Trim an image Trim an image
*/ */
VImage Trim(VImage image, std::vector<double> background, double threshold) { VImage Trim(VImage image, std::vector<double> background, double threshold, bool const lineArt) {
if (image.width() < 3 && image.height() < 3) { if (image.width() < 3 && image.height() < 3) {
throw VError("Image to trim must be at least 3x3 pixels"); throw VError("Image to trim must be at least 3x3 pixels");
} }
@@ -287,6 +287,7 @@ namespace sharp {
int left, top, width, height; int left, top, width, height;
left = image.find_trim(&top, &width, &height, VImage::option() left = image.find_trim(&top, &width, &height, VImage::option()
->set("background", background) ->set("background", background)
->set("line_art", lineArt)
->set("threshold", threshold)); ->set("threshold", threshold));
if (HasAlpha(image)) { if (HasAlpha(image)) {
// Search alpha channel (A) // Search alpha channel (A)
@@ -294,6 +295,7 @@ namespace sharp {
VImage alpha = image[image.bands() - 1]; VImage alpha = image[image.bands() - 1];
leftA = alpha.find_trim(&topA, &widthA, &heightA, VImage::option() leftA = alpha.find_trim(&topA, &widthA, &heightA, VImage::option()
->set("background", backgroundAlpha) ->set("background", backgroundAlpha)
->set("line_art", lineArt)
->set("threshold", threshold)); ->set("threshold", threshold));
if (widthA > 0 && heightA > 0) { if (widthA > 0 && heightA > 0) {
if (width > 0 && height > 0) { if (width > 0 && height > 0) {
@@ -370,6 +372,7 @@ namespace sharp {
pages.reserve(nPages); pages.reserve(nPages);
// Split the image into cropped frames // Split the image into cropped frames
image = StaySequential(image, VIPS_ACCESS_SEQUENTIAL);
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
pages.push_back( pages.push_back(
image.extract_area(left, *pageHeight * i + top, width, height)); image.extract_area(left, *pageHeight * i + top, width, height));

View File

@@ -79,7 +79,7 @@ namespace sharp {
/* /*
Trim an image Trim an image
*/ */
VImage Trim(VImage image, std::vector<double> background, double const threshold); VImage Trim(VImage image, std::vector<double> background, double threshold, bool const lineArt);
/* /*
* Linear adjustment (a * in + b) * Linear adjustment (a * in + b)

View File

@@ -126,10 +126,10 @@ class PipelineWorker : public Napi::AsyncWorker {
} }
// Trim // Trim
if (baton->trimThreshold > 0.0) { if (baton->trimThreshold >= 0.0) {
MultiPageUnsupported(nPages, "Trim"); MultiPageUnsupported(nPages, "Trim");
image = sharp::StaySequential(image, access); image = sharp::StaySequential(image, access);
image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold); image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold, baton->trimLineArt);
baton->trimOffsetLeft = image.xoffset(); baton->trimOffsetLeft = image.xoffset();
baton->trimOffsetTop = image.yoffset(); baton->trimOffsetTop = image.yoffset();
} }
@@ -182,7 +182,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// - trimming or pre-resize extract isn't required; // - trimming or pre-resize extract isn't required;
// - input colourspace is not specified; // - input colourspace is not specified;
bool const shouldPreShrink = (targetResizeWidth > 0 || targetResizeHeight > 0) && bool const shouldPreShrink = (targetResizeWidth > 0 || targetResizeHeight > 0) &&
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0 && baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold < 0.0 &&
baton->colourspaceInput == VIPS_INTERPRETATION_LAST && !shouldRotateBefore; baton->colourspaceInput == VIPS_INTERPRETATION_LAST && !shouldRotateBefore;
if (shouldPreShrink) { if (shouldPreShrink) {
@@ -485,9 +485,7 @@ class PipelineWorker : public Napi::AsyncWorker {
image = sharp::StaySequential(image, access); image = sharp::StaySequential(image, access);
image = image.smartcrop(baton->width, baton->height, VImage::option() image = image.smartcrop(baton->width, baton->height, VImage::option()
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION) ->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
#if (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 15)
->set("premultiplied", shouldPremultiplyAlpha) ->set("premultiplied", shouldPremultiplyAlpha)
#endif
->set("attention_x", &attention_x) ->set("attention_x", &attention_x)
->set("attention_y", &attention_y)); ->set("attention_y", &attention_y));
baton->hasCropOffset = true; baton->hasCropOffset = true;
@@ -940,6 +938,7 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("Q", baton->tiffQuality) ->set("Q", baton->tiffQuality)
->set("bitdepth", baton->tiffBitdepth) ->set("bitdepth", baton->tiffBitdepth)
->set("compression", baton->tiffCompression) ->set("compression", baton->tiffCompression)
->set("miniswhite", baton->tiffMiniswhite)
->set("predictor", baton->tiffPredictor) ->set("predictor", baton->tiffPredictor)
->set("pyramid", baton->tiffPyramid) ->set("pyramid", baton->tiffPyramid)
->set("tile", baton->tiffTile) ->set("tile", baton->tiffTile)
@@ -1136,6 +1135,7 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("Q", baton->tiffQuality) ->set("Q", baton->tiffQuality)
->set("bitdepth", baton->tiffBitdepth) ->set("bitdepth", baton->tiffBitdepth)
->set("compression", baton->tiffCompression) ->set("compression", baton->tiffCompression)
->set("miniswhite", baton->tiffMiniswhite)
->set("predictor", baton->tiffPredictor) ->set("predictor", baton->tiffPredictor)
->set("pyramid", baton->tiffPyramid) ->set("pyramid", baton->tiffPyramid)
->set("tile", baton->tiffTile) ->set("tile", baton->tiffTile)
@@ -1215,7 +1215,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// Handle warnings // Handle warnings
std::string warning = sharp::VipsWarningPop(); std::string warning = sharp::VipsWarningPop();
while (!warning.empty()) { while (!warning.empty()) {
debuglog.MakeCallback(Receiver().Value(), { Napi::String::New(env, warning) }); debuglog.Call(Receiver().Value(), { Napi::String::New(env, warning) });
warning = sharp::VipsWarningPop(); warning = sharp::VipsWarningPop();
} }
@@ -1248,11 +1248,10 @@ class PipelineWorker : public Napi::AsyncWorker {
info.Set("attentionX", static_cast<int32_t>(baton->attentionX)); info.Set("attentionX", static_cast<int32_t>(baton->attentionX));
info.Set("attentionY", static_cast<int32_t>(baton->attentionY)); info.Set("attentionY", static_cast<int32_t>(baton->attentionY));
} }
if (baton->trimThreshold > 0.0) { if (baton->trimThreshold >= 0.0) {
info.Set("trimOffsetLeft", static_cast<int32_t>(baton->trimOffsetLeft)); info.Set("trimOffsetLeft", static_cast<int32_t>(baton->trimOffsetLeft));
info.Set("trimOffsetTop", static_cast<int32_t>(baton->trimOffsetTop)); info.Set("trimOffsetTop", static_cast<int32_t>(baton->trimOffsetTop));
} }
if (baton->input->textAutofitDpi) { if (baton->input->textAutofitDpi) {
info.Set("textAutofitDpi", static_cast<uint32_t>(baton->input->textAutofitDpi)); info.Set("textAutofitDpi", static_cast<uint32_t>(baton->input->textAutofitDpi));
} }
@@ -1263,17 +1262,17 @@ class PipelineWorker : public Napi::AsyncWorker {
// Pass ownership of output data to Buffer instance // Pass ownership of output data to Buffer instance
Napi::Buffer<char> data = Napi::Buffer<char>::NewOrCopy(env, static_cast<char*>(baton->bufferOut), Napi::Buffer<char> data = Napi::Buffer<char>::NewOrCopy(env, static_cast<char*>(baton->bufferOut),
baton->bufferOutLength, sharp::FreeCallback); baton->bufferOutLength, sharp::FreeCallback);
Callback().MakeCallback(Receiver().Value(), { env.Null(), data, info }); Callback().Call(Receiver().Value(), { env.Null(), data, info });
} else { } else {
// Add file size to info // Add file size to info
struct STAT64_STRUCT st; struct STAT64_STRUCT st;
if (STAT64_FUNCTION(baton->fileOut.data(), &st) == 0) { if (STAT64_FUNCTION(baton->fileOut.data(), &st) == 0) {
info.Set("size", static_cast<uint32_t>(st.st_size)); info.Set("size", static_cast<uint32_t>(st.st_size));
} }
Callback().MakeCallback(Receiver().Value(), { env.Null(), info }); Callback().Call(Receiver().Value(), { env.Null(), info });
} }
} else { } else {
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() }); Callback().Call(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
} }
// Delete baton // Delete baton
@@ -1291,7 +1290,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// Decrement processing task counter // Decrement processing task counter
sharp::counterProcess--; sharp::counterProcess--;
Napi::Number queueLength = Napi::Number::New(env, static_cast<int>(sharp::counterQueue)); Napi::Number queueLength = Napi::Number::New(env, static_cast<int>(sharp::counterQueue));
queueListener.MakeCallback(Receiver().Value(), { queueLength }); queueListener.Call(Receiver().Value(), { queueLength });
} }
private: private:
@@ -1519,6 +1518,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
baton->thresholdGrayscale = sharp::AttrAsBool(options, "thresholdGrayscale"); baton->thresholdGrayscale = sharp::AttrAsBool(options, "thresholdGrayscale");
baton->trimBackground = sharp::AttrAsVectorOfDouble(options, "trimBackground"); baton->trimBackground = sharp::AttrAsVectorOfDouble(options, "trimBackground");
baton->trimThreshold = sharp::AttrAsDouble(options, "trimThreshold"); baton->trimThreshold = sharp::AttrAsDouble(options, "trimThreshold");
baton->trimLineArt = sharp::AttrAsBool(options, "trimLineArt");
baton->gamma = sharp::AttrAsDouble(options, "gamma"); baton->gamma = sharp::AttrAsDouble(options, "gamma");
baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut"); baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut");
baton->linearA = sharp::AttrAsVectorOfDouble(options, "linearA"); baton->linearA = sharp::AttrAsVectorOfDouble(options, "linearA");
@@ -1647,6 +1647,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
baton->gifProgressive = sharp::AttrAsBool(options, "gifProgressive"); baton->gifProgressive = sharp::AttrAsBool(options, "gifProgressive");
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality"); baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid"); baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
baton->tiffMiniswhite = sharp::AttrAsBool(options, "tiffMiniswhite");
baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth"); baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
baton->tiffTile = sharp::AttrAsBool(options, "tiffTile"); baton->tiffTile = sharp::AttrAsBool(options, "tiffTile");
baton->tiffTileWidth = sharp::AttrAsUint32(options, "tiffTileWidth"); baton->tiffTileWidth = sharp::AttrAsUint32(options, "tiffTileWidth");
@@ -1708,7 +1709,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
// Increment queued task counter // Increment queued task counter
Napi::Number queueLength = Napi::Number::New(info.Env(), static_cast<int>(++sharp::counterQueue)); Napi::Number queueLength = Napi::Number::New(info.Env(), static_cast<int>(++sharp::counterQueue));
queueListener.MakeCallback(info.This(), { queueLength }); queueListener.Call(info.This(), { queueLength });
return info.Env().Undefined(); return info.Env().Undefined();
} }

View File

@@ -92,6 +92,7 @@ struct PipelineBaton {
bool thresholdGrayscale; bool thresholdGrayscale;
std::vector<double> trimBackground; std::vector<double> trimBackground;
double trimThreshold; double trimThreshold;
bool trimLineArt;
int trimOffsetLeft; int trimOffsetLeft;
int trimOffsetTop; int trimOffsetTop;
std::vector<double> linearA; std::vector<double> linearA;
@@ -169,6 +170,7 @@ struct PipelineBaton {
VipsForeignTiffPredictor tiffPredictor; VipsForeignTiffPredictor tiffPredictor;
bool tiffPyramid; bool tiffPyramid;
int tiffBitdepth; int tiffBitdepth;
bool tiffMiniswhite;
bool tiffTile; bool tiffTile;
int tiffTileHeight; int tiffTileHeight;
int tiffTileWidth; int tiffTileWidth;
@@ -259,7 +261,8 @@ struct PipelineBaton {
threshold(0), threshold(0),
thresholdGrayscale(true), thresholdGrayscale(true),
trimBackground{}, trimBackground{},
trimThreshold(0.0), trimThreshold(-1.0),
trimLineArt(false),
trimOffsetLeft(0), trimOffsetLeft(0),
trimOffsetTop(0), trimOffsetTop(0),
linearA{}, linearA{},
@@ -335,6 +338,7 @@ struct PipelineBaton {
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL), tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
tiffPyramid(false), tiffPyramid(false),
tiffBitdepth(8), tiffBitdepth(8),
tiffMiniswhite(false),
tiffTile(false), tiffTile(false),
tiffTileHeight(256), tiffTileHeight(256),
tiffTileWidth(256), tiffTileWidth(256),

View File

@@ -106,7 +106,7 @@ class StatsWorker : public Napi::AsyncWorker {
// Handle warnings // Handle warnings
std::string warning = sharp::VipsWarningPop(); std::string warning = sharp::VipsWarningPop();
while (!warning.empty()) { while (!warning.empty()) {
debuglog.MakeCallback(Receiver().Value(), { Napi::String::New(env, warning) }); debuglog.Call(Receiver().Value(), { Napi::String::New(env, warning) });
warning = sharp::VipsWarningPop(); warning = sharp::VipsWarningPop();
} }
@@ -141,9 +141,9 @@ class StatsWorker : public Napi::AsyncWorker {
dominant.Set("g", baton->dominantGreen); dominant.Set("g", baton->dominantGreen);
dominant.Set("b", baton->dominantBlue); dominant.Set("b", baton->dominantBlue);
info.Set("dominant", dominant); info.Set("dominant", dominant);
Callback().MakeCallback(Receiver().Value(), { env.Null(), info }); Callback().Call(Receiver().Value(), { env.Null(), info });
} else { } else {
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() }); Callback().Call(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
} }
delete baton->input; delete baton->input;

View File

@@ -572,8 +572,8 @@ const nohalo: string = sharp.interpolators.nohalo;
const vertexSplitQuadraticBasisSpline: string = sharp.interpolators.vertexSplitQuadraticBasisSpline; const vertexSplitQuadraticBasisSpline: string = sharp.interpolators.vertexSplitQuadraticBasisSpline;
// Triming // Triming
sharp(input).trim('#000').toBuffer(); sharp(input).trim({ background: '#000' }).toBuffer();
sharp(input).trim(10).toBuffer(); sharp(input).trim({ threshold: 10, lineArt: true }).toBuffer();
sharp(input).trim({ background: '#bf1942', threshold: 30 }).toBuffer(); sharp(input).trim({ background: '#bf1942', threshold: 30 }).toBuffer();
// Text input // Text input

View File

@@ -246,7 +246,7 @@ describe('composite', () => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(300, 300) .resize(300, 300)
.composite([{ .composite([{
input: Buffer.from('<svg width="200" height="200"><rect x="0" y="0" width="200" height="200" rx="50" ry="50"/></svg>'), input: Buffer.from('<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"><rect x="0" y="0" width="200" height="200" rx="50" ry="50"/></svg>'),
density: 96, density: 96,
blend: 'dest-in', blend: 'dest-in',
cutout: true cutout: true

View File

@@ -6,43 +6,44 @@
const assert = require('assert'); const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures');
describe('Median filter', () => { const row = [0, 3, 15, 63, 127, 255];
it('1x1 window', async () => { const input = Buffer.from(Array.from(row, () => row).flat());
const [r, g, b] = await sharp(fixtures.inputSvgSmallViewBox) const raw = {
.median(1) width: 6,
height: 6,
channels: 1
};
describe('Median filter', function () {
it('default window (3x3)', async () => {
const data = await sharp(input, { raw })
.median()
.toColourspace('b-w')
.raw() .raw()
.toBuffer(); .toBuffer();
assert.deepStrictEqual({ r: 0, g: 0, b: 0 }, { r, g, b }); assert.deepStrictEqual(data.subarray(0, 6), Buffer.from(row));
}); });
it('3x3 window', async () => { it('3x3 window', async () => {
const [r, g, b] = await sharp(fixtures.inputSvgSmallViewBox) const data = await sharp(input, { raw })
.median(3) .median(3)
.toColourspace('b-w')
.raw() .raw()
.toBuffer(); .toBuffer();
assert.deepStrictEqual({ r: 255, g: 0, b: 127 }, { r, g, b }); assert.deepStrictEqual(data.subarray(0, 6), Buffer.from(row));
}); });
it('7x7 window', async () => { it('5x5 window', async () => {
const [r, g, b] = await sharp(fixtures.inputSvgSmallViewBox) const data = await sharp(input, { raw })
.median(7) .median(5)
.toColourspace('b-w')
.raw() .raw()
.toBuffer(); .toBuffer();
assert.deepStrictEqual({ r: 255, g: 19, b: 146 }, { r, g, b }); assert.deepStrictEqual(data.subarray(0, 6), Buffer.from([0, 3, 15, 15, 63, 127]));
});
it('default window (3x3)', async () => {
const [r, g, b] = await sharp(fixtures.inputSvgSmallViewBox)
.median()
.raw()
.toBuffer();
assert.deepStrictEqual({ r: 255, g: 0, b: 127 }, { r, g, b });
}); });
it('invalid radius', () => { it('invalid radius', () => {

View File

@@ -141,21 +141,21 @@ describe('SVG input', function () {
it('Fails to render SVG larger than 32767x32767', () => it('Fails to render SVG larger than 32767x32767', () =>
assert.rejects( assert.rejects(
() => sharp(Buffer.from('<svg width="32768" height="1" />')).toBuffer(), () => sharp(Buffer.from('<svg xmlns="http://www.w3.org/2000/svg" width="32768" height="1" />')).toBuffer(),
/Input SVG image exceeds 32767x32767 pixel limit/ /Input SVG image exceeds 32767x32767 pixel limit/
) )
); );
it('Fails to render scaled SVG larger than 32767x32767', () => it('Fails to render scaled SVG larger than 32767x32767', () =>
assert.rejects( assert.rejects(
() => sharp(Buffer.from('<svg width="32767" height="1" />')).resize(32768).toBuffer(), () => sharp(Buffer.from('<svg xmlns="http://www.w3.org/2000/svg" width="32767" height="1" />')).resize(32768).toBuffer(),
/Input SVG image will exceed 32767x32767 pixel limit when scaled/ /Input SVG image will exceed 32767x32767 pixel limit when scaled/
) )
); );
it('Detects SVG passed as a string', () => it('Detects SVG passed as a string', () =>
assert.rejects( assert.rejects(
() => sharp('<svg></svg>').toBuffer(), () => sharp('<svg xmlns="http://www.w3.org/2000/svg"></svg>').toBuffer(),
/Input file is missing, did you mean/ /Input file is missing, did you mean/
) )
); );

View File

@@ -7,17 +7,21 @@ const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
const { inRange } = require('../../lib/is');
describe('Text to image', function () { describe('Text to image', function () {
this.retries(3); this.retries(3);
it('text with default values', async () => { it('text with default values', async function () {
const output = fixtures.path('output.text-default.png'); const output = fixtures.path('output.text-default.png');
const text = sharp({ const text = sharp({
text: { text: {
text: 'Hello, world !' text: 'Hello, world !'
} }
}); });
if (!sharp.versions.pango) {
return this.skip();
}
const info = await text.png().toFile(output); const info = await text.png().toFile(output);
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(3, info.channels); assert.strictEqual(3, info.channels);
@@ -40,22 +44,23 @@ describe('Text to image', function () {
it('text with width and height', function (done) { it('text with width and height', function (done) {
const output = fixtures.path('output.text-width-height.png'); const output = fixtures.path('output.text-width-height.png');
const maxWidth = 500;
const maxHeight = 500;
const text = sharp({ const text = sharp({
text: { text: {
text: 'Hello, world!', text: 'Hello, world!',
width: maxWidth, width: 500,
height: maxHeight height: 400
} }
}); });
if (!sharp.versions.pango) {
return this.skip();
}
text.toFile(output, function (err, info) { text.toFile(output, function (err, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(3, info.channels); assert.strictEqual(3, info.channels);
assert.ok(info.width <= maxWidth); assert.ok(inRange(info.width, 450, 550), `Actual width ${info.width}`);
assert.ok(info.height <= maxHeight); assert.ok(inRange(info.height, 300, 450), `Actual height ${info.height}`);
assert.ok(info.textAutofitDpi > 0); assert.ok(inRange(info.textAutofitDpi, 900, 1200), `Actual textAutofitDpi ${info.textAutofitDpi}`);
done(); done();
}); });
}); });
@@ -69,6 +74,9 @@ describe('Text to image', function () {
dpi dpi
} }
}); });
if (!sharp.versions.pango) {
return this.skip();
}
text.toFile(output, function (err, info) { text.toFile(output, function (err, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
@@ -90,6 +98,9 @@ describe('Text to image', function () {
dpi dpi
} }
}); });
if (!sharp.versions.pango) {
return this.skip();
}
text.toFile(output, function (err, info) { text.toFile(output, function (err, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
@@ -112,6 +123,9 @@ describe('Text to image', function () {
font: 'sans 100' font: 'sans 100'
} }
}); });
if (!sharp.versions.pango) {
return this.skip();
}
text.toFile(output, function (err, info) { text.toFile(output, function (err, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
@@ -122,7 +136,7 @@ describe('Text to image', function () {
}); });
}); });
it('text with justify and composite', done => { it('text with justify and composite', function (done) {
const output = fixtures.path('output.text-composite.png'); const output = fixtures.path('output.text-composite.png');
const width = 500; const width = 500;
const dpi = 300; const dpi = 300;
@@ -153,6 +167,9 @@ describe('Text to image', function () {
left: 30, left: 30,
top: 250 top: 250
}]); }]);
if (!sharp.versions.pango) {
return this.skip();
}
text.toFile(output, function (err, info) { text.toFile(output, function (err, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);

View File

@@ -462,6 +462,18 @@ describe('TIFF', function () {
}); });
}); });
it('TIFF miniswhite true value does not throw error', function () {
assert.doesNotThrow(function () {
sharp().tiff({ miniswhite: true });
});
});
it('Invalid TIFF miniswhite value throws error', function () {
assert.throws(function () {
sharp().tiff({ miniswhite: 'true' });
});
});
it('Invalid TIFF tile value throws error', function () { it('Invalid TIFF tile value throws error', function () {
assert.throws(function () { assert.throws(function () {
sharp().tiff({ tile: 'true' }); sharp().tiff({ tile: 'true' });

View File

@@ -741,28 +741,6 @@ describe('Tile', function () {
}); });
}); });
it('Google layout with depth onepixel', function (done) {
const directory = fixtures.path('output.google_depth_onepixel.dzi');
fs.rm(directory, { recursive: true }, function () {
sharp(fixtures.inputJpg)
.tile({
layout: 'google',
depth: 'onepixel',
size: 256
})
.toFile(directory, function (err, info) {
if (err) throw err;
assert.strictEqual('dz', info.format);
assert.strictEqual(2725, info.width);
assert.strictEqual(2225, info.height);
assert.strictEqual(3, info.channels);
assert.strictEqual('number', typeof info.size);
assertGoogleTiles(directory, 256, 13, done);
});
});
});
it('Google layout with depth onetile', function (done) { it('Google layout with depth onetile', function (done) {
const directory = fixtures.path('output.google_depth_onetile.dzi'); const directory = fixtures.path('output.google_depth_onetile.dzi');
fs.rm(directory, { recursive: true }, function () { fs.rm(directory, { recursive: true }, function () {

View File

@@ -11,7 +11,7 @@ const fixtures = require('../fixtures');
describe('Timeout', function () { describe('Timeout', function () {
it('Will timeout after 1s when performing slow blur operation', () => assert.rejects( it('Will timeout after 1s when performing slow blur operation', () => assert.rejects(
() => sharp(fixtures.inputJpg) () => sharp(fixtures.inputJpg)
.blur(100) .blur(200)
.timeout({ seconds: 1 }) .timeout({ seconds: 1 })
.toBuffer(), .toBuffer(),
/timeout: [0-9]+% complete/ /timeout: [0-9]+% complete/

View File

@@ -49,7 +49,7 @@ describe('Tint', function () {
}); });
it('tints rgb image with sepia tone', function (done) { it('tints rgb image with sepia tone', function (done) {
const output = fixtures.path('output.tint-sepia.jpg'); const output = fixtures.path('output.tint-sepia-hex.jpg');
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(320, 240, { fastShrinkOnLoad: false }) .resize(320, 240, { fastShrinkOnLoad: false })
.tint('#704214') .tint('#704214')
@@ -63,7 +63,7 @@ describe('Tint', function () {
}); });
it('tints rgb image with sepia tone with rgb colour', function (done) { it('tints rgb image with sepia tone with rgb colour', function (done) {
const output = fixtures.path('output.tint-sepia.jpg'); const output = fixtures.path('output.tint-sepia-rgb.jpg');
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(320, 240, { fastShrinkOnLoad: false }) .resize(320, 240, { fastShrinkOnLoad: false })
.tint([112, 66, 20]) .tint([112, 66, 20])

View File

@@ -46,7 +46,9 @@ describe('Trim borders', function () {
it('16-bit PNG with alpha channel', function (done) { it('16-bit PNG with alpha channel', function (done) {
sharp(fixtures.inputPngWithTransparency16bit) sharp(fixtures.inputPngWithTransparency16bit)
.resize(32, 32) .resize(32, 32)
.trim(20) .trim({
threshold: 20
})
.toBuffer(function (err, data, info) { .toBuffer(function (err, data, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
@@ -96,7 +98,9 @@ describe('Trim borders', function () {
.then(rotated30 => .then(rotated30 =>
sharp(rotated30) sharp(rotated30)
.rotate(-30) .rotate(-30)
.trim(128) .trim({
threshold: 128
})
.toBuffer({ resolveWithObject: true }) .toBuffer({ resolveWithObject: true })
.then(({ info }) => { .then(({ info }) => {
assert.strictEqual(20, info.width); assert.strictEqual(20, info.width);
@@ -198,49 +202,26 @@ describe('Trim borders', function () {
assert.strictEqual(trimOffsetLeft, 0); assert.strictEqual(trimOffsetLeft, 0);
}); });
describe('Valid parameters', function () { it('Works with line-art', async () => {
const expected = fixtures.expected('alpha-layer-1-fill-trim-resize.png'); const { info } = await sharp(fixtures.inputJpgOverlayLayer2)
Object.entries({ .trim({ lineArt: true })
'Background and threshold default': undefined, .toBuffer({ resolveWithObject: true });
'Background string': '#00000000',
'Background option': { assert.strictEqual(info.trimOffsetTop, -552);
background: '#00000000'
},
'Threshold number': 10,
'Threshold option': {
threshold: 10
}
}).forEach(function ([description, parameter]) {
it(description, function (done) {
sharp(fixtures.inputPngOverlayLayer1)
.resize(450, 322)
.trim(parameter)
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
assert.strictEqual(450, info.width);
assert.strictEqual(322, info.height);
assert.strictEqual(-204, info.trimOffsetLeft);
assert.strictEqual(0, info.trimOffsetTop);
fixtures.assertSimilar(expected, data, done);
});
});
});
}); });
describe('Invalid parameters', function () { describe('Invalid parameters', function () {
Object.entries({ Object.entries({
'Invalid background string': 'fail', 'Invalid string': 'fail',
'Invalid background option': { 'Invalid background option': {
background: 'fail' background: 'fail'
}, },
'Negative threshold number': -1,
'Negative threshold option': { 'Negative threshold option': {
threshold: -1 threshold: -1
}, },
'Invalid lineArt': {
Boolean: false lineArt: 'fail'
}
}).forEach(function ([description, parameter]) { }).forEach(function ([description, parameter]) {
it(description, function () { it(description, function () {
assert.throws(function () { assert.throws(function () {
@@ -253,7 +234,9 @@ describe('Trim borders', function () {
describe('Specific background colour', function () { describe('Specific background colour', function () {
it('Doesn\'t trim at all', async () => { it('Doesn\'t trim at all', async () => {
const { info } = await sharp(fixtures.inputPngTrimSpecificColour) const { info } = await sharp(fixtures.inputPngTrimSpecificColour)
.trim('yellow') .trim({
background: 'yellow'
})
.toBuffer({ resolveWithObject: true }); .toBuffer({ resolveWithObject: true });
const { width, height, trimOffsetTop, trimOffsetLeft } = info; const { width, height, trimOffsetTop, trimOffsetLeft } = info;
@@ -265,7 +248,9 @@ describe('Trim borders', function () {
it('Only trims the bottom', async () => { it('Only trims the bottom', async () => {
const { info } = await sharp(fixtures.inputPngTrimSpecificColour) const { info } = await sharp(fixtures.inputPngTrimSpecificColour)
.trim('#21468B') .trim({
background: '#21468B'
})
.toBuffer({ resolveWithObject: true }); .toBuffer({ resolveWithObject: true });
const { width, height, trimOffsetTop, trimOffsetLeft } = info; const { width, height, trimOffsetTop, trimOffsetLeft } = info;
@@ -277,7 +262,9 @@ describe('Trim borders', function () {
it('Only trims the bottom, in 16-bit', async () => { it('Only trims the bottom, in 16-bit', async () => {
const { info } = await sharp(fixtures.inputPngTrimSpecificColour16bit) const { info } = await sharp(fixtures.inputPngTrimSpecificColour16bit)
.trim('#21468B') .trim({
background: '#21468B'
})
.toBuffer({ resolveWithObject: true }); .toBuffer({ resolveWithObject: true });
const { width, height, trimOffsetTop, trimOffsetLeft } = info; const { width, height, trimOffsetTop, trimOffsetLeft } = info;
@@ -289,7 +276,9 @@ describe('Trim borders', function () {
it('Only trims the bottom, including alpha', async () => { it('Only trims the bottom, including alpha', async () => {
const { info } = await sharp(fixtures.inputPngTrimSpecificColourIncludeAlpha) const { info } = await sharp(fixtures.inputPngTrimSpecificColourIncludeAlpha)
.trim('#21468B80') .trim({
background: '#21468B80'
})
.toBuffer({ resolveWithObject: true }); .toBuffer({ resolveWithObject: true });
const { width, height, trimOffsetTop, trimOffsetLeft } = info; const { width, height, trimOffsetTop, trimOffsetLeft } = info;