Compare commits

...

7 Commits

Author SHA1 Message Date
Lovell Fuller
0144358afb Release v0.20.8 2018-09-05 08:44:01 +01:00
Lovell Fuller
136097efe7 Downgrade nyc for continued Node 4 support 2018-09-04 17:07:10 +01:00
Lovell Fuller
374c6959d7 Changelog and credit for #1358 #1362 2018-09-04 16:39:24 +01:00
Axel Eirola
7d48a5ccf4 Allow floating point density input (#1362)
Metadata output will still remain integer
2018-09-01 08:58:30 +01:00
ajhool
bf3254cb16 Install: avoid race conditions when creating directories (#1358) 2018-08-29 09:20:26 +01:00
Lovell Fuller
5bed3a7d52 Release v0.20.7 2018-08-21 11:50:14 +01:00
Lovell Fuller
ece111280b Use copy+unlink if rename fails during install #1345 2018-08-20 15:14:31 +01:00
16 changed files with 111 additions and 36 deletions

View File

@@ -18,7 +18,7 @@ If the overlay image contains an alpha channel then composition with premultipli
- `options.left` **[Number][4]?** the pixel offset from the left edge. - `options.left` **[Number][4]?** the pixel offset from the left edge.
- `options.tile` **[Boolean][5]** set to true to repeat the overlay image across the entire image with the given `gravity`. (optional, default `false`) - `options.tile` **[Boolean][5]** set to true to repeat the overlay image across the entire image with the given `gravity`. (optional, default `false`)
- `options.cutout` **[Boolean][5]** set to true to apply only the alpha channel of the overlay image to the input image, giving the appearance of one image being cut out of another. (optional, default `false`) - `options.cutout` **[Boolean][5]** set to true to apply only the alpha channel of the overlay image to the input image, giving the appearance of one image being cut out of another. (optional, default `false`)
- `options.density` **[Number][4]** integral number representing the DPI for vector overlay image. (optional, default `72`) - `options.density` **[Number][4]** number representing the DPI for vector overlay image. (optional, default `72`)
- `options.raw` **[Object][3]?** describes overlay when using raw pixel data. - `options.raw` **[Object][3]?** describes overlay when using raw pixel data.
- `options.raw.width` **[Number][4]?** - `options.raw.width` **[Number][4]?**
- `options.raw.height` **[Number][4]?** - `options.raw.height` **[Number][4]?**

View File

@@ -12,7 +12,7 @@
- `options.failOnError` **[Boolean][4]** by default apply a "best effort" - `options.failOnError` **[Boolean][4]** by default apply a "best effort"
to decode images, even if the data is corrupt or invalid. Set this flag to true to decode images, even if the data is corrupt or invalid. Set this flag to true
if you'd rather halt processing and raise an error when loading invalid images. (optional, default `false`) if you'd rather halt processing and raise an error when loading invalid images. (optional, default `false`)
- `options.density` **[Number][5]** integral number representing the DPI for vector images. (optional, default `72`) - `options.density` **[Number][5]** number representing the DPI for vector images. (optional, default `72`)
- `options.page` **[Number][5]** page number to extract for multi-page input (GIF, TIFF) (optional, default `0`) - `options.page` **[Number][5]** page number to extract for multi-page input (GIF, TIFF) (optional, default `0`)
- `options.raw` **[Object][3]?** describes raw pixel input image data. See `raw()` for pixel ordering. - `options.raw` **[Object][3]?** describes raw pixel input image data. See `raw()` for pixel ordering.
- `options.raw.width` **[Number][5]?** - `options.raw.width` **[Number][5]?**

View File

@@ -4,6 +4,21 @@
Requires libvips v8.6.1. Requires libvips v8.6.1.
#### v0.20.8 - 5<sup>th</sup> September 2018
* Avoid race conditions when creating directories during installation.
[#1358](https://github.com/lovell/sharp/pull/1358)
[@ajhool](https://github.com/ajhool)
* Accept floating point values for input density parameter.
[#1362](https://github.com/lovell/sharp/pull/1362)
[@aeirola](https://github.com/aeirola)
#### v0.20.7 - 21<sup>st</sup> August 2018
* Use copy+unlink if rename operation fails during installation.
[#1345](https://github.com/lovell/sharp/issues/1345)
#### v0.20.6 - 20<sup>th</sup> August 2018 #### v0.20.6 - 20<sup>th</sup> August 2018
* Add removeAlpha operation to remove alpha channel, if any. * Add removeAlpha operation to remove alpha channel, if any.

View File

@@ -116,6 +116,8 @@ the help and code contributions of the following people:
* [Espen Hovlandsdal](https://github.com/rexxars) * [Espen Hovlandsdal](https://github.com/rexxars)
* [Sylvain Dumont](https://github.com/sylvaindumont) * [Sylvain Dumont](https://github.com/sylvaindumont)
* [Alun Davies](https://github.com/alundavies) * [Alun Davies](https://github.com/alundavies)
* [Aidan Hoolachan](https://github.com/ajhool)
* [Axel Eirola](https://github.com/aeirola)
Thank you! Thank you!

View File

@@ -4,6 +4,7 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const copyFileSync = require('fs-copy-file-sync'); const copyFileSync = require('fs-copy-file-sync');
const libvips = require('../lib/libvips');
const npmLog = require('npmlog'); const npmLog = require('npmlog');
if (process.platform === 'win32') { if (process.platform === 'win32') {
@@ -11,8 +12,8 @@ if (process.platform === 'win32') {
const buildReleaseDir = path.join(buildDir, 'Release'); const buildReleaseDir = path.join(buildDir, 'Release');
npmLog.info('sharp', `Creating ${buildReleaseDir}`); npmLog.info('sharp', `Creating ${buildReleaseDir}`);
try { try {
fs.mkdirSync(buildDir); libvips.mkdirSync(buildDir);
fs.mkdirSync(buildReleaseDir); libvips.mkdirSync(buildReleaseDir);
} catch (err) {} } catch (err) {}
const vendorLibDir = path.join(__dirname, '..', 'vendor', 'lib'); const vendorLibDir = path.join(__dirname, '..', 'vendor', 'lib');
npmLog.info('sharp', `Copying DLLs from ${vendorLibDir} to ${buildReleaseDir}`); npmLog.info('sharp', `Copying DLLs from ${vendorLibDir} to ${buildReleaseDir}`);

View File

@@ -9,6 +9,7 @@ const npmLog = require('npmlog');
const semver = require('semver'); const semver = require('semver');
const simpleGet = require('simple-get'); const simpleGet = require('simple-get');
const tar = require('tar'); const tar = require('tar');
const copyFileSync = require('fs-copy-file-sync');
const agent = require('../lib/agent'); const agent = require('../lib/agent');
const libvips = require('../lib/libvips'); const libvips = require('../lib/libvips');
@@ -19,9 +20,7 @@ const distBaseUrl = process.env.SHARP_DIST_BASE_URL || `https://github.com/lovel
const extractTarball = function (tarPath) { const extractTarball = function (tarPath) {
const vendorPath = path.join(__dirname, '..', 'vendor'); const vendorPath = path.join(__dirname, '..', 'vendor');
if (!fs.existsSync(vendorPath)) { libvips.mkdirSync(vendorPath);
fs.mkdirSync(vendorPath);
}
tar tar
.extract({ .extract({
file: tarPath, file: tarPath,
@@ -82,7 +81,14 @@ try {
response.pipe(tmpFile); response.pipe(tmpFile);
}); });
tmpFile.on('close', function () { tmpFile.on('close', function () {
fs.renameSync(tarPathTemp, tarPathCache); try {
// Attempt to rename
fs.renameSync(tarPathTemp, tarPathCache);
} catch (err) {
// Fall back to copy and unlink
copyFileSync(tarPathTemp, tarPathCache);
fs.unlinkSync(tarPathTemp);
}
extractTarball(tarPathCache); extractTarball(tarPathCache);
}); });
} }

View File

@@ -34,7 +34,7 @@ const is = require('./is');
* @param {Number} [options.left] - the pixel offset from the left edge. * @param {Number} [options.left] - the pixel offset from the left edge.
* @param {Boolean} [options.tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`. * @param {Boolean} [options.tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
* @param {Boolean} [options.cutout=false] - set to true to apply only the alpha channel of the overlay image to the input image, giving the appearance of one image being cut out of another. * @param {Boolean} [options.cutout=false] - set to true to apply only the alpha channel of the overlay image to the input image, giving the appearance of one image being cut out of another.
* @param {Number} [options.density=72] - integral number representing the DPI for vector overlay image. * @param {Number} [options.density=72] - number representing the DPI for vector overlay image.
* @param {Object} [options.raw] - describes overlay when using raw pixel data. * @param {Object} [options.raw] - describes overlay when using raw pixel data.
* @param {Number} [options.raw.width] * @param {Number} [options.raw.width]
* @param {Number} [options.raw.height] * @param {Number} [options.raw.height]

View File

@@ -96,7 +96,7 @@ const debuglog = util.debuglog('sharp');
* @param {Boolean} [options.failOnError=false] - by default apply a "best effort" * @param {Boolean} [options.failOnError=false] - by default apply a "best effort"
* to decode images, even if the data is corrupt or invalid. Set this flag to true * to decode images, even if the data is corrupt or invalid. Set this flag to true
* if you'd rather halt processing and raise an error when loading invalid images. * if you'd rather halt processing and raise an error when loading invalid images.
* @param {Number} [options.density=72] - integral number representing the DPI for vector images. * @param {Number} [options.density=72] - number representing the DPI for vector images.
* @param {Number} [options.page=0] - page number to extract for multi-page input (GIF, TIFF) * @param {Number} [options.page=0] - page number to extract for multi-page input (GIF, TIFF)
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering. * @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
* @param {Number} [options.raw.width] * @param {Number} [options.raw.width]

View File

@@ -36,7 +36,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
} }
// Density // Density
if (is.defined(inputOptions.density)) { if (is.defined(inputOptions.density)) {
if (is.integer(inputOptions.density) && is.inRange(inputOptions.density, 1, 2400)) { if (is.inRange(inputOptions.density, 1, 2400)) {
inputDescriptor.density = inputOptions.density; inputDescriptor.density = inputOptions.density;
} else { } else {
throw new Error('Invalid density (1 to 2400) ' + inputOptions.density); throw new Error('Invalid density (1 to 2400) ' + inputOptions.density);

View File

@@ -15,15 +15,21 @@ const spawnSyncOptions = {
shell: true shell: true
}; };
const mkdirSync = function (dirPath) {
try {
fs.mkdirSync(dirPath);
} catch (err) {
if (err.code !== 'EEXIST') {
throw err;
}
}
};
const cachePath = function () { const cachePath = function () {
const npmCachePath = env.npm_config_cache || (env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(os.homedir(), '.npm')); const npmCachePath = env.npm_config_cache || (env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(os.homedir(), '.npm'));
if (!fs.existsSync(npmCachePath)) { mkdirSync(npmCachePath);
fs.mkdirSync(npmCachePath);
}
const libvipsCachePath = path.join(npmCachePath, '_libvips'); const libvipsCachePath = path.join(npmCachePath, '_libvips');
if (!fs.existsSync(libvipsCachePath)) { mkdirSync(libvipsCachePath);
fs.mkdirSync(libvipsCachePath);
}
return libvipsCachePath; return libvipsCachePath;
}; };
@@ -78,5 +84,6 @@ module.exports = {
globalLibvipsVersion: globalLibvipsVersion, globalLibvipsVersion: globalLibvipsVersion,
hasVendoredLibvips: hasVendoredLibvips, hasVendoredLibvips: hasVendoredLibvips,
pkgConfigPath: pkgConfigPath, pkgConfigPath: pkgConfigPath,
useGlobalLibvips: useGlobalLibvips useGlobalLibvips: useGlobalLibvips,
mkdirSync: mkdirSync
}; };

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 and TIFF images", "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"version": "0.20.6", "version": "0.20.8",
"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": [
@@ -52,7 +52,9 @@
"Tom Lokhorst <tom@lokhorst.eu>", "Tom Lokhorst <tom@lokhorst.eu>",
"Espen Hovlandsdal <espen@hovlandsdal.com>", "Espen Hovlandsdal <espen@hovlandsdal.com>",
"Sylvain Dumont <sylvain.dumont35@gmail.com>", "Sylvain Dumont <sylvain.dumont35@gmail.com>",
"Alun Davies <alun.owain.davies@googlemail.com>" "Alun Davies <alun.owain.davies@googlemail.com>",
"Aidan Hoolachan <ajhoolachan21@gmail.com>",
"Axel Eirola <axel.eirola@iki.fi>"
], ],
"scripts": { "scripts": {
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)", "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
@@ -86,7 +88,7 @@
"dependencies": { "dependencies": {
"color": "^3.0.0", "color": "^3.0.0",
"detect-libc": "^1.0.3", "detect-libc": "^1.0.3",
"nan": "^2.10.0", "nan": "^2.11.0",
"fs-copy-file-sync": "^1.1.1", "fs-copy-file-sync": "^1.1.1",
"npmlog": "^4.1.2", "npmlog": "^4.1.2",
"prebuild-install": "^4.0.0", "prebuild-install": "^4.0.0",
@@ -99,11 +101,12 @@
"async": "^2.6.1", "async": "^2.6.1",
"cc": "^1.0.2", "cc": "^1.0.2",
"decompress-zip": "^0.3.1", "decompress-zip": "^0.3.1",
"documentation": "^8.1.1", "documentation": "^8.1.2",
"exif-reader": "^1.0.2", "exif-reader": "^1.0.2",
"icc": "^1.0.0", "icc": "^1.0.0",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"nyc": "^12.0.2", "mock-fs": "^4.6.0",
"nyc": "^12.0.1",
"prebuild": "^7.6.2", "prebuild": "^7.6.2",
"prebuild-ci": "^2.2.3", "prebuild-ci": "^2.2.3",
"rimraf": "^2.6.2", "rimraf": "^2.6.2",

View File

@@ -55,7 +55,7 @@ namespace sharp {
descriptor->failOnError = AttrTo<bool>(input, "failOnError"); descriptor->failOnError = AttrTo<bool>(input, "failOnError");
// Density for vector-based input // Density for vector-based input
if (HasAttr(input, "density")) { if (HasAttr(input, "density")) {
descriptor->density = AttrTo<uint32_t>(input, "density"); descriptor->density = AttrTo<double>(input, "density");
} }
// Raw pixel input // Raw pixel input
if (HasAttr(input, "rawChannels")) { if (HasAttr(input, "rawChannels")) {
@@ -228,7 +228,7 @@ namespace sharp {
->set("access", accessMethod) ->set("access", accessMethod)
->set("fail", descriptor->failOnError); ->set("fail", descriptor->failOnError);
if (imageType == ImageType::SVG || imageType == ImageType::PDF) { if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
option->set("dpi", static_cast<double>(descriptor->density)); option->set("dpi", descriptor->density);
} }
if (imageType == ImageType::MAGICK) { if (imageType == ImageType::MAGICK) {
option->set("density", std::to_string(descriptor->density).data()); option->set("density", std::to_string(descriptor->density).data());
@@ -270,7 +270,7 @@ namespace sharp {
->set("access", accessMethod) ->set("access", accessMethod)
->set("fail", descriptor->failOnError); ->set("fail", descriptor->failOnError);
if (imageType == ImageType::SVG || imageType == ImageType::PDF) { if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
option->set("dpi", static_cast<double>(descriptor->density)); option->set("dpi", descriptor->density);
} }
if (imageType == ImageType::MAGICK) { if (imageType == ImageType::MAGICK) {
option->set("density", std::to_string(descriptor->density).data()); option->set("density", std::to_string(descriptor->density).data());
@@ -355,8 +355,8 @@ namespace sharp {
/* /*
Set pixels/mm resolution based on a pixels/inch density. Set pixels/mm resolution based on a pixels/inch density.
*/ */
void SetDensity(VImage image, const int density) { void SetDensity(VImage image, const double density) {
const double pixelsPerMm = static_cast<double>(density) / 25.4; const double pixelsPerMm = density / 25.4;
image.set("Xres", pixelsPerMm); image.set("Xres", pixelsPerMm);
image.set("Yres", pixelsPerMm); image.set("Yres", pixelsPerMm);
image.set(VIPS_META_RESOLUTION_UNIT, "in"); image.set(VIPS_META_RESOLUTION_UNIT, "in");

View File

@@ -49,7 +49,7 @@ namespace sharp {
char *buffer; char *buffer;
bool failOnError; bool failOnError;
size_t bufferLength; size_t bufferLength;
int density; double density;
int rawChannels; int rawChannels;
int rawWidth; int rawWidth;
int rawHeight; int rawHeight;
@@ -63,7 +63,7 @@ namespace sharp {
buffer(nullptr), buffer(nullptr),
failOnError(FALSE), failOnError(FALSE),
bufferLength(0), bufferLength(0),
density(72), density(72.0),
rawChannels(0), rawChannels(0),
rawWidth(0), rawWidth(0),
rawHeight(0), rawHeight(0),
@@ -186,7 +186,7 @@ namespace sharp {
/* /*
Set pixels/mm resolution based on a pixels/inch density. Set pixels/mm resolution based on a pixels/inch density.
*/ */
void SetDensity(VImage image, const int density); void SetDensity(VImage image, const double density);
/* /*
Check the proposed format supports the current dimensions. Check the proposed format supports the current dimensions.

BIN
test/fixtures/expected/svg14.4.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

View File

@@ -939,6 +939,21 @@ describe('Input/output', function () {
}); });
}); });
it('Convert SVG to PNG at 14.4DPI', function (done) {
sharp(fixtures.inputSvg, { density: 14.4 })
.toFormat('png')
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
assert.strictEqual(20, info.width);
assert.strictEqual(20, info.height);
fixtures.assertSimilar(fixtures.expected('svg14.4.png'), data, function (err) {
if (err) throw err;
done();
});
});
});
it('Convert SVG with embedded images to PNG, respecting dimensions, autoconvert to PNG', function (done) { it('Convert SVG with embedded images to PNG, respecting dimensions, autoconvert to PNG', function (done) {
sharp(fixtures.inputSvgWithEmbeddedImages) sharp(fixtures.inputSvgWithEmbeddedImages)
.toBuffer(function (err, data, info) { .toBuffer(function (err, data, info) {
@@ -1506,11 +1521,6 @@ describe('Input/output', function () {
sharp(null, { density: 'zoinks' }); sharp(null, { density: 'zoinks' });
}); });
}); });
it('Invalid density: float', function () {
assert.throws(function () {
sharp(null, { density: 0.5 });
});
});
it('Ignore unknown attribute', function () { it('Ignore unknown attribute', function () {
sharp(null, { unknown: true }); sharp(null, { unknown: true });
}); });

View File

@@ -4,6 +4,7 @@ const assert = require('assert');
const fs = require('fs'); const fs = require('fs');
const semver = require('semver'); const semver = require('semver');
const libvips = require('../../lib/libvips'); const libvips = require('../../lib/libvips');
const mockFS = require('mock-fs');
const originalPlatform = process.platform; const originalPlatform = process.platform;
@@ -74,4 +75,34 @@ describe('libvips binaries', function () {
assert.strictEqual(true, fs.existsSync(cachePath)); assert.strictEqual(true, fs.existsSync(cachePath));
}); });
}); });
describe('safe directory creation', function () {
before(function () {
mockFS({
exampleDirA: {
exampleDirB: {
exampleFile: 'Example test file'
}
}
});
});
after(function () { mockFS.restore(); });
it('mkdirSync creates a directory', function () {
const dirPath = 'createdDir';
libvips.mkdirSync(dirPath);
assert.strictEqual(true, fs.existsSync(dirPath));
});
it('mkdirSync does not throw error or overwrite an existing dir', function () {
const dirPath = 'exampleDirA';
const nestedDirPath = 'exampleDirA/exampleDirB';
assert.strictEqual(true, fs.existsSync(dirPath));
libvips.mkdirSync(dirPath);
assert.strictEqual(true, fs.existsSync(dirPath));
assert.strictEqual(true, fs.existsSync(nestedDirPath));
});
});
}); });