Compare commits

..

17 Commits

Author SHA1 Message Date
Lovell Fuller
ca52894651 Release v0.23.2 2019-10-28 18:21:11 +00:00
Lovell Fuller
4009acdd30 Docs: support added for Node.js 13 2019-10-28 18:19:33 +00:00
Lovell Fuller
147c93ecd3 Tests: increase coverage for jpeg-related logic 2019-10-27 20:03:10 +00:00
Lovell Fuller
8e04e4b07f Tests: add coverage for tiff quality option 2019-10-27 19:54:23 +00:00
Lovell Fuller
e7413ea1e5 Tests: increase coverage for metadata-related logic 2019-10-27 19:32:48 +00:00
Lovell Fuller
220bb03a32 Switch from excl npmignore to incl package.json files 2019-10-27 18:22:10 +00:00
Lovell Fuller
20f512fe5f Bump dependency patch versions 2019-10-26 23:15:41 +01:00
Lovell Fuller
efb3523eaa Remove duplicate validation from resize background 2019-10-26 23:13:12 +01:00
Lovell Fuller
2f2276e091 Changelog entries for #1924 #1932 2019-10-26 22:53:39 +01:00
Paul Neave
08a6597626 Add background option to tile output operation (#1924) 2019-10-25 14:30:33 +01:00
Nicolas Stepien
d82a6ee4fc Add Node.js 13 to CI (#1932) 2019-10-23 19:28:19 +01:00
Lovell Fuller
e627f6d68d Docs: clarify that input 'path' refers to the filesystem 2019-10-05 08:43:40 +01:00
Lovell Fuller
e650f58bd8 Improve error messaging for root/sudo permission problems 2019-10-04 12:14:08 +01:00
Lovell Fuller
5a9b6c8afd Tighten validation of page/pages constructor options 2019-10-03 16:41:32 +01:00
Lovell Fuller
075771d1e9 Improve error messaging for 404 errors on non-standard platforms 2019-10-03 15:32:15 +01:00
Lovell Fuller
4fcf091fef Bump tar dep (appears to be non-breaking despite major increment) 2019-10-03 15:30:56 +01:00
Marc Bornträger
0e66454fe4 Docs: Simplify Alpine Linux info, uses 3.10 instead of edge 2019-10-01 12:00:38 +01:00
22 changed files with 187 additions and 44 deletions

View File

@@ -11,7 +11,7 @@ Did you see the [documentation relating to installation](https://sharp.pixelplum
Have you ensured the platform and version of Node.js used for `npm install` is the same as the platform and version of Node.js used at runtime?
If you're (mis)using `sudo npm install` have you tried with the `sudo npm install --unsafe-perm` flag?
If you are installing as a `root` or `sudo` user, have you tried with the `npm install --unsafe-perm` flag?
What is the complete output of running `npm install --verbose sharp`? Have you checked this output for useful error messages?

View File

@@ -1,16 +0,0 @@
build
node_modules
coverage
.editorconfig
.gitattributes
.gitignore
test
.travis.yml
appveyor.yml
mkdocs.yml
docs/css/
vendor
.prebuildrc
.nyc_output
.github/
.vscode/

View File

@@ -18,6 +18,12 @@ matrix:
sudo: false
language: node_js
node_js: "12"
- name: "Linux (glibc) - Node 13"
os: linux
dist: trusty
sudo: false
language: node_js
node_js: "13"
after_success:
- npm install coveralls
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
@@ -51,6 +57,16 @@ matrix:
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux (musl) - Node 13"
os: linux
dist: trusty
sudo: true
language: minimal
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:13.0-alpine
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "OS X - Node 8"
os: osx
osx_image: xcode9.2
@@ -66,3 +82,8 @@ matrix:
osx_image: xcode9.2
language: node_js
node_js: "12"
- name: "OS X - Node 13"
os: osx
osx_image: xcode10
language: node_js
node_js: "13"

View File

@@ -21,7 +21,7 @@ As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
Most modern 64-bit OS X, Windows and Linux systems running
Node versions 8, 10 and 12
Node versions 8, 10, 12 and 13
do not require any additional install or runtime dependencies.
## Examples

View File

@@ -7,6 +7,7 @@ environment:
- nodejs_version: "8"
- nodejs_version: "10"
- nodejs_version: "12"
- nodejs_version: "13"
install:
- ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) x64
- npm install -g npm@6

View File

@@ -6,7 +6,7 @@
- `input` **([Buffer][1] \| [String][2])?** if present, can be
a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
a String containing the filesystem path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
- `options` **[Object][3]?** if present, is an Object with optional attributes.
- `options.failOnError` **[Boolean][4]** by default halt processing and raise an error when loading invalid images.

View File

@@ -303,7 +303,7 @@ const data = await sharp(input)
- Throws **[Error][4]** unsupported format or options
Returns **Sharp**
Returns **Sharp**
## tile
@@ -315,10 +315,11 @@ Warning: multiple sharp instances concurrently producing tile output can expose
### Parameters
- `options` **[Object][6]?**
- `options` **[Object][6]?**
- `options.size` **[Number][9]** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
- `options.overlap` **[Number][9]** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
- `options.angle` **[Number][9]** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
- `options.background` **([String][2] \| [Object][6])** background colour, parsed by the [color][10] module, defaults to white without transparency. (optional, default `{r:255,g:255,b:255,alpha:1}`)
- `options.depth` **[String][2]?** how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
- `options.skipBlanks` **[Number][9]** threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images (optional, default `-1`)
- `options.container` **[String][2]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
@@ -359,3 +360,5 @@ Returns **Sharp**
[8]: https://nodejs.org/api/buffer.html
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[10]: https://www.npmjs.org/package/color

View File

@@ -4,6 +4,16 @@
Requires libvips v8.8.1.
#### v0.23.2 - 28<sup>th</sup> October 2019
* Add `background` option to tile output operation.
[#1924](https://github.com/lovell/sharp/pull/1924)
[@neave](https://github.com/neave)
* Add support for Node.js 13.
[#1932](https://github.com/lovell/sharp/pull/1932)
[@MayhemYDG](https://github.com/MayhemYDG)
#### v0.23.1 - 26<sup>th</sup> September 2019
* Ensure `sharp.format.vips` is present and correct (filesystem only).

View File

@@ -17,7 +17,7 @@ As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
Most modern 64-bit OS X, Windows and Linux systems running
Node versions 8, 10 and 12
Node versions 8, 10, 12 and 13
do not require any additional install or runtime dependencies.
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)

View File

@@ -15,7 +15,7 @@ yarn add sharp
### Building from source
Pre-compiled binaries for sharp are provided for use with
Node versions 8, 10 and 12 on
Node versions 8, 10, 12 and 13 on
64-bit Windows, OS X and Linux platforms.
Sharp will be built from source at install time when:
@@ -74,9 +74,8 @@ libvips is available in the
[community repository](https://pkgs.alpinelinux.org/packages?name=vips-dev):
```sh
apk add vips-dev fftw-dev build-base --update-cache \
--repository https://alpine.global.ssl.fastly.net/alpine/edge/community/ \
--repository https://alpine.global.ssl.fastly.net/alpine/edge/main
apk add --upgrade --no-cache vips-dev build-base \
--repository https://alpine.global.ssl.fastly.net/alpine/v3.10/community/
```
The smaller stack size of musl libc means

View File

@@ -19,6 +19,9 @@ const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SH
const fail = function (err) {
npmLog.error('sharp', err.message);
if (err.code === 'EACCES') {
npmLog.info('sharp', 'Are you trying to install as a root or sudo user? Try again with the --unsafe-perm flag');
}
npmLog.info('sharp', 'Attempting to build from source via node-gyp but this may fail due to the above error');
npmLog.info('sharp', 'Please see https://sharp.pixelplumbing.com/page/install for required dependencies');
process.exit(1);
@@ -79,14 +82,16 @@ try {
npmLog.info('sharp', `Downloading ${url}`);
simpleGet({ url: url, agent: agent() }, function (err, response) {
if (err) {
throw err;
fail(err);
} else if (response.statusCode === 404) {
fail(new Error(`Prebuilt libvips binaries are not yet available for ${platformAndArch}`));
} else if (response.statusCode !== 200) {
fail(new Error(`Status ${response.statusCode} ${response.statusMessage}`));
} else {
response
.on('error', fail)
.pipe(tmpFile);
}
if (response.statusCode !== 200) {
throw new Error(`Status ${response.statusCode}`);
}
response
.on('error', fail)
.pipe(tmpFile);
});
tmpFile
.on('error', fail)

View File

@@ -78,7 +78,7 @@ const debuglog = util.debuglog('sharp');
*
* @param {(Buffer|String)} [input] - if present, can be
* a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
* a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
* a String containing the filesystem path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
* JPEG, PNG, WebP, 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 {Boolean} [options.failOnError=true] - by default halt processing and raise an error when loading invalid images.
@@ -213,6 +213,7 @@ const Sharp = function (input, options) {
tileSize: 256,
tileOverlap: 0,
tileSkipBlanks: -1,
tileBackground: [255, 255, 255, 255],
linearA: 1,
linearB: 0,
// Function to notify of libvips warnings

View File

@@ -65,11 +65,15 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
if (is.defined(inputOptions.pages)) {
if (is.integer(inputOptions.pages) && is.inRange(inputOptions.pages, -1, 100000)) {
inputDescriptor.pages = inputOptions.pages;
} else {
throw is.invalidParameterError('pages', 'integer between -1 and 100000', inputOptions.pages);
}
}
if (is.defined(inputOptions.page)) {
if (is.integer(inputOptions.page) && is.inRange(inputOptions.page, 0, 100000)) {
inputDescriptor.page = inputOptions.page;
} else {
throw is.invalidParameterError('page', 'integer between 0 and 100000', inputOptions.page);
}
}
// Create new image

View File

@@ -551,6 +551,7 @@ function toFormat (format, options) {
* @param {Number} [options.size=256] tile size in pixels, a value between 1 and 8192.
* @param {Number} [options.overlap=0] tile overlap in pixels, a value between 0 and 8192.
* @param {Number} [options.angle=0] tile angle of rotation, must be a multiple of 90.
* @param {String|Object} [options.background={r: 255, g: 255, b: 255, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to white without transparency.
* @param {String} [options.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
* @param {Number} [options.skipBlanks=-1] threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images
* @param {String} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
@@ -603,6 +604,8 @@ function tile (options) {
throw is.invalidParameterError('angle', 'positive/negative multiple of 90', options.angle);
}
}
// Background colour
this._setBackgroundColourOption('tileBackground', options.background);
// Depth of tiles
if (is.defined(options.depth)) {
if (is.string(options.depth) && is.inArray(options.depth, ['onepixel', 'onetile', 'one'])) {

View File

@@ -246,9 +246,7 @@ function resize (width, height, options) {
}
}
// Background
if (is.defined(options.background)) {
this._setBackgroundColourOption('resizeBackground', options.background);
}
this._setBackgroundColourOption('resizeBackground', options.background);
// Kernel
if (is.defined(options.kernel)) {
if (is.string(kernel[options.kernel])) {

View File

@@ -1,7 +1,7 @@
{
"name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"version": "0.23.1",
"version": "0.23.2",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [
@@ -62,7 +62,8 @@
"Michael B. Klein <mbklein@gmail.com>",
"Jordan Prudhomme <jordan@raboland.fr>",
"Ilya Ovdin <iovdin@gmail.com>",
"Andargor <andargor@yahoo.com>"
"Andargor <andargor@yahoo.com>",
"Paul Neave <paul.neave@gmail.com>"
],
"scripts": {
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
@@ -75,6 +76,14 @@
"docs": "for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md --markdown-toc=false lib/$m.js >docs/api-$m.md; done"
},
"main": "lib/index.js",
"files": [
"binding.gyp",
"docs/**",
"!docs/css/**",
"install/**",
"lib/**",
"src/**"
],
"repository": {
"type": "git",
"url": "git://github.com/lovell/sharp"
@@ -103,7 +112,7 @@
"prebuild-install": "^5.3.2",
"semver": "^6.3.0",
"simple-get": "^3.1.0",
"tar": "^4.4.13",
"tar": "^5.0.5",
"tunnel-agent": "^0.6.0"
},
"devDependencies": {
@@ -111,13 +120,13 @@
"cc": "^1.0.2",
"decompress-zip": "^0.3.2",
"documentation": "^12.1.2",
"exif-reader": "^1.0.2",
"exif-reader": "^1.0.3",
"icc": "^1.0.0",
"license-checker": "^25.0.1",
"mocha": "^6.2.0",
"mock-fs": "^4.10.1",
"mocha": "^6.2.2",
"mock-fs": "^4.10.2",
"nyc": "^14.1.1",
"prebuild": "^9.1.0",
"prebuild": "^9.1.1",
"prebuild-ci": "^3.1.0",
"rimraf": "^3.0.0",
"semistandard": "^14.2.0"

View File

@@ -959,6 +959,11 @@ class PipelineWorker : public Nan::AsyncWorker {
};
suffix = AssembleSuffixString(extname, options);
}
// Remove alpha channel from tile background if image does not contain an alpha channel
if (!HasAlpha(image)) {
baton->tileBackground.pop_back();
}
// Write DZ to file
vips::VOption *options = VImage::option()
->set("strip", !baton->withMetadata)
@@ -968,6 +973,7 @@ class PipelineWorker : public Nan::AsyncWorker {
->set("layout", baton->tileLayout)
->set("suffix", const_cast<char*>(suffix.data()))
->set("angle", CalculateAngleRotation(baton->tileAngle))
->set("background", baton->tileBackground)
->set("skip_blanks", baton->tileSkipBlanks);
// libvips chooses a default depth based on layout. Instead of replicating that logic here by
@@ -1374,6 +1380,7 @@ NAN_METHOD(pipeline) {
baton->tileOverlap = AttrTo<uint32_t>(options, "tileOverlap");
std::string tileContainer = AttrAsStr(options, "tileContainer");
baton->tileAngle = AttrTo<int32_t>(options, "tileAngle");
baton->tileBackground = AttrAsRgba(options, "tileBackground");
baton->tileSkipBlanks = AttrTo<int32_t>(options, "tileSkipBlanks");
if (tileContainer == "zip") {
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;

View File

@@ -175,6 +175,7 @@ struct PipelineBaton {
VipsForeignDzLayout tileLayout;
std::string tileFormat;
int tileAngle;
std::vector<double> tileBackground;
int tileSkipBlanks;
VipsForeignDzDepth tileDepth;
std::unique_ptr<double[]> recombMatrix;
@@ -280,6 +281,7 @@ struct PipelineBaton {
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ),
tileAngle(0),
tileBackground{ 255.0, 255.0, 255.0, 255.0 },
tileSkipBlanks(-1),
tileDepth(VIPS_FOREIGN_DZ_DEPTH_LAST) {}
};

View File

@@ -648,6 +648,16 @@ describe('Input/output', function () {
it('Ignore unknown attribute', function () {
sharp(null, { unknown: true });
});
it('Invalid page property throws', function () {
assert.throws(function () {
sharp(null, { page: -1 });
}, /Expected integer between 0 and 100000 for page but received -1 of type number/);
});
it('Invalid pages property throws', function () {
assert.throws(function () {
sharp(null, { pages: '1' });
}, /Expected integer between -1 and 100000 for pages but received 1 of type string/);
});
});
describe('create new image', function () {

View File

@@ -259,4 +259,35 @@ describe('JPEG', function () {
});
});
});
it('Specifying quantization table provides different JPEG', function (done) {
// First generate with default quantization table
sharp(fixtures.inputJpg)
.resize(320, 240)
.jpeg({ optimiseCoding: false })
.toBuffer(function (err, withDefaultQuantizationTable, withInfo) {
if (err) throw err;
assert.strictEqual(true, withDefaultQuantizationTable.length > 0);
assert.strictEqual(withDefaultQuantizationTable.length, withInfo.size);
assert.strictEqual('jpeg', withInfo.format);
assert.strictEqual(320, withInfo.width);
assert.strictEqual(240, withInfo.height);
// Then generate with different quantization table
sharp(fixtures.inputJpg)
.resize(320, 240)
.jpeg({ optimiseCoding: false, quantizationTable: 3 })
.toBuffer(function (err, withQuantTable3, withoutInfo) {
if (err) throw err;
assert.strictEqual(true, withQuantTable3.length > 0);
assert.strictEqual(withQuantTable3.length, withoutInfo.size);
assert.strictEqual('jpeg', withoutInfo.format);
assert.strictEqual(320, withoutInfo.width);
assert.strictEqual(240, withoutInfo.height);
// Verify image is same (as mozjpeg may not be present) size or less
assert.strictEqual(true, withQuantTable3.length <= withDefaultQuantizationTable.length);
done();
});
});
});
});

View File

@@ -307,6 +307,20 @@ describe('Image metadata', function () {
readable.pipe(pipeline);
});
it('Stream in, rejected Promise out', () => {
const pipeline = sharp();
fs
.createReadStream(__filename)
.pipe(pipeline);
return pipeline
.metadata()
.then(
() => Promise.reject(new Error('Expected metadata to reject')),
err => assert.strictEqual(err.message, 'Input buffer contains unsupported image format')
);
});
it('Stream', function (done) {
const readable = fs.createReadStream(fixtures.inputJpg);
const pipeline = sharp().metadata(function (err, metadata) {
@@ -390,6 +404,34 @@ describe('Image metadata', function () {
});
});
it('Include metadata in output, enabled via empty object', () =>
sharp(fixtures.inputJpgWithExif)
.withMetadata({})
.toBuffer()
.then((buffer) => sharp(buffer)
.metadata()
.then(metadata => {
assert.strictEqual(true, metadata.hasProfile);
assert.strictEqual(8, metadata.orientation);
assert.strictEqual('object', typeof metadata.exif);
assert.strictEqual(true, metadata.exif instanceof Buffer);
// EXIF
const exif = exifReader(metadata.exif);
assert.strictEqual('object', typeof exif);
assert.strictEqual('object', typeof exif.image);
assert.strictEqual('number', typeof exif.image.XResolution);
// ICC
assert.strictEqual('object', typeof metadata.icc);
assert.strictEqual(true, metadata.icc instanceof Buffer);
const profile = icc.parse(metadata.icc);
assert.strictEqual('object', typeof profile);
assert.strictEqual('RGB', profile.colorSpace);
assert.strictEqual('Perceptual', profile.intent);
assert.strictEqual('Monitor', profile.deviceClass);
})
)
);
it('Remove EXIF metadata after a resize', function (done) {
sharp(fixtures.inputJpgWithExif)
.resize(320, 240)

View File

@@ -85,6 +85,19 @@ describe('TIFF', function () {
});
});
it('Increasing TIFF quality increases file size', () =>
sharp(fixtures.inputJpgWithLandscapeExif1)
.tiff({ quality: 40 })
.toBuffer()
.then(tiff40 => sharp(fixtures.inputJpgWithLandscapeExif1)
.tiff({ quality: 90 })
.toBuffer()
.then(tiff90 =>
assert.strictEqual(true, tiff40.length < tiff90.length)
)
)
);
it('Invalid TIFF quality throws error', function () {
assert.throws(function () {
sharp().tiff({ quality: 101 });