diff --git a/README.md b/README.md index a9fd7e5..ed8ab77 100644 --- a/README.md +++ b/README.md @@ -48,15 +48,31 @@ Optionally specify `options` to resize the image: opts = {} // proportionally resize the photo to a maximum height -opts = {height} +opts = {height: 300} // proportionally resize the photo to a maximum width -opts = {width} - -// resize and crop the photo to exactly height x width (aspect ratio preserved) -opts = {height, width} +opts = {width: 300} + +// resize and crop the photo to exactly height x width +// the image will not be distorted +opts = {height: 100, width: 100} + +// overlay a watermark on top of the image +opts = { + watermark: { + // PNG file with transparency, relative to the working directory + file: 'path/watermark.png', + // NorthWest | North | NorthEast | West | Center | East | SouthWest | South | SouthEast + gravity: 'SouthEast', + // whether the watermark should be repeated across the whole image + tile: false + } +} ``` +Note: watermarks are not compatible with cropped images. +The `watermark` option will simply be ignored if both width and height are specified. + ### .still ```js @@ -64,21 +80,7 @@ opts = {height, width} ``` Extract a single frame from the video in `source`, and writes the image to `target`. -Optionally specify `options` to resize the image: - -```js -// don't resize -opts = {} - -// proportionally resize the still to a maximum height -opts = {height} - -// proportionally resize the still to a maximum width -opts = {width} - -// resize and crop the still to exactly height x width (aspect ratio preserved) -opts = {height, width} -``` +This method supports all the same options as `.image()`. ### .video diff --git a/lib/index.js b/lib/index.js index 5a5638b..aee0d47 100644 --- a/lib/index.js +++ b/lib/index.js @@ -13,6 +13,19 @@ exports.image = function (source, target, options, callback) { // read baked-in orientation info, and output a rotated image with orientation=0 const image = gm(source) image.autoOrient() + // optional watermark + const cropping = options.width && options.height + if (options.watermark && !cropping) { + image.composite(options.watermark.file) + if (options.watermark.tile) { + image.tile(options.watermark.file) + } + if (options.watermark.gravity) { + image.gravity(options.watermark.gravity) + } else { + image.gravity('SouthEast') + } + } // resize if necessary if (options.height && options.width) { // crop to the exact height and weight diff --git a/test-data/expected/images/bike-wm-crop.jpg b/test-data/expected/images/bike-wm-crop.jpg new file mode 100644 index 0000000..8b1f644 Binary files /dev/null and b/test-data/expected/images/bike-wm-crop.jpg differ diff --git a/test-data/expected/images/bike-wm-default.jpg b/test-data/expected/images/bike-wm-default.jpg new file mode 100644 index 0000000..f627cee Binary files /dev/null and b/test-data/expected/images/bike-wm-default.jpg differ diff --git a/test-data/expected/images/bike-wm-gravity.jpg b/test-data/expected/images/bike-wm-gravity.jpg new file mode 100644 index 0000000..f6bac1f Binary files /dev/null and b/test-data/expected/images/bike-wm-gravity.jpg differ diff --git a/test-data/expected/images/bike-wm-resize.jpg b/test-data/expected/images/bike-wm-resize.jpg new file mode 100644 index 0000000..78da11b Binary files /dev/null and b/test-data/expected/images/bike-wm-resize.jpg differ diff --git a/test-data/expected/images/bike-wm-tiled.jpg b/test-data/expected/images/bike-wm-tiled.jpg new file mode 100644 index 0000000..38cd123 Binary files /dev/null and b/test-data/expected/images/bike-wm-tiled.jpg differ diff --git a/test-data/input/images/bike.jpg b/test-data/input/images/bike.jpg new file mode 100644 index 0000000..5343f80 Binary files /dev/null and b/test-data/input/images/bike.jpg differ diff --git a/test-data/input/images/watermark.png b/test-data/input/images/watermark.png new file mode 100644 index 0000000..9b35f7b Binary files /dev/null and b/test-data/input/images/watermark.png differ diff --git a/test/image.js b/test/image.js index f906ce7..e6a43c8 100644 --- a/test/image.js +++ b/test/image.js @@ -75,6 +75,71 @@ tape('converts a TIFF to JPEG', (test) => { }) }) +tape('can add a watermark in the default location', (test) => { + diff.image(test, { + input: 'images/bike.jpg', + expect: 'images/bike-wm-default.jpg', + options: { + watermark: { + file: 'test-data/input/images/watermark.png' + } + } + }) +}) + +tape('can add a watermark in a given location', (test) => { + diff.image(test, { + input: 'images/bike.jpg', + expect: 'images/bike-wm-gravity.jpg', + options: { + watermark: { + file: 'test-data/input/images/watermark.png', + gravity: 'NorthEast' + } + } + }) +}) + +tape('can add a tiled watermark', (test) => { + diff.image(test, { + input: 'images/bike.jpg', + expect: 'images/bike-wm-tiled.jpg', + options: { + watermark: { + file: 'test-data/input/images/watermark.png', + tile: true + } + } + }) +}) + +tape('includes the watermark when resizing', (test) => { + diff.image(test, { + input: 'images/bike.jpg', + expect: 'images/bike-wm-resize.jpg', + options: { + height: 200, + watermark: { + file: 'test-data/input/images/watermark.png' + } + } + }) +}) + +tape('ignores the watermark when cropping', (test) => { + diff.image(test, { + input: 'images/bike.jpg', + expect: 'images/bike-wm-crop.jpg', + options: { + height: 100, + width: 100, + watermark: { + file: 'test-data/input/images/watermark.png' + } + } + }) +}) + const ORIENTATIONS = [1, 2, 3, 4, 5, 6, 7, 8] ORIENTATIONS.forEach((orientation) => {