Welis Garagen-Anzeige

This commit is contained in:
2018-02-04 20:14:45 +01:00
parent 9b4b42bdc0
commit 48d7ac9661
855 changed files with 134068 additions and 5 deletions
Generated Vendored
+2
View File
@@ -0,0 +1,2 @@
test/
examples/
+10
View File
@@ -0,0 +1,10 @@
before_install:
- sudo apt-get update
- sudo apt-get install imagemagick graphicsmagick
language: node_js
node_js:
- "6"
- "4"
- "0.10"
- "0.12"
- "iojs-v2"
Generated Vendored
+574
View File
@@ -0,0 +1,574 @@
1.23.1 / 2017-12-27
* fixed: use debug > 2.6.9 because of security issue #685 danez
* tests; add nsp check
* tests; get tests passing on OSX
1.23.0 / 2016-08-03
* fixed; webpack support #547 sean-shirazi
* fixed; windows support - use cross-spawn to spawn processes #537 bdukes
* added; allow thumbnail to accept the same options as resize #527 Sebmaster
* added; dispose support #487 dlwr
* docs; add example of loading image from URL #544 wahengchang
* docs; Fix a link in README.md #532 clbn
* travis; update travis versions #551 amilajack
1.22.0 / 2016-04-07
* fixed; identity parser: support multi-value keys by creating an array #508 #509 [emaniacs](https://github.com/emaniacs)
* fixed; error handling if gm is not installed #499 [aeo3](https://github.com/aeo3)
* fixed; highlightColor typo in compare #504 [DanielHudson](https://github.com/DanielHudson)
* docs; Fix typo #475 [rodrigoalviani](https://github.com/rodrigoalviani)
1.21.1 / 2015-10-26
* fixed: Fixed #465 hard coded gm binary, also fixed issues with compare and fixed tests so they will fail on subsequent runs when they should do [rwky](https://github.com/rwky)
1.21.0 / 2015-10-26 **contains security fix**
* fixed: gm.compare fails to escape arguments properly (Reported by Brendan Scarvell) [rwky](https://github.com/rwky)
1.20.0 / 2015-09-23
* changed: Reverted "Add format inference from filename for buffers/streams" due to errors #448
1.19.0 / 2015-09-16
* changed: Added error to notify about image magick not supporting minify [encima](https://github.com/encima)
* changed: Refactored orientation getter to use faster identify call [lbeschastny](https://github.com/lbeschastny)
* added: resizeExact function [DanMMX](https://github.com/DanMMX)
* added: thumbExact function [DanMMX](https://github.com/DanMMX)
* added: Add format inference from filename for buffers/streams [adurrive](https://github.com/adurrive)
* fixed: Hex values when passed to compare aren't quoted automatically [DanMMX](https://github.com/DanMMX)
* fixed: identify returning last frame size instead of the larges on animated gifs [preynal](https://github.com/preynal)
* docs: Updated docs [laurilehmijoki](https://github.com/laurilehmijoki)
1.18.1 / 2015-05-18
* changed: Added io.js support [rwky](https://github.com/rwky)
1.18.0 / 2015-05-18
* changed: Removed support for node 0.8 and added support for 0.12 [rwky](https://github.com/rwky)
* changed: Listen to stdin error event for spawn errors [kapouer](https://github.com/kapouer)
* changed: Improved error handling when gm isn't installed [FreshXOpenSource](https://github.com/FreshXOpenSource)
* changed: Allow append method to use an array of arguments [emohacker](https://github.com/emohacker)
* changed: appPath option now specifies full path to gm binary John Borkowski
* changed: Ignore warning messages for identify [asrail](https://github.com/asrail)
* added: Montage method [donaldpcook](https://github.com/donaldpcook)
* added: Progressive option to thumb [mohebifar](https://github.com/mohebifar)
* added: Native gm auto-orient for use with gm >= 1.3.18 [bog](https://github.com/bog)
* added: Timeout support by passing the timeout option in milliseconds [marcbachmann](https://github.com/marcbachmann)
* fixed: density when using ImageMagick [syzer](https://github.com/syzer)
* fixed: resize behaviour for falsy values [adius](https://github.com/adius)
1.17.0 / 2014-10-28
==================
* changed: extended compare callback also returns the file names #297 [mastix](https://github.com/mastix)
* changed: pass spawn crash to callback #306 [medikoo](https://github.com/medikoo)
* changed: geometry supports arbitary string as first argument #330 [jdiez17](https://github.com/jdiez17)
* added: support for repage+ option #275 [desigens](https://github.com/desigens)
* added: added the dissolve command #300 [microadm](https://github.com/microadam)
* added: composite method #332 [jdiez17](https://github.com/jdiez17)
* fixed: cannot set tolerance to 0 #302 [rwky](https://github.com/rwky)
* fixed: handle empty buffers #330 [alcidesv](https://github.com/alcidesv)
1.16.0 / 2014-05-09
==================
* fixed; dropped "+" when 0 passed as vertical roll amt #267 [dwtkns](https://github.com/dwtkns)
* added; highlight-style support #272 [fdecampredon](https://github.com/fdecampredon)
1.15.0 / 2014-05-03
===================
* changed; gm.compare logic to always run the mse comparison as expected #258 [Vokkim](https://github.com/Vokkim)
* added; `tolerance` to gm.compare options object #258 [Vokkim](https://github.com/Vokkim)
* added; option to set ImageMagick application path explicitly #250 (akreitals)
* fixed; gm.compare: support values like 9.51582e-05 #260 [normanrz](https://github.com/normanrz)
* README: add call for maintainers
1.14.2 / 2013-12-24
===================
* fixed; background is now a setting #246 (PEM--)
1.14.1 / 2013-12-09
===================
* fixed; identify -verbose colon behavior #240 ludow
1.14.0 / 2013-12-04
===================
* added; compare method for imagemagick (longlho)
1.13.3 / 2013-10-22
===================
* fixed; escape diffOptions.file in compare (dwabyick)
1.13.2 / 2013-10-18
===================
* fixed; density is a setting not an operator
1.13.1 / 2013-09-15
===================
* added; boolean for % crop
1.13.0 / 2013-09-07
===================
* added; morph more than two images (overra)
1.12.2 / 2013-08-29
===================
* fixed; fallback to through in node 0.8
1.12.1 / 2013-08-29 (unpublished)
===================
* refactor; replace through with stream.PassThrough
1.12.0 / 2013-08-27
===================
* added; diff image output file (chenglou)
1.11.1 / 2013-08-17
===================
* added; proto.selectFrame(#)
* fixed; getters should not ignore frame selection
1.11.0 / 2013-07-23
===================
* added; optional formatting string for gm().identify(format, callback) (tornillo)
* removed; error messages when gm/im binary is not installed
1.10.0 / 2013-06-27
===================
* refactor; use native `-auto-orient` for imagemagick
1.9.2 / 2013-06-12
==================
* refactor; move `streamToBuffer` to a separate module
* fixed; .stream(format) without a callback
1.9.1 / 2013-05-07
==================
* fixed; gm().resize(width) always only resizes width
* fixed; gm('img.gif').format() returns the format of the first frame
1.9.0 / 2013-04-21
==================
* added; node v0.10 support
* removed; node < v0.8 support - `Buffer.concat()`
* tests; all tests now run on Travis
* added; gm().stream() returns a stream when no callback is present
* added; gm().toBuffer(callback)
* fixed; gm().size() only returns the size of the first frame of a GIF
1.8.2 / 2013-03-07
==================
* include source path in identify data #126 [soupdiver](https://github.com/soupdiver)
1.8.1 / 2012-12-21
==================
* Avoid losing already set arguments on identify #105 #113 #109 [JNissi](https://github.com/JNissi)
* tests; add autoOrient + thumb() test
* tests; add test case for #113
* tests; added test for #109
* tests; add resize on buffer test
1.8.0 / 2012-12-14
==================
* added; geometry support to scale() #98
* removed; incorrect/broken dissolve() method (never worked)
* fixed; handle child_proc error when using Buffer input #109
* fixed; use of Buffers with identify() #109
* fixed; no longer include -size arg with resize() #98
* fixed; remove -size arg from extent() #103
* fixed; magnify support
* fixed; autoOrient to work with all types of exif orientations [dambalah](https://github.com/dambalah) #108
* tests; npm test runs unit only (now compatible with travis)
* tests; fix magnify test on imagemagick
* tests; added for cmd line args
1.7.0 / 2012-12-06
==================
* added; gm.compare support
* added; passing Buffers directly [danmilon](https://github.com/danmilon)
1.6.1 / 2012-11-13
==================
* fixed regression; only pass additional params on error #96
1.6.0 / 2012-11-10
==================
* changed; rename internal buffer to _buffer #88 [kof](https://github.com/kof)
* changed; optimized identify getters (format, depth, size, color, filesize). #83 please read this for details: https://github.com/aheckmann/gm/commit/8fcf3f8f84a02cc2001da874cbebb89bf7084409
* added; visionmedia/debug support
* added; `gm convert -thumbnail` support. _differs from thumb()._ [danmilon](https://github.com/danmilon)
* fixed; -rotate 0 support #90
* fixed; multi-execution of same gm instance arguments corruption
* fixed; gracefully handle parser errors #94 [eldilibra](https://github.com/eldilibra)
1.5.1 / 2012-10-02
==================
* fixed; passing multiple paths to append() #77
1.5.0 / 2012-09-15
==================
* fixed; callback scope
* fixed; append() usage #77
1.4.2 / 2012-08-17
==================
* fixed; identify parsing for ImageMagick exif data (#58)
* fixed; when in imageMagick mode, complain about missing imageMagick [bcherry](https://github.com/bcherry) (#73)
* added; tests
1.4.1 / 2012-07-31
==================
* fixed; scenes() args
* fixed; accept the left-to-right arg of append()
* added; _subCommand
## v1.4 - 07/28/2012
* added; adjoin() [Math-]
* added; affine() [Math-]
* added; append() [Math-]
* added; authenticate() [Math-]
* added; average() [Math-]
* added; backdrop() [Math-]
* added; blackThreshold() [Math-]
* added; bluePrimary() [Math-]
* added; border() [Math-]
* added; borderColor() [Math-]
* added; box() [Math-]
* added; channel() [Math-]
* added; clip() [Math-]
* added; coalesce() [Math-]
* added; colorMap() [Math-]
* added; compose() [Math-]
* added; compress() [Math-]
* added; convolve() [Math-]
* added; createDirectories() [Math-]
* added; deconstruct() [Math-]
* added; delay() [Math-]
* added; define() [Math-]
* added; displace() [Math-]
* added; display() [Math-]
* added; dispose() [Math-]
* added; disolve() [Math-]
* added; encoding() [Math-]
* added; endian() [Math-]
* added; file() [Math-]
* added; flatten() [Math-]
* added; foreground() [Math-]
* added; frame() [Math-]
* added; fuzz() [Math-]
* added; gaussian() [Math-]
* added; geometry() [Math-]
* added; greenPrimary() [Math-]
* added; highlightColor() [Math-]
* added; highlightStyle() [Math-]
* added; iconGeometry() [Math-]
* added; intent() [Math-]
* added; lat() [Math-]
* added; level() [Math-]
* added; list() [Math-]
* added; log() [Math-]
* added; map() [Math-]
* added; matte() [Math-]
* added; matteColor() [Math-]
* added; mask() [Math-]
* added; maximumError() [Math-]
* added; mode() [Math-]
* added; monitor() [Math-]
* added; mosaic() [Math-]
* added; motionBlur() [Math-]
* added; name() [Math-]
* added; noop() [Math-]
* added; normalize() [Math-]
* added; opaque() [Math-]
* added; operator() [Math-]
* added; orderedDither() [Math-]
* added; outputDirectory() [Math-]
* added; page() [Math-]
* added; pause() [Math-]
* added; pen() [Math-]
* added; ping() [Math-]
* added; pointSize() [Math-]
* added; preview() [Math-]
* added; process() [Math-]
* added; profile() [Math-]
* added; progress() [Math-]
* added; rawSize() [Math-]
* added; randomThreshold() [Math-]
* added; recolor() [Math-]
* added; redPrimary() [Math-]
* added; remote() [Math-]
* added; render() [Math-]
* added; repage() [Math-]
* added; sample() [Math-]
* added; samplingFactor() [Math-]
* added; scene() [Math-]
* added; scenes() [Math-]
* added; screen() [Math-]
* added; segment() [Math-]
* added; set() [Math-]
* added; shade() [Math-]
* added; shadow() [Math-]
* added; sharedMemory() [Math-]
* added; shave() [Math-]
* added; shear() [Math-]
* added; silent() [Math-]
* added; snaps() [Math-]
* added; stagano() [Math-]
* added; stereo() [Math-]
* added; textFont() [Math-]
* added; texture() [Math-]
* added; threshold() [Math-]
* added; tile() [Math-]
* added; transform() [Math-]
* added; transparent() [Math-]
* added; treeDepth() [Math-]
* added; update() [Math-]
* added; units() [Math-]
* added; unsharp() [Math-]
* added; usePixmap() [Math-]
* added; view() [Math-]
* added; virtualPixel() [Math-]
* added; visual() [Math-]
* added; watermark() [Math-]
* added; wave() [Math-]
* added; whitePoint() [Math-]
* added; whiteThreshold() [Math-]
* added; window() [Math-]
* added; windowGroup() [Math-]
## v1.3.2 - 06/22/2012
* added; node >= 0.7/0.8 compat
## v1.3.1 - 06/06/2012
* fixed; thumb() alignment and cropping [thomaschaaf]
* added; hint when graphicsmagick is not installed (#62)
* fixed; minify() (#59)
## v1.3.0 - 04/11/2012
* added; flatten support [jwarchol]
* added; background support [jwarchol]
* fixed; identify parser error [chriso]
## v1.2.0 - 03/30/2012
* added; extent and gravity support [jwarchol]
## v1.1.0 - 03/15/2012
* added; filter() support [travisbeck]
* added; density() [travisbeck]
* fixed; permit either width or height in resize [dambalah]
* updated; docs
## v1.0.5 - 02/15/2012
* added; strip() support [Math-]
* added; interlace() support [Math-]
* added; setFormat() support [Math-]
* fixed; regexps for image types [Math-]
## v1.0.4 - 02/09/2012
* expose utils
## v1.0.3 - 01/27/2012
* removed; console.log
## v1.0.2 - 01/24/2012
* added; debugging info on parser errors
* fixed; exports.version
## v1.0.1 - 01/12/2012
* fixed; use of reserved keyword `super` for node v0.5+
## v1.0.0 - 01/12/2012
* added; autoOrient support [kainosnoema] (#21)
* added; orientation support [kainosnoema] (#21)
* fixed; identify parser now properly JSON formats all data output by `gm identify` such as IPTC, GPS, Make, etc (#20)
* added; support for running as imagemagick (#23, #29)
* added; subclassing support; useful for setting default constructor options like one constructor for ImageMagick, the other for GM
* added; more tests
* changed; remove redundant `orientation`, `resolution`, and `filesize` from `this.data` in `indentify()`. Use their uppercase equivalents.
## v0.6.0 - 12/14/2011
* added; stream support [kainosnoema] (#22)
## v0.5.0 - 07/07/2011
* added; gm#trim() support [lepokle]
* added; gm#inputIs() support
* fixed; 'geometry does not contain image' error: gh-17
## v0.4.3 - 05/17/2011
* added; bunch of tests
* fixed; polygon, polyline, bezier drawing bug
## v0.4.2 - 05/10/2011
* added; resize options support
## v0.4.1 - 04/28/2011
* shell args are now escaped (thanks @visionmedia)
* added; gm.in()
* added; gm.out()
* various refactoring
## v0.4.0 - 9/21/2010
* removed deprecated `new` method
* added drawing docs
## v0.3.2 - 9/06/2010
* new images are now created using same gm() constructor
## v0.3.1 - 9/06/2010
* can now create images from scratch
* add type method
## v0.3.0 - 8/26/2010
* add drawing api
## v0.2.2 - 8/22/2010
* add quality option to thumb()
* add teropa to contributors
* added support for colorspace()
## v0.2.1 - 7/31/2010
* fixed naming conflict. depth() manipulation method renamed bitdepth()
* added better docs
## v0.2.0 - 7/29/2010
new methods
- swirl
- spread
- solarize
- sharpen
- roll
- sepia
- region
- raise
- lower
- paint
- noise
- negative
- morph
- median
- antialias
- limit
- label
- implode
- gamma
- enhance
- equalize
- emboss
- edge
- dither
- monochrome
- despeckle
- depth
- cycle
- contrast
- comment
- colors
added more default args to several methods
added more examples
## v0.1.2 - 7/28/2010
* refactor project into separate modules
## v0.1.1 - 7/27/2010
* add modulate method
* add colorize method
* add charcoal method
* add chop method
* bug fix in write without a callback
## v0.1.0 - 6/27/2010
* no longer supporting mogrify
* add image data getter methods
* size
* format
* color
* res
* depth
* filesize
* identify
* add new convert methods
* scale
* resample
* rotate
* flip
* flop
* crop
* magnify
* minify
* quality
* blur
* thumb
## v0.0.1 - 6/11/2010
Initial release
Generated Vendored
+11
View File
@@ -0,0 +1,11 @@
test: security
@node test/ --integration $(TESTS)
test-unit: security
@node test/ $(TESTS)
security:
@node_modules/.bin/nsp check
.PHONY: test test-unit security
Generated Vendored
+649
View File
@@ -0,0 +1,649 @@
# gm [![Build Status](https://travis-ci.org/aheckmann/gm.png?branch=master)](https://travis-ci.org/aheckmann/gm) [![NPM Version](https://img.shields.io/npm/v/gm.svg?style=flat)](https://www.npmjs.org/package/gm)
GraphicsMagick and ImageMagick for node
## Bug Reports
When reporting bugs please include the version of graphicsmagick/imagemagick you're using (gm -version/convert -version) as well as the version of this module and copies of any images you're having problems with.
## Getting started
First download and install [GraphicsMagick](http://www.graphicsmagick.org/) or [ImageMagick](http://www.imagemagick.org/). In Mac OS X, you can simply use [Homebrew](http://mxcl.github.io/homebrew/) and do:
brew install imagemagick
brew install graphicsmagick
If you want WebP support with ImageMagick, you must add the WebP option:
brew install imagemagick --with-webp
then either use npm:
npm install gm
or clone the repo:
git clone git://github.com/aheckmann/gm.git
## Use ImageMagick instead of gm
Subclass `gm` to enable ImageMagick
```js
var fs = require('fs')
, gm = require('gm').subClass({imageMagick: true});
// resize and remove EXIF profile data
gm('/path/to/my/img.jpg')
.resize(240, 240)
...
```
## Basic Usage
```js
var fs = require('fs')
, gm = require('gm');
// resize and remove EXIF profile data
gm('/path/to/my/img.jpg')
.resize(240, 240)
.noProfile()
.write('/path/to/resize.png', function (err) {
if (!err) console.log('done');
});
// some files would not be resized appropriately
// http://stackoverflow.com/questions/5870466/imagemagick-incorrect-dimensions
// you have two options:
// use the '!' flag to ignore aspect ratio
gm('/path/to/my/img.jpg')
.resize(240, 240, '!')
.write('/path/to/resize.png', function (err) {
if (!err) console.log('done');
});
// use the .resizeExact with only width and/or height arguments
gm('/path/to/my/img.jpg')
.resizeExact(240, 240)
.write('/path/to/resize.png', function (err) {
if (!err) console.log('done');
});
// obtain the size of an image
gm('/path/to/my/img.jpg')
.size(function (err, size) {
if (!err)
console.log(size.width > size.height ? 'wider' : 'taller than you');
});
// output all available image properties
gm('/path/to/img.png')
.identify(function (err, data) {
if (!err) console.log(data)
});
// pull out the first frame of an animated gif and save as png
gm('/path/to/animated.gif[0]')
.write('/path/to/firstframe.png', function (err) {
if (err) console.log('aaw, shucks');
});
// auto-orient an image
gm('/path/to/img.jpg')
.autoOrient()
.write('/path/to/oriented.jpg', function (err) {
if (err) ...
})
// crazytown
gm('/path/to/my/img.jpg')
.flip()
.magnify()
.rotate('green', 45)
.blur(7, 3)
.crop(300, 300, 150, 130)
.edge(3)
.write('/path/to/crazy.jpg', function (err) {
if (!err) console.log('crazytown has arrived');
})
// annotate an image
gm('/path/to/my/img.jpg')
.stroke("#ffffff")
.drawCircle(10, 10, 20, 10)
.font("Helvetica.ttf", 12)
.drawText(30, 20, "GMagick!")
.write("/path/to/drawing.png", function (err) {
if (!err) console.log('done');
});
// creating an image
gm(200, 400, "#ddff99f3")
.drawText(10, 50, "from scratch")
.write("/path/to/brandNewImg.jpg", function (err) {
// ...
});
```
## Streams
```js
// passing a stream
var readStream = fs.createReadStream('/path/to/my/img.jpg');
gm(readStream, 'img.jpg')
.write('/path/to/reformat.png', function (err) {
if (!err) console.log('done');
});
// passing a downloadable image by url
var request = require('request');
var url = "www.abc.com/pic.jpg"
gm(request(url))
.write('/path/to/reformat.png', function (err) {
if (!err) console.log('done');
});
// can also stream output to a ReadableStream
// (can be piped to a local file or remote server)
gm('/path/to/my/img.jpg')
.resize('200', '200')
.stream(function (err, stdout, stderr) {
var writeStream = fs.createWriteStream('/path/to/my/resized.jpg');
stdout.pipe(writeStream);
});
// without a callback, .stream() returns a stream
// this is just a convenience wrapper for above.
var writeStream = fs.createWriteStream('/path/to/my/resized.jpg');
gm('/path/to/my/img.jpg')
.resize('200', '200')
.stream()
.pipe(writeStream);
// pass a format or filename to stream() and
// gm will provide image data in that format
gm('/path/to/my/img.jpg')
.stream('png', function (err, stdout, stderr) {
var writeStream = fs.createWriteStream('/path/to/my/reformatted.png');
stdout.pipe(writeStream);
});
// or without the callback
var writeStream = fs.createWriteStream('/path/to/my/reformatted.png');
gm('/path/to/my/img.jpg')
.stream('png')
.pipe(writeStream);
// combine the two for true streaming image processing
var readStream = fs.createReadStream('/path/to/my/img.jpg');
gm(readStream)
.resize('200', '200')
.stream(function (err, stdout, stderr) {
var writeStream = fs.createWriteStream('/path/to/my/resized.jpg');
stdout.pipe(writeStream);
});
// GOTCHA:
// when working with input streams and any 'identify'
// operation (size, format, etc), you must pass "{bufferStream: true}" if
// you also need to convert (write() or stream()) the image afterwards
// NOTE: this buffers the readStream in memory!
var readStream = fs.createReadStream('/path/to/my/img.jpg');
gm(readStream)
.size({bufferStream: true}, function(err, size) {
this.resize(size.width / 2, size.height / 2)
this.write('/path/to/resized.jpg', function (err) {
if (!err) console.log('done');
});
});
```
## Buffers
```js
// A buffer can be passed instead of a filepath as well
var buf = require('fs').readFileSync('/path/to/image.jpg');
gm(buf, 'image.jpg')
.noise('laplacian')
.write('/path/to/out.jpg', function (err) {
if (err) return handle(err);
console.log('Created an image from a Buffer!');
});
/*
A buffer can also be returned instead of a stream
The first argument to toBuffer is optional, it specifies the image format
*/
gm('img.jpg')
.resize(100, 100)
.toBuffer('PNG',function (err, buffer) {
if (err) return handle(err);
console.log('done!');
})
```
## Custom Arguments
If `gm` does not supply you with a method you need or does not work as you'd like, you can simply use `gm().in()` or `gm().out()` to set your own arguments.
- `gm().command()` - Custom command such as `identify` or `convert`
- `gm().in()` - Custom input arguments
- `gm().out()` - Custom output arguments
The command will be formatted in the following order:
1. `command` - ie `convert`
2. `in` - the input arguments
3. `source` - stdin or an image file
4. `out` - the output arguments
5. `output` - stdout or the image file to write to
For example, suppose you want the following command:
```bash
gm "convert" "label:Offline" "PNG:-"
```
However, using `gm().label()` may not work as intended for you:
```js
gm()
.label('Offline')
.stream();
```
would yield:
```bash
gm "convert" "-label" "\"Offline\"" "PNG:-"
```
Instead, you can use `gm().out()`:
```js
gm()
.out('label:Offline')
.stream();
```
which correctly yields:
```bash
gm "convert" "label:Offline" "PNG:-"
```
### Custom Identify Format String
When identifying an image, you may want to use a custom formatting string instead of using `-verbose`, which is quite slow.
You can use your own [formatting string](http://www.imagemagick.org/script/escape.php) when using `gm().identify(format, callback)`.
For example,
```js
gm('img.png').format(function (err, format) {
})
// is equivalent to
gm('img.png').identify('%m', function (err, format) {
})
```
since `%m` is the format option for getting the image file format.
## Platform differences
Please document and refer to any [platform or ImageMagick/GraphicsMagick issues/differences here](https://github.com/aheckmann/gm/wiki/GraphicsMagick-and-ImageMagick-versions).
## Examples:
Check out the [examples](http://github.com/aheckmann/gm/tree/master/examples/) directory to play around.
Also take a look at the [extending gm](http://wiki.github.com/aheckmann/gm/extending-gm)
page to see how to customize gm to your own needs.
## Constructor:
There are a few ways you can use the `gm` image constructor.
- 1) `gm(path)` When you pass a string as the first argument it is interpreted as the path to an image you intend to manipulate.
- 2) `gm(stream || buffer, [filename])` You may also pass a ReadableStream or Buffer as the first argument, with an optional file name for format inference.
- 3) `gm(width, height, [color])` When you pass two integer arguments, gm will create a new image on the fly with the provided dimensions and an optional background color. And you can still chain just like you do with pre-existing images too. See [here](http://github.com/aheckmann/gm/blob/master/examples/new.js) for an example.
The links below refer to an older version of gm but everything should still work, if anyone feels like updating them please make a PR
## Methods
- getters
- [size](http://aheckmann.github.com/gm/docs.html#getters) - returns the size (WxH) of the image
- [orientation](http://aheckmann.github.com/gm/docs.html#getters) - returns the EXIF orientation of the image
- [format](http://aheckmann.github.com/gm/docs.html#getters) - returns the image format (gif, jpeg, png, etc)
- [depth](http://aheckmann.github.com/gm/docs.html#getters) - returns the image color depth
- [color](http://aheckmann.github.com/gm/docs.html#getters) - returns the number of colors
- [res](http://aheckmann.github.com/gm/docs.html#getters) - returns the image resolution
- [filesize](http://aheckmann.github.com/gm/docs.html#getters) - returns image filesize
- [identify](http://aheckmann.github.com/gm/docs.html#getters) - returns all image data available. Takes an optional format string.
- manipulation
- [adjoin](http://aheckmann.github.com/gm/docs.html#adjoin)
- [affine](http://aheckmann.github.com/gm/docs.html#affine)
- [antialias](http://aheckmann.github.com/gm/docs.html#antialias)
- [append](http://aheckmann.github.com/gm/docs.html#append)
- [authenticate](http://aheckmann.github.com/gm/docs.html#authenticate)
- [autoOrient](http://aheckmann.github.com/gm/docs.html#autoOrient)
- [average](http://aheckmann.github.com/gm/docs.html#average)
- [backdrop](http://aheckmann.github.com/gm/docs.html#backdrop)
- [bitdepth](http://aheckmann.github.com/gm/docs.html#bitdepth)
- [blackThreshold](http://aheckmann.github.com/gm/docs.html#blackThreshold)
- [bluePrimary](http://aheckmann.github.com/gm/docs.html#bluePrimary)
- [blur](http://aheckmann.github.com/gm/docs.html#blur)
- [border](http://aheckmann.github.com/gm/docs.html#border)
- [borderColor](http://aheckmann.github.com/gm/docs.html#borderColor)
- [box](http://aheckmann.github.com/gm/docs.html#box)
- [channel](http://aheckmann.github.com/gm/docs.html#channel)
- [charcoal](http://aheckmann.github.com/gm/docs.html#charcoal)
- [chop](http://aheckmann.github.com/gm/docs.html#chop)
- [clip](http://aheckmann.github.com/gm/docs.html#clip)
- [coalesce](http://aheckmann.github.com/gm/docs.html#coalesce)
- [colors](http://aheckmann.github.com/gm/docs.html#colors)
- [colorize](http://aheckmann.github.com/gm/docs.html#colorize)
- [colorMap](http://aheckmann.github.com/gm/docs.html#colorMap)
- [colorspace](http://aheckmann.github.com/gm/docs.html#colorspace)
- [comment](http://aheckmann.github.com/gm/docs.html#comment)
- [compose](http://aheckmann.github.com/gm/docs.html#compose)
- [compress](http://aheckmann.github.com/gm/docs.html#compress)
- [contrast](http://aheckmann.github.com/gm/docs.html#contrast)
- [convolve](http://aheckmann.github.com/gm/docs.html#convolve)
- [createDirectories](http://aheckmann.github.com/gm/docs.html#createDirectories)
- [crop](http://aheckmann.github.com/gm/docs.html#crop)
- [cycle](http://aheckmann.github.com/gm/docs.html#cycle)
- [deconstruct](http://aheckmann.github.com/gm/docs.html#deconstruct)
- [delay](http://aheckmann.github.com/gm/docs.html#delay)
- [define](http://aheckmann.github.com/gm/docs.html#define)
- [density](http://aheckmann.github.com/gm/docs.html#density)
- [despeckle](http://aheckmann.github.com/gm/docs.html#despeckle)
- [dither](http://aheckmann.github.com/gm/docs.html#dither)
- [displace](http://aheckmann.github.com/gm/docs.html#dither)
- [display](http://aheckmann.github.com/gm/docs.html#display)
- [dispose](http://aheckmann.github.com/gm/docs.html#dispose)
- [dissolve](http://aheckmann.github.com/gm/docs.html#dissolve)
- [edge](http://aheckmann.github.com/gm/docs.html#edge)
- [emboss](http://aheckmann.github.com/gm/docs.html#emboss)
- [encoding](http://aheckmann.github.com/gm/docs.html#encoding)
- [enhance](http://aheckmann.github.com/gm/docs.html#enhance)
- [endian](http://aheckmann.github.com/gm/docs.html#endian)
- [equalize](http://aheckmann.github.com/gm/docs.html#equalize)
- [extent](http://aheckmann.github.com/gm/docs.html#extent)
- [file](http://aheckmann.github.com/gm/docs.html#file)
- [filter](http://aheckmann.github.com/gm/docs.html#filter)
- [flatten](http://aheckmann.github.com/gm/docs.html#flatten)
- [flip](http://aheckmann.github.com/gm/docs.html#flip)
- [flop](http://aheckmann.github.com/gm/docs.html#flop)
- [foreground](http://aheckmann.github.com/gm/docs.html#foreground)
- [frame](http://aheckmann.github.com/gm/docs.html#frame)
- [fuzz](http://aheckmann.github.com/gm/docs.html#fuzz)
- [gamma](http://aheckmann.github.com/gm/docs.html#gamma)
- [gaussian](http://aheckmann.github.com/gm/docs.html#gaussian)
- [geometry](http://aheckmann.github.com/gm/docs.html#geometry)
- [gravity](http://aheckmann.github.com/gm/docs.html#gravity)
- [greenPrimary](http://aheckmann.github.com/gm/docs.html#greenPrimary)
- [highlightColor](http://aheckmann.github.com/gm/docs.html#highlightColor)
- [highlightStyle](http://aheckmann.github.com/gm/docs.html#highlightStyle)
- [iconGeometry](http://aheckmann.github.com/gm/docs.html#iconGeometry)
- [implode](http://aheckmann.github.com/gm/docs.html#implode)
- [intent](http://aheckmann.github.com/gm/docs.html#intent)
- [interlace](http://aheckmann.github.com/gm/docs.html#interlace)
- [label](http://aheckmann.github.com/gm/docs.html#label)
- [lat](http://aheckmann.github.com/gm/docs.html#lat)
- [level](http://aheckmann.github.com/gm/docs.html#level)
- [list](http://aheckmann.github.com/gm/docs.html#list)
- [limit](http://aheckmann.github.com/gm/docs.html#limit)
- [log](http://aheckmann.github.com/gm/docs.html#log)
- [loop](http://aheckmann.github.com/gm/docs.html#loop)
- [lower](http://aheckmann.github.com/gm/docs.html#lower)
- [magnify](http://aheckmann.github.com/gm/docs.html#magnify)
- [map](http://aheckmann.github.com/gm/docs.html#map)
- [matte](http://aheckmann.github.com/gm/docs.html#matte)
- [matteColor](http://aheckmann.github.com/gm/docs.html#matteColor)
- [mask](http://aheckmann.github.com/gm/docs.html#mask)
- [maximumError](http://aheckmann.github.com/gm/docs.html#maximumError)
- [median](http://aheckmann.github.com/gm/docs.html#median)
- [minify](http://aheckmann.github.com/gm/docs.html#minify)
- [mode](http://aheckmann.github.com/gm/docs.html#mode)
- [modulate](http://aheckmann.github.com/gm/docs.html#modulate)
- [monitor](http://aheckmann.github.com/gm/docs.html#monitor)
- [monochrome](http://aheckmann.github.com/gm/docs.html#monochrome)
- [morph](http://aheckmann.github.com/gm/docs.html#morph)
- [mosaic](http://aheckmann.github.com/gm/docs.html#mosaic)
- [motionBlur](http://aheckmann.github.com/gm/docs.html#motionBlur)
- [name](http://aheckmann.github.com/gm/docs.html#name)
- [negative](http://aheckmann.github.com/gm/docs.html#negative)
- [noise](http://aheckmann.github.com/gm/docs.html#noise)
- [noop](http://aheckmann.github.com/gm/docs.html#noop)
- [normalize](http://aheckmann.github.com/gm/docs.html#normalize)
- [noProfile](http://aheckmann.github.com/gm/docs.html#profile)
- [opaque](http://aheckmann.github.com/gm/docs.html#opaque)
- [operator](http://aheckmann.github.com/gm/docs.html#operator)
- [orderedDither](http://aheckmann.github.com/gm/docs.html#orderedDither)
- [outputDirectory](http://aheckmann.github.com/gm/docs.html#outputDirectory)
- [paint](http://aheckmann.github.com/gm/docs.html#paint)
- [page](http://aheckmann.github.com/gm/docs.html#page)
- [pause](http://aheckmann.github.com/gm/docs.html#pause)
- [pen](http://aheckmann.github.com/gm/docs.html#pen)
- [ping](http://aheckmann.github.com/gm/docs.html#ping)
- [pointSize](http://aheckmann.github.com/gm/docs.html#pointSize)
- [preview](http://aheckmann.github.com/gm/docs.html#preview)
- [process](http://aheckmann.github.com/gm/docs.html#process)
- [profile](http://aheckmann.github.com/gm/docs.html#profile)
- [progress](http://aheckmann.github.com/gm/docs.html#progress)
- [quality](http://aheckmann.github.com/gm/docs.html#quality)
- [raise](http://aheckmann.github.com/gm/docs.html#raise)
- [rawSize](http://aheckmann.github.com/gm/docs.html#rawSize)
- [randomThreshold](http://aheckmann.github.com/gm/docs.html#randomThreshold)
- [recolor](http://aheckmann.github.com/gm/docs.html#recolor)
- [redPrimary](http://aheckmann.github.com/gm/docs.html#redPrimary)
- [region](http://aheckmann.github.com/gm/docs.html#region)
- [remote](http://aheckmann.github.com/gm/docs.html#remote)
- [render](http://aheckmann.github.com/gm/docs.html#render)
- [repage](http://aheckmann.github.com/gm/docs.html#repage)
- [resample](http://aheckmann.github.com/gm/docs.html#resample)
- [resize](http://aheckmann.github.com/gm/docs.html#resize)
- [roll](http://aheckmann.github.com/gm/docs.html#roll)
- [rotate](http://aheckmann.github.com/gm/docs.html#rotate)
- [sample](http://aheckmann.github.com/gm/docs.html#sample)
- [samplingFactor](http://aheckmann.github.com/gm/docs.html#samplingFactor)
- [scale](http://aheckmann.github.com/gm/docs.html#scale)
- [scene](http://aheckmann.github.com/gm/docs.html#scene)
- [scenes](http://aheckmann.github.com/gm/docs.html#scenes)
- [screen](http://aheckmann.github.com/gm/docs.html#screen)
- [segment](http://aheckmann.github.com/gm/docs.html#segment)
- [sepia](http://aheckmann.github.com/gm/docs.html#sepia)
- [set](http://aheckmann.github.com/gm/docs.html#set)
- [setFormat](http://aheckmann.github.com/gm/docs.html#setformat)
- [shade](http://aheckmann.github.com/gm/docs.html#shade)
- [shadow](http://aheckmann.github.com/gm/docs.html#shadow)
- [sharedMemory](http://aheckmann.github.com/gm/docs.html#sharedMemory)
- [sharpen](http://aheckmann.github.com/gm/docs.html#sharpen)
- [shave](http://aheckmann.github.com/gm/docs.html#shave)
- [shear](http://aheckmann.github.com/gm/docs.html#shear)
- [silent](http://aheckmann.github.com/gm/docs.html#silent)
- [solarize](http://aheckmann.github.com/gm/docs.html#solarize)
- [snaps](http://aheckmann.github.com/gm/docs.html#snaps)
- [stegano](http://aheckmann.github.com/gm/docs.html#stegano)
- [stereo](http://aheckmann.github.com/gm/docs.html#stereo)
- [strip](http://aheckmann.github.com/gm/docs.html#strip) _imagemagick only_
- [spread](http://aheckmann.github.com/gm/docs.html#spread)
- [swirl](http://aheckmann.github.com/gm/docs.html#swirl)
- [textFont](http://aheckmann.github.com/gm/docs.html#textFont)
- [texture](http://aheckmann.github.com/gm/docs.html#texture)
- [threshold](http://aheckmann.github.com/gm/docs.html#threshold)
- [thumb](http://aheckmann.github.com/gm/docs.html#thumb)
- [tile](http://aheckmann.github.com/gm/docs.html#tile)
- [transform](http://aheckmann.github.com/gm/docs.html#transform)
- [transparent](http://aheckmann.github.com/gm/docs.html#transparent)
- [treeDepth](http://aheckmann.github.com/gm/docs.html#treeDepth)
- [trim](http://aheckmann.github.com/gm/docs.html#trim)
- [type](http://aheckmann.github.com/gm/docs.html#type)
- [update](http://aheckmann.github.com/gm/docs.html#update)
- [units](http://aheckmann.github.com/gm/docs.html#units)
- [unsharp](http://aheckmann.github.com/gm/docs.html#unsharp)
- [usePixmap](http://aheckmann.github.com/gm/docs.html#usePixmap)
- [view](http://aheckmann.github.com/gm/docs.html#view)
- [virtualPixel](http://aheckmann.github.com/gm/docs.html#virtualPixel)
- [visual](http://aheckmann.github.com/gm/docs.html#visual)
- [watermark](http://aheckmann.github.com/gm/docs.html#watermark)
- [wave](http://aheckmann.github.com/gm/docs.html#wave)
- [whitePoint](http://aheckmann.github.com/gm/docs.html#whitePoint)
- [whiteThreshold](http://aheckmann.github.com/gm/docs.html#whiteThreshold)
- [window](http://aheckmann.github.com/gm/docs.html#window)
- [windowGroup](http://aheckmann.github.com/gm/docs.html#windowGroup)
- drawing primitives
- [draw](http://aheckmann.github.com/gm/docs.html#draw)
- [drawArc](http://aheckmann.github.com/gm/docs.html#drawArc)
- [drawBezier](http://aheckmann.github.com/gm/docs.html#drawBezier)
- [drawCircle](http://aheckmann.github.com/gm/docs.html#drawCircle)
- [drawEllipse](http://aheckmann.github.com/gm/docs.html#drawEllipse)
- [drawLine](http://aheckmann.github.com/gm/docs.html#drawLine)
- [drawPoint](http://aheckmann.github.com/gm/docs.html#drawPoint)
- [drawPolygon](http://aheckmann.github.com/gm/docs.html#drawPolygon)
- [drawPolyline](http://aheckmann.github.com/gm/docs.html#drawPolyline)
- [drawRectangle](http://aheckmann.github.com/gm/docs.html#drawRectangle)
- [drawText](http://aheckmann.github.com/gm/docs.html#drawText)
- [fill](http://aheckmann.github.com/gm/docs.html#fill)
- [font](http://aheckmann.github.com/gm/docs.html#font)
- [fontSize](http://aheckmann.github.com/gm/docs.html#fontSize)
- [stroke](http://aheckmann.github.com/gm/docs.html#stroke)
- [strokeWidth](http://aheckmann.github.com/gm/docs.html#strokeWidth)
- [setDraw](http://aheckmann.github.com/gm/docs.html#setDraw)
- image output
- **write** - writes the processed image data to the specified filename
- **stream** - provides a `ReadableStream` with the processed image data
- **toBuffer** - returns the image as a `Buffer` instead of a stream
##compare
Graphicsmagicks `compare` command is exposed through `gm.compare()`. This allows us to determine if two images can be considered "equal".
Currently `gm.compare` only accepts file paths.
gm.compare(path1, path2 [, options], callback)
```js
gm.compare('/path/to/image1.jpg', '/path/to/another.png', function (err, isEqual, equality, raw, path1, path2) {
if (err) return handle(err);
// if the images were considered equal, `isEqual` will be true, otherwise, false.
console.log('The images were equal: %s', isEqual);
// to see the total equality returned by graphicsmagick we can inspect the `equality` argument.
console.log('Actual equality: %d', equality);
// inspect the raw output
console.log(raw);
// print file paths
console.log(path1, path2);
})
```
You may wish to pass a custom tolerance threshold to increase or decrease the default level of `0.4`.
```js
gm.compare('/path/to/image1.jpg', '/path/to/another.png', 1.2, function (err, isEqual) {
...
})
```
To output a diff image, pass a configuration object to define the diff options and tolerance.
```js
var options = {
file: '/path/to/diff.png',
highlightColor: 'yellow',
tolerance: 0.02
}
gm.compare('/path/to/image1.jpg', '/path/to/another.png', options, function (err, isEqual, equality, raw) {
...
})
```
##composite
GraphicsMagick supports compositing one image on top of another. This is exposed through `gm.composite()`. Its first argument is an image path with the changes to the base image, and an optional mask image.
Currently, `gm.composite()` only accepts file paths.
gm.composite(other [, mask])
```js
gm('/path/to/image.jpg')
.composite('/path/to/second_image.jpg')
.geometry('+100+150')
.write('/path/to/composite.png', function(err) {
if(!err) console.log("Written composite image.");
});
```
##montage
GraphicsMagick supports montage for combining images side by side. This is exposed through `gm.montage()`. Its only argument is an image path with the changes to the base image.
Currently, `gm.montage()` only accepts file paths.
gm.montage(other)
```js
gm('/path/to/image.jpg')
.montage('/path/to/second_image.jpg')
.geometry('+100+150')
.write('/path/to/montage.png', function(err) {
if(!err) console.log("Written montage image.");
});
```
## Contributors
[https://github.com/aheckmann/gm/contributors](https://github.com/aheckmann/gm/contributors)
## Inspiration
http://github.com/quiiver/magickal-node
## Plugins
[https://github.com/aheckmann/gm/wiki](https://github.com/aheckmann/gm/wiki)
## License
(The MIT License)
Copyright (c) 2010 [Aaron Heckmann](aaron.heckmann+github@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Generated Vendored
+129
View File
@@ -0,0 +1,129 @@
/**
* Module dependencies.
*/
var Stream = require('stream').Stream;
var EventEmitter = require('events').EventEmitter;
var util = require('util');
util.inherits(gm, EventEmitter);
/**
* Constructor.
*
* @param {String|Number} path - path to img source or ReadableStream or width of img to create
* @param {Number} [height] - optional filename of ReadableStream or height of img to create
* @param {String} [color] - optional hex background color of created img
*/
function gm (source, height, color) {
var width;
if (!(this instanceof gm)) {
return new gm(source, height, color);
}
EventEmitter.call(this);
this._options = {};
this.options(this.__proto__._options);
this.data = {};
this._in = [];
this._out = [];
this._outputFormat = null;
this._subCommand = 'convert';
if (source instanceof Stream) {
this.sourceStream = source;
source = height || 'unknown.jpg';
} else if (Buffer.isBuffer(source)) {
this.sourceBuffer = source;
source = height || 'unknown.jpg';
} else if (height) {
// new images
width = source;
source = "";
this.in("-size", width + "x" + height);
if (color) {
this.in("xc:"+ color);
}
}
if (typeof source === "string") {
// then source is a path
// parse out gif frame brackets from filename
// since stream doesn't use source path
// eg. "filename.gif[0]"
var frames = source.match(/(\[.+\])$/);
if (frames) {
this.sourceFrames = source.substr(frames.index, frames[0].length);
source = source.substr(0, frames.index);
}
}
this.source = source;
this.addSrcFormatter(function (src) {
// must be first source formatter
var inputFromStdin = this.sourceStream || this.sourceBuffer;
var ret = inputFromStdin ? '-' : this.source;
if (ret && this.sourceFrames) ret += this.sourceFrames;
src.length = 0;
src[0] = ret;
});
}
/**
* Subclasses the gm constructor with custom options.
*
* @param {options} options
* @return {gm} the subclasses gm constructor
*/
var parent = gm;
gm.subClass = function subClass (options) {
function gm (source, height, color) {
if (!(this instanceof parent)) {
return new gm(source, height, color);
}
parent.call(this, source, height, color);
}
gm.prototype.__proto__ = parent.prototype;
gm.prototype._options = {};
gm.prototype.options(options);
return gm;
}
/**
* Augment the prototype.
*/
require("./lib/options")(gm.prototype);
require("./lib/getters")(gm);
require("./lib/args")(gm.prototype);
require("./lib/drawing")(gm.prototype);
require("./lib/convenience")(gm.prototype);
require("./lib/command")(gm.prototype);
require("./lib/compare")(gm.prototype);
require("./lib/composite")(gm.prototype);
require("./lib/montage")(gm.prototype);
/**
* Expose.
*/
module.exports = exports = gm;
module.exports.utils = require('./lib/utils');
module.exports.compare = require('./lib/compare')();
module.exports.version = require('./package.json').version;
+1122
View File
File diff suppressed because it is too large Load Diff
+446
View File
@@ -0,0 +1,446 @@
/**
* Module dependencies.
*/
var spawn = require('cross-spawn');
var utils = require('./utils');
var debug = require('debug')('gm');
var series = require('array-series');
var PassThrough = require('stream').PassThrough;
/**
* Error messaging.
*/
var noBufferConcat = 'gm v1.9.0+ required node v0.8+. Please update your version of node, downgrade gm < 1.9, or do not use `bufferStream`.';
/**
* Extend proto
*/
module.exports = function (proto) {
function args (prop) {
return function args () {
var len = arguments.length;
var a = [];
var i = 0;
for (; i < len; ++i) {
a.push(arguments[i]);
}
this[prop] = this[prop].concat(a);
return this;
}
}
function streamToUnemptyBuffer(stream, callback) {
var done = false
var buffers = []
stream.on('data', function (data) {
buffers.push(data)
})
stream.on('end', function () {
var result, err;
if (done)
return
done = true
result = Buffer.concat(buffers)
buffers = null
if (result.length==0)
{
err = new Error("Stream yields empty buffer");
callback(err, null);
} else {
callback(null, result);
}
})
stream.on('error', function (err) {
done = true
buffers = null
callback(err)
})
}
proto.in = args('_in');
proto.out = args('_out');
proto._preprocessor = [];
proto.preprocessor = args('_preprocessor');
/**
* Execute the command and write the image to the specified file name.
*
* @param {String} name
* @param {Function} callback
* @return {Object} gm
*/
proto.write = function write (name, callback) {
if (!callback) callback = name, name = null;
if ("function" !== typeof callback) {
throw new TypeError("gm().write() expects a callback function")
}
if (!name) {
return callback(TypeError("gm().write() expects a filename when writing new files"));
}
this.outname = name;
var self = this;
this._preprocess(function (err) {
if (err) return callback(err);
self._spawn(self.args(), true, callback);
});
}
/**
* Execute the command and return stdin and stderr
* ReadableStreams providing the image data.
* If no callback is passed, a "through" stream will be returned,
* and stdout will be piped through, otherwise the error will be passed.
*
* @param {String} format (optional)
* @param {Function} callback (optional)
* @return {Stream}
*/
proto.stream = function stream (format, callback) {
if (!callback && typeof format === 'function') {
callback = format;
format = null;
}
var throughStream;
if ("function" !== typeof callback) {
throughStream = new PassThrough();
callback = function (err, stdout, stderr) {
if (err) throughStream.emit('error', err);
else stdout.pipe(throughStream);
}
}
if (format) {
format = format.split('.').pop();
this.outname = format + ":-";
}
var self = this;
this._preprocess(function (err) {
if (err) return callback(err);
return self._spawn(self.args(), false, callback);
});
return throughStream || this;
}
/**
* Convenience function for `proto.stream`.
* Simply returns the buffer instead of the stream.
*
* @param {String} format (optional)
* @param {Function} callback
* @return {null}
*/
proto.toBuffer = function toBuffer (format, callback) {
if (!callback) callback = format, format = null;
if ("function" !== typeof callback) {
throw new Error('gm().toBuffer() expects a callback.');
}
return this.stream(format, function (err, stdout) {
if (err) return callback(err);
streamToUnemptyBuffer(stdout, callback);
})
}
/**
* Run any preProcessor functions in series. Used by autoOrient.
*
* @param {Function} callback
* @return {Object} gm
*/
proto._preprocess = function _preprocess (callback) {
series(this._preprocessor, this, callback);
}
/**
* Execute the command, buffer input and output, return stdout and stderr buffers.
*
* @param {String} bin
* @param {Array} args
* @param {Function} callback
* @return {Object} gm
*/
proto._exec = function _exec (args, callback) {
return this._spawn(args, true, callback);
}
/**
* Execute the command with stdin, returning stdout and stderr streams or buffers.
* @param {String} bin
* @param {Array} args
* @param {ReadableStream} stream
* @param {Boolean} shouldBuffer
* @param {Function} callback, signature (err, stdout, stderr) -> *
* @return {Object} gm
* @TODO refactor this mess
*/
proto._spawn = function _spawn (args, bufferOutput, callback) {
var appPath = this._options.appPath || '';
var bin = this._options.imageMagick
? appPath + args.shift()
: appPath + 'gm'
var cmd = bin + ' ' + args.map(utils.escape).join(' ')
, self = this
, proc, err
, timeout = parseInt(this._options.timeout)
, disposers = this._options.disposers
, timeoutId;
debug(cmd);
//imageMagick does not support minify (https://github.com/aheckmann/gm/issues/385)
if(args.indexOf("-minify") > -1 && this._options.imageMagick){
err = new Error("imageMagick does not support minify, use -scale or -sample. Alternatively, use graphicsMagick");
return cb(err);
}
try {
proc = spawn(bin, args);
} catch (e) {
return cb(e);
}
proc.stdin.once('error', cb);
proc.on('error', function(err){
if (err.code === 'ENOENT') {
cb(new Error('Could not execute GraphicsMagick/ImageMagick: '+cmd+" this most likely means the gm/convert binaries can't be found"));
} else {
cb(err);
}
});
if (timeout) {
timeoutId = setTimeout(function(){
dispose('gm() resulted in a timeout.');
}, timeout);
}
if (disposers) {
disposers.forEach(function(disposer) {
disposer.events.forEach(function(event) {
disposer.emitter.on(event, dispose);
});
});
}
if (self.sourceBuffer) {
proc.stdin.write(this.sourceBuffer);
proc.stdin.end();
} else if (self.sourceStream) {
if (!self.sourceStream.readable) {
err = new Error("gm().stream() or gm().write() with a non-readable stream.");
return cb(err);
}
self.sourceStream.pipe(proc.stdin);
// bufferStream
// We convert the input source from a stream to a buffer.
if (self.bufferStream && !this._buffering) {
if (!Buffer.concat) {
throw new Error(noBufferConcat);
}
// Incase there are multiple processes in parallel,
// we only need one
self._buffering = true;
streamToUnemptyBuffer(self.sourceStream, function (err, buffer) {
self.sourceBuffer = buffer;
self.sourceStream = null; // The stream is now dead
})
}
}
// for _exec operations (identify() mostly), we also
// need to buffer the output stream before returning
if (bufferOutput) {
var stdout = ''
, stderr = ''
, onOut
, onErr
, onExit
proc.stdout.on('data', onOut = function (data) {
stdout += data;
});
proc.stderr.on('data', onErr = function (data) {
stderr += data;
});
proc.on('close', onExit = function (code, signal) {
if (code !== 0 || signal !== null) {
err = new Error('Command failed: ' + stderr);
err.code = code;
err.signal = signal;
};
cb(err, stdout, stderr, cmd);
stdout = stderr = onOut = onErr = onExit = null;
});
} else {
cb(null, proc.stdout, proc.stderr, cmd);
}
return self;
function cb (err, stdout, stderr, cmd) {
if (cb.called) return;
if (timeoutId) clearTimeout(timeoutId);
cb.called = 1;
if (args[0] !== 'identify' && bin !== 'identify') {
self._in = [];
self._out = [];
}
callback.call(self, err, stdout, stderr, cmd);
}
function dispose (msg) {
var message = msg ? msg : 'gm() was disposed';
err = new Error(message);
cb(err);
if (proc.exitCode === null) {
proc.stdin.pause();
proc.kill();
}
}
}
/**
* Returns arguments to be used in the command.
*
* @return {Array}
*/
proto.args = function args () {
var outname = this.outname || "-";
if (this._outputFormat) outname = this._outputFormat + ':' + outname;
return [].concat(
this._subCommand
, this._in
, this.src()
, this._out
, outname
).filter(Boolean); // remove falsey
}
/**
* Adds an img source formatter.
*
* `formatters` are passed an array of images which will be
* used as 'input' images for the command. Useful for methods
* like `.append()` where multiple source images may be used.
*
* @param {Function} formatter
* @return {gm} this
*/
proto.addSrcFormatter = function addSrcFormatter (formatter) {
if ('function' != typeof formatter)
throw new TypeError('sourceFormatter must be a function');
this._sourceFormatters || (this._sourceFormatters = []);
this._sourceFormatters.push(formatter);
return this;
}
/**
* Applies all _sourceFormatters
*
* @return {Array}
*/
proto.src = function src () {
var arr = [];
for (var i = 0; i < this._sourceFormatters.length; ++i) {
this._sourceFormatters[i].call(this, arr);
}
return arr;
}
/**
* Image types.
*/
var types = {
'jpg': /\.jpe?g$/i
, 'png' : /\.png$/i
, 'gif' : /\.gif$/i
, 'tiff': /\.tif?f$/i
, 'bmp' : /(?:\.bmp|\.dib)$/i
, 'webp': /\.webp$/i
};
types.jpeg = types.jpg;
types.tif = types.tiff;
types.dib = types.bmp;
/**
* Determine the type of source image.
*
* @param {String} type
* @return {Boolean}
* @example
* if (this.inputIs('png')) ...
*/
proto.inputIs = function inputIs (type) {
if (!type) return false;
var rgx = types[type];
if (!rgx) {
if ('.' !== type[0]) type = '.' + type;
rgx = new RegExp('\\' + type + '$', 'i');
}
return rgx.test(this.source);
}
/**
* add disposer (like 'close' of http.IncomingMessage) in order to dispose gm() with any event
*
* @param {EventEmitter} emitter
* @param {Array} events
* @return {Object} gm
* @example
* command.addDisposer(req, ['close', 'end', 'finish']);
*/
proto.addDisposer = function addDisposer (emitter, events) {
if (!this._options.disposers) {
this._options.disposers = [];
}
this._options.disposers.push({
emitter: emitter,
events: events
});
return this;
};
}
+120
View File
@@ -0,0 +1,120 @@
// compare
var spawn = require('cross-spawn');
/**
* Compare two images uses graphicsmagicks `compare` command.
*
* gm.compare(img1, img2, 0.4, function (err, equal, equality) {
* if (err) return handle(err);
* console.log('The images are equal: %s', equal);
* console.log('There equality was %d', equality);
* });
*
* @param {String} orig Path to an image.
* @param {String} compareTo Path to another image to compare to `orig`.
* @param {Number|Object} [options] Options object or the amount of difference to tolerate before failing - defaults to 0.4
* @param {Function} cb(err, Boolean, equality, rawOutput)
*/
module.exports = exports = function (proto) {
function compare(orig, compareTo, options, cb) {
var isImageMagick = this._options && this._options.imageMagick;
var appPath = this._options && this._options.appPath || '';
var bin = isImageMagick
? appPath + 'compare'
: appPath + 'gm'
var args = ['-metric', 'mse', orig, compareTo]
if (!isImageMagick) {
args.unshift('compare');
}
var tolerance = 0.4;
// outputting the diff image
if (typeof options === 'object') {
if (options.highlightColor && options.highlightColor.indexOf('"') < 0) {
options.highlightColor = '"' + options.highlightColor + '"';
}
if (options.file) {
if (typeof options.file !== 'string') {
throw new TypeError('The path for the diff output is invalid');
}
// graphicsmagick defaults to red
if (options.highlightColor) {
args.push('-highlight-color');
args.push(options.highlightColor);
}
if (options.highlightStyle) {
args.push('-highlight-style')
args.push(options.highlightStyle)
}
// For IM, filename is the last argument. For GM it's `-file <filename>`
if (!isImageMagick) {
args.push('-file');
}
args.push(options.file);
}
if (typeof options.tolerance != 'undefined') {
if (typeof options.tolerance !== 'number') {
throw new TypeError('The tolerance value should be a number');
}
tolerance = options.tolerance;
}
} else {
// For ImageMagick diff file is required but we don't care about it, so null it out
if (isImageMagick) {
args.push('null:');
}
if (typeof options == 'function') {
cb = options; // tolerance value not provided, flip the cb place
} else {
tolerance = options
}
}
var proc = spawn(bin, args);
var stdout = '';
var stderr = '';
proc.stdout.on('data',function(data) { stdout+=data });
proc.stderr.on('data',function(data) { stderr+=data });
proc.on('close', function (code) {
// ImageMagick returns err code 2 if err, 0 if similar, 1 if dissimilar
if (isImageMagick) {
if (code === 0) {
return cb(null, 0 <= tolerance, 0, stdout);
}
else if (code === 1) {
err = null;
stdout = stderr;
} else {
return cb(stderr);
}
} else {
if(code !== 0) {
return cb(stderr);
}
}
// Since ImageMagick similar gives err code 0 and no stdout, there's really no matching
// Otherwise, output format for IM is `12.00 (0.123)` and for GM it's `Total: 0.123`
var regex = isImageMagick ? /\((\d+\.?[\d\-\+e]*)\)/m : /Total: (\d+\.?\d*)/m;
var match = regex.exec(stdout);
if (!match) {
err = new Error('Unable to parse output.\nGot ' + stdout);
return cb(err);
}
var equality = parseFloat(match[1]);
cb(null, equality <= tolerance, equality, stdout, orig, compareTo);
});
}
if (proto) {
proto.compare = compare;
}
return compare;
};
+29
View File
@@ -0,0 +1,29 @@
// composite
/**
* Composite images together using the `composite` command in graphicsmagick.
*
* gm('/path/to/image.jpg')
* .composite('/path/to/second_image.jpg')
* .geometry('+100+150')
* .write('/path/to/composite.png', function(err) {
* if(!err) console.log("Written composite image.");
* });
*
* @param {String} other Path to the image that contains the changes.
* @param {String} [mask] Path to the image with opacity informtion. Grayscale.
*/
module.exports = exports = function(proto) {
proto.composite = function(other, mask) {
this.in(other);
// If the mask is defined, add it to the output.
if(typeof mask !== "undefined")
this.out(mask);
this.subCommand("composite");
return this;
}
}
+11
View File
@@ -0,0 +1,11 @@
/**
* Extend proto
*/
module.exports = function (proto) {
require("./convenience/thumb")(proto);
require("./convenience/morph")(proto);
require("./convenience/sepia")(proto);
require("./convenience/autoOrient")(proto);
}
+57
View File
@@ -0,0 +1,57 @@
/**
* Extend proto.
*/
module.exports = function (proto) {
var exifTransforms = {
topleft: ''
, topright: ['-flop']
, bottomright: ['-rotate', 180]
, bottomleft: ['-flip']
, lefttop: ['-flip', '-rotate', 90]
, righttop: ['-rotate', 90]
, rightbottom: ['-flop', '-rotate', 90]
, leftbottom: ['-rotate', 270]
}
proto.autoOrient = function autoOrient () {
// Always strip EXIF data since we can't
// change/edit it.
// imagemagick has a native -auto-orient option
// so does graphicsmagick, but in 1.3.18.
// nativeAutoOrient option enables this if you know you have >= 1.3.18
if (this._options.nativeAutoOrient || this._options.imageMagick) {
this.out('-auto-orient');
this.strip();
return this;
}
this.preprocessor(function (callback) {
this.orientation({bufferStream: true}, function (err, orientation) {
if (err) return callback(err);
var transforms = exifTransforms[orientation.toLowerCase()];
if (transforms) {
// remove any existing transforms that might conflict
var index = this._out.indexOf(transforms[0]);
if (~index) {
this._out.splice(index, transforms.length);
}
// repage to fix coordinates
this._out.unshift.apply(this._out, transforms.concat('-page', '+0+0'));
}
this.strip();
callback();
});
});
return this;
}
}
+61
View File
@@ -0,0 +1,61 @@
/**
* Module dependencies.
*/
var fs = require('fs');
var parallel = require('array-parallel');
/**
* Extend proto.
*/
module.exports = function (proto) {
/**
* Do nothing.
*/
function noop () {}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-morph
proto.morph = function morph (other, outname, callback) {
if (!outname) {
throw new Error("an output filename is required");
}
callback = (callback || noop).bind(this)
var self = this;
if (Array.isArray(other)) {
other.forEach(function (img) {
self.out(img);
});
self.out("-morph", other.length);
} else {
self.out(other, "-morph", 1);
}
self.write(outname, function (err, stdout, stderr, cmd) {
if (err) return callback(err, stdout, stderr, cmd);
// Apparently some platforms create the following temporary files.
// Check if the output file exists, if it doesn't, then
// work with temporary files.
fs.exists(outname, function (exists) {
if (exists) return callback(null, stdout, stderr, cmd);
parallel([
fs.unlink.bind(fs, outname + '.0'),
fs.unlink.bind(fs, outname + '.2'),
fs.rename.bind(fs, outname + '.1', outname)
], function (err) {
callback(err, stdout, stderr, cmd);
})
})
});
return self;
}
}
+10
View File
@@ -0,0 +1,10 @@
/**
* Extend proto.
*/
module.exports = function (proto) {
proto.sepia = function sepia () {
return this.modulate(115, 0, 100).colorize(7, 21, 50);
}
}
+92
View File
@@ -0,0 +1,92 @@
/**
* Extend proto.
*/
module.exports = function (proto) {
proto.thumb = function thumb (w, h, name, quality, align, progressive, callback, opts) {
var self = this,
args = Array.prototype.slice.call(arguments);
opts = args.pop();
if (typeof opts === 'function') {
callback = opts;
opts = '';
} else {
callback = args.pop();
}
w = args.shift();
h = args.shift();
name = args.shift();
quality = args.shift() || 63;
align = args.shift() || 'topleft';
var interlace = args.shift() ? 'Line' : 'None';
self.size(function (err, size) {
if (err) {
return callback.apply(self, arguments);
}
w = parseInt(w, 10);
h = parseInt(h, 10);
var w1, h1;
var xoffset = 0;
var yoffset = 0;
if (size.width < size.height) {
w1 = w;
h1 = Math.floor(size.height * (w/size.width));
if (h1 < h) {
w1 = Math.floor(w1 * (((h-h1)/h) + 1));
h1 = h;
}
} else if (size.width > size.height) {
h1 = h;
w1 = Math.floor(size.width * (h/size.height));
if (w1 < w) {
h1 = Math.floor(h1 * (((w-w1)/w) + 1));
w1 = w;
}
} else if (size.width == size.height) {
var bigger = (w>h?w:h);
w1 = bigger;
h1 = bigger;
}
if (align == 'center') {
if (w < w1) {
xoffset = (w1-w)/2;
}
if (h < h1) {
yoffset = (h1-h)/2;
}
}
self
.quality(quality)
.in("-size", w1+"x"+h1)
.scale(w1, h1, opts)
.crop(w, h, xoffset, yoffset)
.interlace(interlace)
.noProfile()
.write(name, function () {
callback.apply(self, arguments);
});
});
return self;
};
proto.thumbExact = function () {
var self = this,
args = Array.prototype.slice.call(arguments);
args.push('!');
self.thumb.apply(self, args);
};
};
+160
View File
@@ -0,0 +1,160 @@
/**
* Module dependencies.
*/
var escape = require('./utils').escape;
/**
* Extend proto.
*/
module.exports = function (proto) {
// http://www.graphicsmagick.org/GraphicsMagick.html#details-fill
proto.fill = function fill (color) {
return this.out("-fill", color || "none");
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-stroke
proto.stroke = function stroke (color, width) {
if (width) {
this.strokeWidth(width);
}
return this.out("-stroke", color || "none");
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-strokewidth
proto.strokeWidth = function strokeWidth (width) {
return this.out("-strokewidth", width);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-font
proto.font = function font (font, size) {
if (size) {
this.fontSize(size);
}
return this.out("-font", font);
}
// http://www.graphicsmagick.org/GraphicsMagick.html
proto.fontSize = function fontSize (size) {
return this.out("-pointsize", size);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.draw = function draw (args) {
return this.out("-draw", [].slice.call(arguments).join(" "));
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawPoint = function drawPoint (x, y) {
return this.draw("point", x +","+ y);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawLine = function drawLine (x0, y0, x1, y1) {
return this.draw("line", x0+","+y0, x1+","+y1);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawRectangle = function drawRectangle (x0, y0, x1, y1, wc, hc) {
var shape = "rectangle"
, lastarg;
if ("undefined" !== typeof wc) {
shape = "roundRectangle";
if ("undefined" === typeof hc) {
hc = wc;
}
lastarg = wc+","+hc;
}
return this.draw(shape, x0+","+y0, x1+","+y1, lastarg);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawArc = function drawArc (x0, y0, x1, y1, a0, a1) {
return this.draw("arc", x0+","+y0, x1+","+y1, a0+","+a1);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawEllipse = function drawEllipse (x0, y0, rx, ry, a0, a1) {
if (a0 == undefined) a0 = 0;
if (a1 == undefined) a1 = 360;
return this.draw("ellipse", x0+","+y0, rx+","+ry, a0+","+a1);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawCircle = function drawCircle (x0, y0, x1, y1) {
return this.draw("circle", x0+","+y0, x1+","+y1);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawPolyline = function drawPolyline () {
return this.draw("polyline", formatPoints(arguments));
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawPolygon = function drawPolygon () {
return this.draw("polygon", formatPoints(arguments));
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawBezier = function drawBezier () {
return this.draw("bezier", formatPoints(arguments));
}
proto._gravities = [
"northwest"
, "north"
, "northeast"
, "west"
, "center"
, "east"
, "southwest"
, "south"
, "southeast"];
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawText = function drawText (x0, y0, text, gravity) {
var gravity = String(gravity || "").toLowerCase()
, arg = ["text " + x0 + "," + y0 + " " + escape(text)];
if (~this._gravities.indexOf(gravity)) {
arg.unshift("gravity", gravity);
}
return this.draw.apply(this, arg);
}
proto._drawProps = ["color", "matte"];
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.setDraw = function setDraw (prop, x, y, method) {
prop = String(prop || "").toLowerCase();
if (!~this._drawProps.indexOf(prop)) {
return this;
}
return this.draw(prop, x+","+y, method);
}
}
function formatPoints (points) {
var len = points.length
, result = []
, i = 0;
for (; i < len; ++i) {
result.push(points[i].join(","));
}
return result;
}
Generated Vendored Executable
+346
View File
@@ -0,0 +1,346 @@
/**
* Extend proto.
*/
module.exports = function (gm) {
var proto = gm.prototype;
/**
* `identify` states
*/
const IDENTIFYING = 1;
const IDENTIFIED = 2;
/**
* Map getter functions to output names.
*
* - format: specifying the -format argument (see man gm)
* - verbose: use -verbose instead of -format (only if necessary b/c its slow)
* - helper: use the conversion helper
*/
var map = {
'format': { key: 'format', format: '%m ', helper: 'Format' }
, 'depth': { key: 'depth', format: '%q' }
, 'filesize': { key: 'Filesize', format: '%b' }
, 'size': { key: 'size', format: '%wx%h ', helper: 'Geometry' }
, 'color': { key: 'color', format: '%k', helper: 'Colors' }
, 'orientation': { key: 'Orientation', format: '%[EXIF:Orientation]', helper: 'Orientation' }
, 'res': { key: 'Resolution', verbose: true }
}
/**
* Getter functions
*/
Object.keys(map).forEach(function (getter) {
proto[getter] = function (opts, callback) {
if (!callback) callback = opts, opts = {};
if (!callback) return this;
var val = map[getter]
, key = val.key
, self = this;
if (self.data[key]) {
callback.call(self, null, self.data[key]);
return self;
}
self.on(getter, callback);
self.bufferStream = !!opts.bufferStream;
if (val.verbose) {
self.identify(opts, function (err, stdout, stderr, cmd) {
if (err) {
self.emit(getter, err, self.data[key], stdout, stderr, cmd);
} else {
self.emit(getter, err, self.data[key]);
}
});
return self;
}
var args = makeArgs(self, val);
self._exec(args, function (err, stdout, stderr, cmd) {
if (err) {
self.emit(getter, err, self.data[key], stdout, stderr, cmd);
return;
}
var result = (stdout||'').trim();
if (val.helper in helper) {
helper[val.helper](self.data, result);
} else {
self.data[key] = result;
}
self.emit(getter, err, self.data[key]);
});
return self;
}
});
/**
* identify command
*
* Overwrites all internal data with the parsed output
* which is more accurate than the fast shortcut
* getters.
*/
proto.identify = function identify (opts, callback) {
// identify with pattern
if (typeof(opts) === 'string') {
opts = {
format: opts
}
}
if (!callback) callback = opts, opts = {};
if (!callback) return this;
if (opts && opts.format) return identifyPattern.call(this, opts, callback);
var self = this;
if (IDENTIFIED === self._identifyState) {
callback.call(self, null, self.data);
return self;
}
self.on('identify', callback);
if (IDENTIFYING === self._identifyState) {
return self;
}
self._identifyState = IDENTIFYING;
self.bufferStream = !!opts.bufferStream;
var args = makeArgs(self, { verbose: true });
self._exec(args, function (err, stdout, stderr, cmd) {
if (err) {
self.emit('identify', err, self.data, stdout, stderr, cmd);
return;
}
err = parse(stdout, self);
if (err) {
self.emit('identify', err, self.data, stdout, stderr, cmd);
return;
}
self.data.path = self.source;
self.emit('identify', null, self.data);
self._identifyState = IDENTIFIED;
});
return self;
}
/**
* identify with pattern
*
* Execute `identify -format` with custom pattern
*/
function identifyPattern (opts, callback) {
var self = this;
self.bufferStream = !!opts.bufferStream;
var args = makeArgs(self, opts);
self._exec(args, function (err, stdout, stderr, cmd) {
if (err) {
return callback.call(self, err, undefined, stdout, stderr, cmd);
}
callback.call(self, err, (stdout||'').trim());
});
return self;
}
/**
* Parses `identify` responses.
*
* @param {String} stdout
* @param {Gm} self
* @return {Error} [optionally]
*/
function parse (stdout, self) {
// normalize
var parts = (stdout||"").trim().replace(/\r\n|\r/g, "\n").split("\n");
// skip the first line (its just the filename)
parts.shift();
try {
var len = parts.length
, rgx1 = /^( *)(.+?): (.*)$/ // key: val
, rgx2 = /^( *)(.+?):$/ // key: begin nested object
, out = { indent: {} }
, level = null
, lastkey
, i = 0
, res
, o
for (; i < len; ++i) {
res = rgx1.exec(parts[i]) || rgx2.exec(parts[i]);
if (!res) continue;
var indent = res[1].length
, key = res[2] ? res[2].trim() : '';
if ('Image' == key || 'Warning' == key) continue;
var val = res[3] ? res[3].trim() : null;
// first iteration?
if (null === level) {
level = indent;
o = out.root = out.indent[level] = self.data;
} else if (indent < level) {
// outdent
if (!(indent in out.indent)) {
continue;
}
o = out.indent[indent];
} else if (indent > level) {
// dropping into a nested object
out.indent[level] = o;
// weird format, key/val pair with nested children. discard the val
o = o[lastkey] = {};
}
level = indent;
if (val) {
// if previous key was exist and we got the same key
// cast it to an array.
if(o.hasOwnProperty(key)){
// cast it to an array and dont forget the previous value
if(!Array.isArray(o[key])){
var tmp = o[key];
o[key] = [tmp];
}
// set value
o[key].push(val);
} else {
o[key] = val;
}
if (key in helper) {
helper[key](o, val);
}
}
lastkey = key;
}
} catch (err) {
err.message = err.message + "\n\n Identify stdout:\n " + stdout;
return err;
}
}
/**
* Create an argument array for the identify command.
*
* @param {gm} self
* @param {Object} val
* @return {Array}
*/
function makeArgs (self, val) {
var args = [
'identify'
, '-ping'
];
if (val.format) {
args.push('-format', val.format);
}
if (val.verbose) {
args.push('-verbose');
}
args = args.concat(self.src());
return args;
}
/**
* Map exif orientation codes to orientation names.
*/
var orientations = {
'1': 'TopLeft'
, '2': 'TopRight'
, '3': 'BottomRight'
, '4': 'BottomLeft'
, '5': 'LeftTop'
, '6': 'RightTop'
, '7': 'RightBottom'
, '8': 'LeftBottom'
}
/**
* identify -verbose helpers
*/
var helper = gm.identifyHelpers = {};
helper.Geometry = function Geometry (o, val) {
// We only want the size of the first frame.
// Each frame is separated by a space.
var split = val.split(" ").shift().split("x");
var width = parseInt(split[0], 10);
var height = parseInt(split[1], 10);
if (o.size && o.size.width && o.size.height) {
if (width > o.size.width) o.size.width = width;
if (height > o.size.height) o.size.height = height;
} else {
o.size = {
width: width,
height: height
}
}
};
helper.Format = function Format (o, val) {
o.format = val.split(" ")[0];
};
helper.Depth = function Depth (o, val) {
o.depth = parseInt(val, 10);
};
helper.Colors = function Colors (o, val) {
o.color = parseInt(val, 10);
};
helper.Orientation = function Orientation (o, val) {
if (val in orientations) {
o['Profile-EXIF'] || (o['Profile-EXIF'] = {});
o['Profile-EXIF'].Orientation = val;
o.Orientation = orientations[val];
} else {
o.Orientation = val || 'Unknown';
}
};
}
+24
View File
@@ -0,0 +1,24 @@
// montage
/**
* Montage images next to each other using the `montage` command in graphicsmagick.
*
* gm('/path/to/image.jpg')
* .montage('/path/to/second_image.jpg')
* .geometry('+100+150')
* .write('/path/to/montage.png', function(err) {
* if(!err) console.log("Written montage image.");
* });
*
* @param {String} other Path to the image that contains the changes.
*/
module.exports = exports = function(proto) {
proto.montage = function(other) {
this.in(other);
this.subCommand("montage");
return this;
}
}
+17
View File
@@ -0,0 +1,17 @@
module.exports = exports = function (proto) {
proto._options = {};
proto.options = function setOptions (options) {
var keys = Object.keys(options)
, i = keys.length
, key
while (i--) {
key = keys[i];
this._options[key] = options[key];
}
return this;
}
}
+43
View File
@@ -0,0 +1,43 @@
/**
* Escape the given shell `arg`.
*
* @param {String} arg
* @return {String}
* @api public
*/
exports.escape = function escape (arg) {
return '"' + String(arg).trim().replace(/"/g, '\\"') + '"';
};
exports.unescape = function escape (arg) {
return String(arg).trim().replace(/"/g, "");
};
exports.argsToArray = function (args) {
var arr = [];
for (var i = 0; i <= arguments.length; i++) {
if ('undefined' != typeof arguments[i])
arr.push(arguments[i]);
}
return arr;
};
exports.isUtil = function (v) {
var ty = 'object';
switch (Object.prototype.toString.call(v)) {
case '[object String]':
ty = 'String';
break;
case '[object Array]':
ty = 'Array';
break;
case '[object Boolean]':
ty = 'Boolean';
break;
}
return ty;
}
+125
View File
@@ -0,0 +1,125 @@
{
"_args": [
[
{
"raw": "gm",
"scope": null,
"escapedName": "gm",
"name": "gm",
"rawSpec": "",
"spec": "latest",
"type": "tag"
},
"/Users/gerrit/Documents/node.js-Projects/tinkerforge"
]
],
"_from": "gm@latest",
"_id": "gm@1.23.1",
"_inCache": true,
"_location": "/gm",
"_nodeVersion": "6.12.2",
"_npmOperationalInternal": {
"host": "s3://npm-registry-packages",
"tmp": "tmp/gm-1.23.1.tgz_1514436565904_0.7165352762676775"
},
"_npmUser": {
"name": "aaron",
"email": "aaron.heckmann+github@gmail.com"
},
"_npmVersion": "3.10.10",
"_phantomChildren": {},
"_requested": {
"raw": "gm",
"scope": null,
"escapedName": "gm",
"name": "gm",
"rawSpec": "",
"spec": "latest",
"type": "tag"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/gm/-/gm-1.23.1.tgz",
"_shasum": "2edeeb958084d0f8ea7988e5d995b1c7dfc14777",
"_shrinkwrap": null,
"_spec": "gm",
"_where": "/Users/gerrit/Documents/node.js-Projects/tinkerforge",
"author": {
"name": "Aaron Heckmann",
"email": "aaron.heckmann+github@gmail.com"
},
"bugs": {
"url": "http://github.com/aheckmann/gm/issues"
},
"dependencies": {
"array-parallel": "~0.1.3",
"array-series": "~0.1.5",
"cross-spawn": "^4.0.0",
"debug": "^3.1.0"
},
"description": "GraphicsMagick and ImageMagick for node.js",
"devDependencies": {
"async": "~0.9.0",
"nsp": "^3"
},
"directories": {},
"dist": {
"shasum": "2edeeb958084d0f8ea7988e5d995b1c7dfc14777",
"tarball": "https://registry.npmjs.org/gm/-/gm-1.23.1.tgz"
},
"engines": {
"node": ">= 0.10.0"
},
"gitHead": "0286f47b1d5c1479c136616a73bced0ee0d2835c",
"homepage": "https://github.com/aheckmann/gm#readme",
"keywords": [
"graphics",
"magick",
"image",
"graphicsmagick",
"imagemagick",
"gm",
"convert",
"identify",
"compare"
],
"license": "MIT",
"licenses": [
{
"type": "MIT",
"url": "http://www.opensource.org/licenses/mit-license.php"
}
],
"main": "./index",
"maintainers": [
{
"name": "jongleberry",
"email": "jonathanrichardong@gmail.com"
},
{
"name": "rwky",
"email": "admin@rwky.net"
},
{
"name": "fragphace",
"email": "fragphace@gmail.com"
},
{
"name": "aaron",
"email": "aaron.heckmann+github@gmail.com"
}
],
"name": "gm",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/aheckmann/gm.git"
},
"scripts": {
"test": "make test;"
},
"version": "1.23.1"
}