Big News: Socket Selected for OpenAI's Cybersecurity Grant Program.Details
Socket
Book a DemoSign in
Socket

imagemagick-native

Package Overview
Dependencies
Maintainers
2
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

imagemagick-native - npm Package Compare versions

Comparing version
1.5.0
to
1.6.0
+72
test/test.gravity.js
var test = require('tap').test
, imagemagick = require('..')
, debug = 1
;
console.log("image magick's version is: " + imagemagick.version());
var versions = imagemagick.version().split(".");
function saveToFileIfDebug (buffer, file) {
if (debug) {
require('fs').writeFileSync( file, buffer, 'binary' );
console.log( "wrote file: "+file );
}
}
test( 'taller default', function (t) {
var buffer = imagemagick.convert({
srcData: require('fs').readFileSync( "test.png" ), // 58x66
width: 100,
height: 100,
resizeStyle: 'aspectfill',
quality: 80,
format: 'PNG',
debug: debug
});
t.equal( Buffer.isBuffer(buffer), true, 'buffer is Buffer' );
saveToFileIfDebug( buffer, "out.gravity-default.png" );
t.end();
});
[ "Center",
"East",
"West",
"North",
"South",
"NorthEast",
"NorthWest",
"SouthEast",
"SouthWest",
"None",
].forEach( function (gravity) {
test( 'taller ' + gravity, function (t) {
var buffer = imagemagick.convert({
srcData: require('fs').readFileSync( "test.png" ), // 58x66
width: 100,
height: 100,
resizeStyle: 'aspectfill',
gravity: gravity,
quality: 80,
format: 'PNG',
debug: debug
});
t.equal( Buffer.isBuffer(buffer), true, 'buffer is Buffer' );
saveToFileIfDebug( buffer, "out.gravity-"+gravity+".png" );
t.end();
});
test( 'wide ' + gravity, function (t) {
var buffer = imagemagick.convert({
srcData: require('fs').readFileSync( "test.wide.png" ), // 58x66
width: 100,
height: 100,
resizeStyle: 'aspectfill',
gravity: gravity,
quality: 80,
format: 'PNG',
debug: debug
});
t.equal( Buffer.isBuffer(buffer), true, 'buffer is Buffer' );
saveToFileIfDebug( buffer, "out.wide.gravity-"+gravity+".png" );
t.end();
});
} );
+1
-1

@@ -10,3 +10,3 @@ {

],
"version": "1.5.0",
"version": "1.6.0",
"license": "MIT",

@@ -13,0 +13,0 @@ "repository": {

+132
-116
# node-imagemagick-native
[Imagemagick](http://www.imagemagick.org/)'s [Magick++](http://www.imagemagick.org/Magick++/) binding for [Node](http://nodejs.org/).
[ImageMagick](http://www.imagemagick.org/)'s [Magick++](http://www.imagemagick.org/Magick++/) binding for [Node](http://nodejs.org/).

@@ -15,2 +15,7 @@ Features

* [Examples](#examples)
* [Convert formats](#example-convert) (PNG to JPEG)
* [Blur](#example-blur)
* [Resize](#example-resize)
* [Rotate, flip, and mirror](#example-rotate-flip-mirror)
* [API Reference](#api)

@@ -24,11 +29,116 @@ * [`convert`](#convert)

* [`version`](#version)
* [Examples](#examples)
* [Convert formats](#example-convert) (PNG to JPEG)
* [Blur](#example-blur)
* [Resize](#example-resize)
* [Rotate, flip, and mirror](#example-rotate-flip-mirror)
* [Installation](#installation)
* [Linux / Mac OS X](#installation-unix)
* [Windows](#installation-windows)
* [Performance](#performance)
* [License](#license)
<a name='examples'></a>
## Examples
<a name='example-convert'></a>
### Convert formats
Convert from one format to another with quality control:
```js
fs.writeFileSync('after.png', imagemagick.convert({
srcData: fs.readFileSync('before.jpg'),
format: 'PNG',
quality: 100 // (best) to 1 (worst)
}));
```
Original JPEG:
![alt text](http://elad.github.io/node-imagemagick-native/examples/quality.jpg 'Original')
Converted to PNG:
quality 100 | quality 50 | quality 1
:---: | :---: | :---:
![alt text](http://elad.github.io/node-imagemagick-native/examples/quality_100.png 'quality 100') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/quality_50.png 'quality 50') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/quality_1.png 'quality 1')
*Image courtesy of [David Yu](https://www.flickr.com/photos/davidyuweb/14175248591).*
<a name='example-blur'></a>
### Blur
Blur image:
```js
fs.writeFileSync('after.jpg', imagemagick.convert({
srcData: fs.readFileSync('before.jpg'),
blur: 5
}));
```
![alt text](http://elad.github.io/node-imagemagick-native/examples/blur_before.jpg 'Before blur') becomes ![alt text](http://elad.github.io/node-imagemagick-native/examples/blur_after.jpg 'After blue')
*Image courtesy of [Tambako The Jaguar](https://www.flickr.com/photos/tambako/3574360498).*
<a name='example-resize'></a>
### Resize
Resized images by specifying `width` and `height`. There are three resizing styles:
* `aspectfill`: Default. The resulting image will be exactly the specified size, and may be cropped.
* `aspectfit`: Scales the image so that it will not have to be cropped.
* `fill`: Squishes or stretches the image so that it fills exactly the specified size.
```js
fs.writeFileSync('after_resize.jpg', imagemagick.convert({
srcData: fs.readFileSync('before_resize.jpg'),
width: 100,
height: 100,
resizeStyle: 'aspectfill', // is the default, or 'aspectfit' or 'fill'
gravity: 'Center' // optional: position crop area when using 'aspectfill'
}));
```
Original:
![alt text](http://elad.github.io/node-imagemagick-native/examples/resize.jpg 'Original')
Resized:
aspectfill | aspectfit | fill
:---: | :---: | :---:
![alt text](http://elad.github.io/node-imagemagick-native/examples/resize_aspectfill.jpg 'aspectfill') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/resize_aspectfit.jpg 'aspectfit') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/resize_fill.jpg 'fill')
*Image courtesy of [Christoph](https://www.flickr.com/photos/scheinwelten/381994831).*
<a name='example-rotate-flip-mirror'></a>
### Rotate, flip, and mirror
Rotate and flip images, and combine the two to mirror:
```js
fs.writeFileSync('after_rotateflip.jpg', imagemagick.convert({
srcData: fs.readFileSync('before_rotateflip.jpg'),
rotate: 180,
flip: true
}));
```
Original:
![alt text](http://elad.github.io/node-imagemagick-native/examples/rotateflip.jpg 'Original')
Modified:
rotate 90 degrees | rotate 180 degrees | flip | flip + rotate 180 degrees = mirror
:---: | :---: | :---: | :---:
![alt text](http://elad.github.io/node-imagemagick-native/examples/rotateflip_rotate_90.jpg 'rotate 90') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/rotateflip_rotate_180.jpg 'rotate 180') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/rotateflip_flip.jpg 'flip') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/rotateflip_mirror.jpg 'flip + rotate 180 = mirror')
*Image courtesy of [Bill Gracey](https://www.flickr.com/photos/9422878@N08/6482704235).*
<a name='api'></a>
## API
## API Reference

@@ -51,6 +161,8 @@ <a name='convert'></a>

resizeStyle: optional. default: 'aspectfill'. can be 'aspectfit', 'fill'
aspectfill: keep aspect ratio, get the exact provided size,
crop top/bottom or left/right if necessary
aspectfill: keep aspect ratio, get the exact provided size.
aspectfit: keep aspect ratio, get maximum image that fits inside provided size
fill: forget aspect ratio, get the exact provided size
gravity: optional. default: 'Center'. used to position the crop area when resizeStyle is 'aspectfill'
can be 'NorthWest', 'North', 'NorthEast', 'West',
'Center', 'East', 'SouthWest', 'South', 'SouthEast', 'None'
format: optional. output format, ex: 'JPEG'. see below for candidates

@@ -248,114 +360,13 @@ filter: optional. resize filter. ex: 'Lagrange', 'Lanczos'. see below for candidates

<a name='installation'></a>
<a name='examples'></a>
## Installation
## Examples
<a name='installation-unix'></a>
<a name='example-convert'></a>
### Linux / Mac OS X
### Convert formats
Install [ImageMagick](http://www.imagemagick.org/) with headers before installing this module.
Tested with ImageMagick 6.7.7 on CentOS 6 and Mac OS X Lion, Ubuntu 12.04 .
Convert from one format to another with quality control:
```js
fs.writeFileSync('after.png', imagemagick.convert({
srcData: fs.readFileSync('before.jpg'),
quality: 100 // (best) to 1 (worst)
}));
```
Original JPEG:
![alt text](http://elad.github.io/node-imagemagick-native/examples/quality.jpg 'Original')
Converted to PNG:
quality 100 | quality 50 | quality 1
:---: | :---: | :---:
![alt text](http://elad.github.io/node-imagemagick-native/examples/quality_100.png 'quality 100') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/quality_50.png 'quality 50') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/quality_1.png 'quality 1')
*Image courtesy of [David Yu](https://www.flickr.com/photos/davidyuweb/14175248591).*
<a name='example-blur'></a>
### Blur
Blur image:
```js
fs.writeFileSync('after.jpg', imagemagick.convert({
srcData: fs.readFileSync('before.jpg'),
blur: 5
}));
```
![alt text](http://elad.github.io/node-imagemagick-native/examples/blur_before.jpg 'Before blur') becomes ![alt text](http://elad.github.io/node-imagemagick-native/examples/blur_after.jpg 'After blue')
*Image courtesy of [Tambako The Jaguar](https://www.flickr.com/photos/tambako/3574360498).*
<a name='example-resize'></a>
### Resize
Resized images by specifying `width` and `height`. There are three resizing styles:
* `aspectfill`: Default. The resulting image will be exactly the specified size, and may be cropped.
* `aspectfit`: Scales the image so that it will not have to be cropped.
* `fill`: Squishes or stretches the image so that it fills exactly the specified size.
```js
fs.writeFileSync('after_resize.jpg', imagemagick.convert({
srcData: fs.readFileSync('before_resize.jpg'),
width: 100,
height: 100,
resizeStyle: 'aspectfill' // is the default, or 'aspectfit' or 'fill'
}));
```
Original:
![alt text](http://elad.github.io/node-imagemagick-native/examples/resize.jpg 'Original')
Resized:
aspectfill | aspectfit | fill
:---: | :---: | :---:
![alt text](http://elad.github.io/node-imagemagick-native/examples/resize_aspectfill.jpg 'aspectfill') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/resize_aspectfit.jpg 'aspectfit') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/resize_fill.jpg 'fill')
*Image courtesy of [Christoph](https://www.flickr.com/photos/scheinwelten/381994831).*
<a name='example-rotate-flip-mirror'></a>
### Rotate, flip, and mirror
Rotate and flip images, and combine the two to mirror:
```js
fs.writeFileSync('after_rotateflip.jpg', imagemagick.convert({
srcData: fs.readFileSync('before_rotateflip.jpg'),
rotate: 180,
flip: true
}));
```
Original:
![alt text](http://elad.github.io/node-imagemagick-native/examples/rotateflip.jpg 'Original')
Modified:
rotate 90 degrees | rotate 180 degrees | flip | flip + rotate 180 degrees = mirror
:---: | :---: | :---: | :---:
![alt text](http://elad.github.io/node-imagemagick-native/examples/rotateflip_rotate_90.jpg 'rotate 90') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/rotateflip_rotate_180.jpg 'rotate 180') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/rotateflip_flip.jpg 'flip') | ![alt text](http://elad.github.io/node-imagemagick-native/examples/rotateflip_mirror.jpg 'flip + rotate 180 = mirror')
*Image courtesy of [Bill Gracey](https://www.flickr.com/photos/9422878@N08/6482704235).*
## Installation
### Linux / Mac
Install [Imagemagick](http://www.imagemagick.org/) with headers before installing this module.
Tested with ImageMagick 6.7.7 on CentOS6 and MacOS10.7, Ubuntu12.04 .
brew install imagemagick

@@ -392,2 +403,4 @@

<a name='installation-windows'></a>
### Windows

@@ -411,2 +424,4 @@

<a name='performance'></a>
## Performance - simple thumbnail creation

@@ -421,2 +436,3 @@

<a name='license'></a>

@@ -423,0 +439,0 @@ ## License (MIT)

@@ -84,2 +84,3 @@ #ifndef BUILDING_NODE_EXTENSION

std::string resizeStyle;
std::string gravity;
std::string format;

@@ -107,2 +108,46 @@ std::string filter;

#define RETURN_BLOB_OR_ERROR(req) \
do { \
im_ctx_base* _context = static_cast<im_ctx_base*>(req->data); \
delete req; \
if (!_context->error.empty()) { \
const char *_err_str = _context->error.c_str(); \
delete _context; \
return NanThrowError(_err_str); \
} else { \
const Handle<Object> _retBuffer = NanNewBufferHandle(_context->dstBlob.length()); \
memcpy( Buffer::Data(_retBuffer), _context->dstBlob.data(), _context->dstBlob.length() ); \
delete _context; \
NanReturnValue(_retBuffer); \
} \
} while(0);
bool ReadImageMagick(Magick::Image *image, Magick::Blob srcBlob, std::string srcFormat, im_ctx_base *context) {
if( ! srcFormat.empty() ){
if (context->debug) printf( "reading with format: %s\n", srcFormat.c_str() );
image->magick( srcFormat.c_str() );
}
try {
image->read( srcBlob );
}
catch (Magick::Warning& warning) {
if (!context->ignoreWarnings) {
context->error = warning.what();
return false;
} else if (context->debug) {
printf("warning: %s\n", warning.what());
}
}
catch (std::exception& err) {
context->error = err.what();
return false;
}
catch (...) {
context->error = std::string("unhandled error");
return false;
}
return true;
}
void DoConvert(uv_work_t* req) {

@@ -130,26 +175,4 @@

if( ! context->srcFormat.empty() ){
if (debug) printf( "srcFormat: %s\n", context->srcFormat.c_str() );
image.magick( context->srcFormat.c_str() );
}
try {
image.read( srcBlob );
}
catch (Magick::Warning& warning) {
if (!context->ignoreWarnings) {
context->error = warning.what();
return;
} else if (debug) {
printf("warning: %s\n", warning.what());
}
}
catch (std::exception& err) {
context->error = err.what();
if ( !ReadImageMagick(&image, srcBlob, context->srcFormat, context) )
return;
}
catch (...) {
context->error = std::string("unhandled error");
return;
}

@@ -172,2 +195,19 @@ if (debug) printf("original width,height: %d, %d\n", (int) image.columns(), (int) image.rows());

const char* gravity = context->gravity.c_str();
if ( strcmp("Center", gravity)!=0
&& strcmp("East", gravity)!=0
&& strcmp("West", gravity)!=0
&& strcmp("North", gravity)!=0
&& strcmp("South", gravity)!=0
&& strcmp("NorthEast", gravity)!=0
&& strcmp("NorthWest", gravity)!=0
&& strcmp("SouthEast", gravity)!=0
&& strcmp("SouthWest", gravity)!=0
&& strcmp("None", gravity)!=0
) {
context->error = std::string("gravity not supported");
return;
}
if (debug) printf( "gravity: %s\n", gravity );
if( ! context->format.empty() ){

@@ -216,2 +256,3 @@ if (debug) printf( "format: %s\n", context->format.c_str() );

unsigned int resizeheight;
if ( aspectratioExpected > aspectratioOriginal ) {

@@ -221,4 +262,12 @@ // expected is taller

resizeheight = height;
xoffset = (unsigned int)( (resizewidth - width) / 2. );
yoffset = 0;
if ( strstr(gravity, "West") != NULL ) {
xoffset = 0;
}
else if ( strstr(gravity, "East") != NULL ) {
xoffset = (unsigned int)( resizewidth - width );
}
else {
xoffset = (unsigned int)( (resizewidth - width) / 2. );
}
yoffset = 0;
}

@@ -229,4 +278,12 @@ else {

resizeheight = (unsigned int)( (double)width / (double)image.columns() * (double)image.rows() + 1. );
xoffset = 0;
yoffset = (unsigned int)( (resizeheight - height) / 2. );
xoffset = 0;
if ( strstr(gravity, "North") != NULL ) {
yoffset = 0;
}
else if ( strstr(gravity, "South") != NULL ) {
yoffset = (unsigned int)( resizeheight - height );
}
else {
yoffset = (unsigned int)( (resizeheight - height) / 2. );
}
}

@@ -250,18 +307,21 @@

// limit canvas size to cropGeometry
if (debug) printf( "crop to: %d, %d, %d, %d\n", width, height, xoffset, yoffset );
Magick::Geometry cropGeometry( width, height, xoffset, yoffset, 0, 0 );
if ( strcmp ( gravity, "None" ) != 0 ) {
// limit canvas size to cropGeometry
if (debug) printf( "crop to: %d, %d, %d, %d\n", width, height, xoffset, yoffset );
Magick::Geometry cropGeometry( width, height, xoffset, yoffset, 0, 0 );
Magick::Color transparent( "transparent" );
if ( strcmp( context->format.c_str(), "PNG" ) == 0 ) {
// make background transparent for PNG
// JPEG background becomes black if set transparent here
transparent.alpha( 1. );
Magick::Color transparent( "transparent" );
if ( strcmp( context->format.c_str(), "PNG" ) == 0 ) {
// make background transparent for PNG
// JPEG background becomes black if set transparent here
transparent.alpha( 1. );
}
#if MagickLibVersion > 0x654
image.extent( cropGeometry, transparent );
#else
image.extent( cropGeometry );
#endif
}
#if MagickLibVersion > 0x654
image.extent( cropGeometry, transparent );
#else
image.extent( cropGeometry );
#endif
}

@@ -335,4 +395,15 @@ else if ( strcmp ( resizeStyle, "aspectfit" ) == 0 ) {

Magick::Blob dstBlob;
image.write( &dstBlob );
try {
image.write( &dstBlob );
}
catch (std::exception& err) {
std::string message = "image.write failed with error: ";
message += err.what();
context->error = message;
return;
}
catch (...) {
context->error = std::string("unhandled error");
return;
}
context->dstBlob = dstBlob;

@@ -381,2 +452,5 @@ }

// resizeStyle: optional. default: "aspectfill". can be "aspectfit", "fill"
// gravity: optional. default: "Center". used when resizeStyle is "aspectfill"
// can be "NorthWest", "North", "NorthEast", "West",
// "Center", "East", "SouthWest", "South", "SouthEast", "None"
// format: optional. one of http://www.imagemagick.org/script/formats.php ex: "JPEG"

@@ -447,2 +521,6 @@ // filter: optional. ex: "Lagrange", "Lanczos". see ImageMagick's magick/option.c for candidates

Local<Value> gravityValue = obj->Get( NanNew<String>("gravity") );
context->gravity = !gravityValue->IsUndefined() ?
NanCString(gravityValue, &count) : "Center";
Local<Value> formatValue = obj->Get( NanNew<String>("format") );

@@ -470,15 +548,3 @@ context->format = !formatValue->IsUndefined() ?

DoConvert(req);
convert_im_ctx* context = static_cast<convert_im_ctx*>(req->data);
delete req;
if (!context->error.empty()) {
const char *err_str = context->error.c_str();
delete context;
return NanThrowError(err_str);
}
else {
const Handle<Object> retBuffer = NanNewBufferHandle(context->dstBlob.length());
memcpy( Buffer::Data(retBuffer), context->dstBlob.data(), context->dstBlob.length() );
delete context;
NanReturnValue(retBuffer);
}
RETURN_BLOB_OR_ERROR(req)
}

@@ -837,21 +903,5 @@ }

Magick::Image image;
try {
image.read( srcBlob );
}
catch (std::exception& err) {
std::string what (err.what());
std::string message = std::string("image.read failed with error: ") + what;
std::size_t found = what.find( "warn" );
if (context->ignoreWarnings && (found != std::string::npos)) {
if (context->debug) printf("warning: %s\n", message.c_str());
}
else {
context->error = message;
return;
}
}
catch (...) {
context->error = std::string("unhandled error");
if ( !ReadImageMagick(&image, srcBlob, "", context) )
return;
}

@@ -880,21 +930,5 @@ Magick::GravityType gravityType;

Magick::Image compositeImage;
try {
compositeImage.read( compositeBlob );
}
catch (std::exception& err) {
std::string what (err.what());
std::string message = std::string("compositeImage.read failed with error: ") + what;
std::size_t found = what.find( "warn" );
if (context->ignoreWarnings && (found != std::string::npos)) {
if (context->debug) printf("warning: %s\n", message.c_str());
}
else {
context->error = message;
return;
}
}
catch (...) {
context->error = std::string("unhandled error");
if ( !ReadImageMagick(&compositeImage, compositeBlob, "", context) )
return;
}

@@ -970,15 +1004,3 @@ image.composite(compositeImage,gravityType,Magick::OverCompositeOp);

DoComposite(req);
composite_im_ctx* context = static_cast<composite_im_ctx*>(req->data);
delete req;
if (!context->error.empty()) {
const char *err_str = context->error.c_str();
delete context;
return NanThrowError(err_str);
}
else {
const Handle<Object> retBuffer = NanNewBufferHandle(context->dstBlob.length());
memcpy( Buffer::Data(retBuffer), context->dstBlob.data(), context->dstBlob.length() );
delete context;
NanReturnValue(retBuffer);
}
RETURN_BLOB_OR_ERROR(req)
}

@@ -985,0 +1007,0 @@ }

@@ -16,2 +16,20 @@ var test = require('tap').test

test( 'convert invalid format', function (t) {
var buffer;
try {
buffer = imagemagick.convert({
srcData: require('fs').readFileSync( "test.png" ), // 58x66
width: 100,
height: 100,
quality: 80,
format: 'PNGX',
debug: debug
});
} catch (e) {
t.like( e.message, /no decode delegate for this image format/, 'err message' );
}
t.equal( buffer, undefined, 'buffer undefined' );
t.end();
});
test( 'convert invalid number of arguments', function (t) {

@@ -18,0 +36,0 @@ var error = 0;