From cf7664a854bb503dee39596392b039c5b2dce1e9 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Mon, 1 Feb 2016 18:21:03 +0000 Subject: [PATCH] Improve SVG support by allowing control of density/DPI Switch pre-built libs from Imagemagick to Graphicsmagick --- binding.gyp | 4 +-- docs/api.md | 22 +++++++------ docs/changelog.md | 8 +++++ docs/install.md | 2 +- index.js | 29 +++++++++++++++-- packaging/arm/build.sh | 50 ++++++++++++++--------------- packaging/lin/Dockerfile | 50 ++++++++++++++--------------- src/pipeline.cc | 14 +++++--- test/fixtures/Wikimedia-logo.svg | 17 ---------- test/fixtures/check.svg | 3 ++ test/fixtures/expected/svg.png | Bin 5817 -> 0 bytes test/fixtures/expected/svg1200.png | Bin 0 -> 574 bytes test/fixtures/expected/svg72.png | Bin 0 -> 938 bytes test/fixtures/index.js | 2 +- test/unit/io.js | 50 +++++++++++++++++++++++++---- 15 files changed, 158 insertions(+), 93 deletions(-) delete mode 100644 test/fixtures/Wikimedia-logo.svg create mode 100644 test/fixtures/check.svg delete mode 100644 test/fixtures/expected/svg.png create mode 100644 test/fixtures/expected/svg1200.png create mode 100644 test/fixtures/expected/svg72.png diff --git a/binding.gyp b/binding.gyp index ad578ee4..e561c761 100644 --- a/binding.gyp +++ b/binding.gyp @@ -132,8 +132,8 @@ '<(module_root_dir)/lib/libglib-2.0.so', '<(module_root_dir)/lib/libgobject-2.0.so', # Dependencies of dependencies, included for openSUSE support - '<(module_root_dir)/lib/libMagickCore-6.Q16.so', - '<(module_root_dir)/lib/libMagickWand-6.Q16.so', + '<(module_root_dir)/lib/libGraphicsMagick.so', + '<(module_root_dir)/lib/libGraphicsMagickWand.so', '<(module_root_dir)/lib/libexif.so', '<(module_root_dir)/lib/libgio-2.0.so', '<(module_root_dir)/lib/libgmodule-2.0.so', diff --git a/docs/api.md b/docs/api.md index e745c6c6..97b29b30 100644 --- a/docs/api.md +++ b/docs/api.md @@ -6,23 +6,27 @@ var sharp = require('sharp'); ### Input -#### sharp([input]) +#### sharp([input], [options]) -Constructor to which further methods are chained. `input`, if present, can be one of: +Constructor to which further methods are chained. -* Buffer containing JPEG, PNG, WebP, GIF* or TIFF image data, or +`input`, if present, can be one of: + +* Buffer containing JPEG, PNG, WebP, GIF, SVG or TIFF image data, or * String containing the path to an image file, with most major formats supported. -The object returned implements the +JPEG, PNG, WebP, GIF, SVG or TIFF format image data +can be streamed into the object when `input` is `null` or `undefined`. + +`options`, if present, is an Object with the following optional attributes: + +* `density` an integral number representing the DPI for vector images, defaulting to 72. + +The object returned by the constructor implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class. -JPEG, PNG, WebP, GIF* or TIFF format image data -can be streamed into the object when `input` is not provided. - JPEG, PNG or WebP format image data can be streamed out from this object. -\* libvips 8.0.0+ is required for Buffer/Stream input of GIF and other `magick` formats. - ```javascript sharp('input.jpg') .resize(300, 200) diff --git a/docs/changelog.md b/docs/changelog.md index 539418a8..64e5a5a0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,11 +2,19 @@ ### v0.13 - "*mind*" +#### v0.13.0 - TBD + +* Improve vector image support by allowing control of density/DPI. + Switch pre-built libs from Imagemagick to Graphicsmagick. + [#110](https://github.com/lovell/sharp/issues/110) + [@bradisbell](https://github.com/bradisbell) + * Switch from libvips' C to C++ bindings, requires upgrade to v8.2.2. [#299](https://github.com/lovell/sharp/issues/299) * Control number of open files in libvips' cache; breaks existing `cache` behaviour. [#315](https://github.com/lovell/sharp/issues/315) + [@impomezia](https://github.com/impomezia) * Ensure 16-bit input images can be embedded onto a transparent background. [#340](https://github.com/lovell/sharp/issues/340) diff --git a/docs/install.md b/docs/install.md index b92ea1d4..ce723df0 100644 --- a/docs/install.md +++ b/docs/install.md @@ -15,7 +15,7 @@ npm install sharp [![Linux Build Status](https://circleci.com/gh/lovell/sharp.svg?style=svg&circle-token=6cb6d1d287a51af83722b19ed8885377fbc85e5c)](https://circleci.com/gh/lovell/sharp) libvips and its dependencies are fetched and stored within `node_modules/sharp` during `npm install`. -This involves an automated HTTPS download of approximately 7MB. +This involves an automated HTTPS download of approximately 6MB. Most recent Linux-based operating systems running on x64 and ARMv6+ CPUs should "just work", e.g.: diff --git a/index.js b/index.js index 9ee6e5d0..5e11cb2c 100644 --- a/index.js +++ b/index.js @@ -35,9 +35,9 @@ var maximum = { }; // Constructor-factory -var Sharp = function(input) { +var Sharp = function(input, options) { if (!(this instanceof Sharp)) { - return new Sharp(input); + return new Sharp(input, options); } stream.Duplex.call(this); this.options = { @@ -46,6 +46,7 @@ var Sharp = function(input) { streamIn: false, sequentialRead: false, limitInputPixels: maximum.pixels, + density: '72', // ICC profiles iccProfilePath: path.join(__dirname, 'icc') + path.sep, // resize options @@ -107,12 +108,13 @@ var Sharp = function(input) { } else if (typeof input === 'object' && input instanceof Buffer) { // input=buffer this.options.bufferIn = input; - } else if (typeof input === 'undefined') { + } else if (typeof input === 'undefined' || input === null) { // input=stream this.options.streamIn = true; } else { throw new Error('Unsupported input ' + typeof input); } + this._inputOptions(options); return this; }; module.exports = Sharp; @@ -133,6 +135,27 @@ module.exports.format = sharp.format(); */ module.exports.versions = versions; +/* + Set input-related options + density: DPI at which to load vector images via libmagick +*/ +Sharp.prototype._inputOptions = function(options) { + if (typeof options === 'object') { + if (typeof options.density !== 'undefined') { + if ( + typeof options.density === 'number' && !Number.isNaN(options.density) && + options.density % 1 === 0 && options.density > 0 && options.density <= 2400 + ) { + this.options.density = options.density.toString(); + } else { + throw new Error('Invalid density (1 to 2400)' + options.density); + } + } + } else if (typeof options !== 'undefined' && options !== null) { + throw new Error('Invalid input options ' + options); + } +}; + /* Handle incoming chunk on Writable Stream */ diff --git a/packaging/arm/build.sh b/packaging/arm/build.sh index bcf38a03..e677dab9 100755 --- a/packaging/arm/build.sh +++ b/packaging/arm/build.sh @@ -19,16 +19,16 @@ export CXXFLAGS="-O3" # Dependency version numbers VERSION_ZLIB=1.2.8 VERSION_FFI=3.2.1 -VERSION_GLIB=2.46.2 +VERSION_GLIB=2.47.5 VERSION_XML2=2.9.3 VERSION_GSF=1.14.34 VERSION_EXIF=0.6.21 +VERSION_LCMS2=2.7 +VERSION_GM=1.3.23 VERSION_JPEG=1.4.2 VERSION_PNG16=1.6.21 -VERSION_LCMS2=2.7 VERSION_WEBP=0.5.0 VERSION_TIFF=4.0.6 -VERSION_MAGICK=6.9.3-2 VERSION_ORC=0.4.24 VERSION_VIPS=8.2.2 @@ -44,9 +44,9 @@ cd ${DEPS}/ffi ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-builddir && make install-strip mkdir ${DEPS}/glib -curl -Ls http://ftp.gnome.org/pub/gnome/sources/glib/2.46/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1 +curl -Ls https://download.gnome.org/sources/glib/2.47/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1 cd ${DEPS}/glib -./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-pcre=internal && make install-strip mkdir ${DEPS}/xml2 curl -Ls http://xmlsoft.org/sources/libxml2-${VERSION_XML2}.tar.gz | tar xzC ${DEPS}/xml2 --strip-components=1 @@ -54,7 +54,7 @@ cd ${DEPS}/xml2 ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-python --with-zlib=${TARGET} && make install-strip mkdir ${DEPS}/gsf -curl -Ls http://ftp.gnome.org/pub/GNOME/sources/libgsf/1.14/libgsf-${VERSION_GSF}.tar.xz | tar xJC ${DEPS}/gsf --strip-components=1 +curl -Ls https://download.gnome.org/sources/libgsf/1.14/libgsf-${VERSION_GSF}.tar.xz | tar xJC ${DEPS}/gsf --strip-components=1 cd ${DEPS}/gsf ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip @@ -63,6 +63,16 @@ curl -Ls http://heanet.dl.sourceforge.net/project/libexif/libexif/${VERSION_EXIF cd ${DEPS}/exif ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip +mkdir ${DEPS}/lcms2 +curl -Ls http://heanet.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1 +cd ${DEPS}/lcms2 +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip + +mkdir ${DEPS}/gm +curl -Ls http://heanet.dl.sourceforge.net/project/graphicsmagick/graphicsmagick/${VERSION_GM}/GraphicsMagick-${VERSION_GM}.tar.xz | tar xJC ${DEPS}/gm --strip-components=1 +cd ${DEPS}/gm +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-magick-plus-plus && make install-strip + mkdir ${DEPS}/jpeg curl -Ls http://heanet.dl.sourceforge.net/project/libjpeg-turbo/${VERSION_JPEG}/libjpeg-turbo-${VERSION_JPEG}.tar.gz | tar xzC ${DEPS}/jpeg --strip-components=1 cd ${DEPS}/jpeg @@ -73,27 +83,17 @@ curl -Ls http://heanet.dl.sourceforge.net/project/libpng/libpng16/${VERSION_PNG1 cd ${DEPS}/png16 ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip -mkdir ${DEPS}/lcms2 -curl -Ls http://heanet.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1 -cd ${DEPS}/lcms2 -./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip - mkdir ${DEPS}/webp curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1 cd ${DEPS}/webp ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip mkdir ${DEPS}/tiff -curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz /deps/tiff.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1 +curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1 cd ${DEPS}/tiff ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip rm ${TARGET}/lib/libtiffxx* -mkdir ${DEPS}/magick -curl -Ls http://www.imagemagick.org/download/releases/ImageMagick-${VERSION_MAGICK}.tar.xz | tar xJC ${DEPS}/magick --strip-components=1 -cd ${DEPS}/magick -./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-magick-plus-plus && make install-strip - mkdir ${DEPS}/orc curl -Ls http://gstreamer.freedesktop.org/data/src/orc/orc-${VERSION_ORC}.tar.xz | tar xJC ${DEPS}/orc --strip-components=1 cd ${DEPS}/orc @@ -103,7 +103,7 @@ mkdir ${DEPS}/vips curl -Ls http://www.vips.ecs.soton.ac.uk/supported/8.2/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1 cd ${DEPS}/vips ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \ - --disable-debug --disable-introspection --without-python --without-fftw \ + --disable-debug --disable-introspection --without-python --without-fftw --with-magickpackage=GraphicsMagick \ --with-zip-includes=${TARGET}/include --with-zip-libraries=${TARGET}/lib \ --with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \ && make install-strip @@ -117,20 +117,20 @@ rm -rf pkgconfig .libs *.la libvipsCC* # Create JSON file of version numbers cd ${TARGET} echo "{\n\ - \"zlib\": \"${VERSION_ZLIB}\",\n\ + \"exif\": \"${VERSION_EXIF}\",\n\ \"ffi\": \"${VERSION_FFI}\",\n\ \"glib\": \"${VERSION_GLIB}\",\n\ - \"xml\": \"${VERSION_XML2}\",\n\ \"gsf\": \"${VERSION_GSF}\",\n\ - \"exif\": \"${VERSION_EXIF}\",\n\ \"jpeg\": \"${VERSION_JPEG}\",\n\ - \"png\": \"${VERSION_PNG16}\",\n\ \"lcms\": \"${VERSION_LCMS2}\",\n\ - \"webp\": \"${VERSION_WEBP}\",\n\ - \"tiff\": \"${VERSION_TIFF}\",\n\ - \"magick\": \"${VERSION_MAGICK}\",\n\ + \"gm\": \"${VERSION_GM}\",\n\ \"orc\": \"${VERSION_ORC}\",\n\ + \"png\": \"${VERSION_PNG16}\",\n\ + \"tiff\": \"${VERSION_TIFF}\",\n\ \"vips\": \"${VERSION_VIPS}\"\n\ + \"webp\": \"${VERSION_WEBP}\",\n\ + \"xml\": \"${VERSION_XML2}\",\n\ + \"zlib\": \"${VERSION_ZLIB}\",\n\ }" >lib/versions.json # Create .tar.gz diff --git a/packaging/lin/Dockerfile b/packaging/lin/Dockerfile index e4dae5a0..7c19d927 100644 --- a/packaging/lin/Dockerfile +++ b/packaging/lin/Dockerfile @@ -20,16 +20,16 @@ ENV PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:${TARGET}/lib/pkgconfig \ # Dependency version numbers ENV VERSION_ZLIB=1.2.8 \ VERSION_FFI=3.2.1 \ - VERSION_GLIB=2.46.2 \ + VERSION_GLIB=2.47.5 \ VERSION_XML2=2.9.3 \ VERSION_GSF=1.14.34 \ VERSION_EXIF=0.6.21 \ + VERSION_LCMS2=2.7 \ + VERSION_GM=1.3.23 \ VERSION_JPEG=1.4.2 \ VERSION_PNG16=1.6.21 \ - VERSION_LCMS2=2.7 \ VERSION_WEBP=0.5.0 \ VERSION_TIFF=4.0.6 \ - VERSION_MAGICK=6.9.3-2 \ VERSION_ORC=0.4.24 \ VERSION_VIPS=8.2.2 @@ -45,9 +45,9 @@ WORKDIR ${DEPS}/ffi RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-builddir && make install-strip RUN mkdir ${DEPS}/glib -RUN curl -Ls http://ftp.gnome.org/pub/gnome/sources/glib/2.46/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1 +RUN curl -Ls https://download.gnome.org/sources/glib/2.47/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1 WORKDIR ${DEPS}/glib -RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip +RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-pcre=internal && make install-strip RUN mkdir ${DEPS}/xml2 RUN curl -Ls http://xmlsoft.org/sources/libxml2-${VERSION_XML2}.tar.gz | tar xzC ${DEPS}/xml2 --strip-components=1 @@ -55,7 +55,7 @@ WORKDIR ${DEPS}/xml2 RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-python --with-zlib=${TARGET} && make install-strip RUN mkdir ${DEPS}/gsf -RUN curl -Ls http://ftp.gnome.org/pub/GNOME/sources/libgsf/1.14/libgsf-${VERSION_GSF}.tar.xz | tar xJC ${DEPS}/gsf --strip-components=1 +RUN curl -Ls https://download.gnome.org/sources/libgsf/1.14/libgsf-${VERSION_GSF}.tar.xz | tar xJC ${DEPS}/gsf --strip-components=1 WORKDIR ${DEPS}/gsf RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip @@ -64,6 +64,16 @@ RUN curl -Ls http://heanet.dl.sourceforge.net/project/libexif/libexif/${VERSION_ WORKDIR ${DEPS}/exif RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip +RUN mkdir ${DEPS}/lcms2 +RUN curl -Ls http://heanet.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1 +WORKDIR ${DEPS}/lcms2 +RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip + +RUN mkdir ${DEPS}/gm +RUN curl -Ls http://heanet.dl.sourceforge.net/project/graphicsmagick/graphicsmagick/${VERSION_GM}/GraphicsMagick-${VERSION_GM}.tar.xz | tar xJC ${DEPS}/gm --strip-components=1 +WORKDIR ${DEPS}/gm +RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-magick-plus-plus && make install-strip + RUN mkdir ${DEPS}/jpeg RUN curl -Ls http://heanet.dl.sourceforge.net/project/libjpeg-turbo/${VERSION_JPEG}/libjpeg-turbo-${VERSION_JPEG}.tar.gz | tar xzC ${DEPS}/jpeg --strip-components=1 WORKDIR ${DEPS}/jpeg @@ -74,27 +84,17 @@ RUN curl -Ls http://heanet.dl.sourceforge.net/project/libpng/libpng16/${VERSION_ WORKDIR ${DEPS}/png16 RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip -RUN mkdir ${DEPS}/lcms2 -RUN curl -Ls http://heanet.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1 -WORKDIR ${DEPS}/lcms2 -RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip - RUN mkdir ${DEPS}/webp RUN curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1 WORKDIR ${DEPS}/webp RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip RUN mkdir ${DEPS}/tiff -RUN curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz /deps/tiff.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1 +RUN curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1 WORKDIR ${DEPS}/tiff RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip RUN rm ${TARGET}/lib/libtiffxx* -RUN mkdir ${DEPS}/magick -RUN curl -Ls http://www.imagemagick.org/download/releases/ImageMagick-${VERSION_MAGICK}.tar.xz | tar xJC ${DEPS}/magick --strip-components=1 -WORKDIR ${DEPS}/magick -RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-magick-plus-plus && make install-strip - RUN mkdir ${DEPS}/orc RUN curl -Ls http://gstreamer.freedesktop.org/data/src/orc/orc-${VERSION_ORC}.tar.xz | tar xJC ${DEPS}/orc --strip-components=1 WORKDIR ${DEPS}/orc @@ -104,7 +104,7 @@ RUN mkdir ${DEPS}/vips RUN curl -Ls http://www.vips.ecs.soton.ac.uk/supported/8.2/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1 WORKDIR ${DEPS}/vips RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \ - --disable-debug --disable-introspection --without-python --without-fftw \ + --disable-debug --disable-introspection --without-python --without-fftw --with-magickpackage=GraphicsMagick \ --with-zip-includes=${TARGET}/include --with-zip-libraries=${TARGET}/lib \ --with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \ && make install-strip @@ -118,20 +118,20 @@ RUN rm -rf pkgconfig .libs *.la libvipsCC* # Create JSON file of version numbers WORKDIR ${TARGET} RUN echo "{\n\ - \"zlib\": \"${VERSION_ZLIB}\",\n\ + \"exif\": \"${VERSION_EXIF}\",\n\ \"ffi\": \"${VERSION_FFI}\",\n\ \"glib\": \"${VERSION_GLIB}\",\n\ - \"xml\": \"${VERSION_XML2}\",\n\ \"gsf\": \"${VERSION_GSF}\",\n\ - \"exif\": \"${VERSION_EXIF}\",\n\ \"jpeg\": \"${VERSION_JPEG}\",\n\ - \"png\": \"${VERSION_PNG16}\",\n\ \"lcms\": \"${VERSION_LCMS2}\",\n\ - \"webp\": \"${VERSION_WEBP}\",\n\ - \"tiff\": \"${VERSION_TIFF}\",\n\ - \"magick\": \"${VERSION_MAGICK}\",\n\ + \"gm\": \"${VERSION_GM}\",\n\ \"orc\": \"${VERSION_ORC}\",\n\ + \"png\": \"${VERSION_PNG16}\",\n\ + \"tiff\": \"${VERSION_TIFF}\",\n\ \"vips\": \"${VERSION_VIPS}\"\n\ + \"webp\": \"${VERSION_WEBP}\",\n\ + \"xml\": \"${VERSION_XML2}\",\n\ + \"zlib\": \"${VERSION_ZLIB}\",\n\ }" >lib/versions.json # Create .tar.gz diff --git a/src/pipeline.cc b/src/pipeline.cc index e37fad7b..c032c2a8 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -77,6 +77,7 @@ struct PipelineBaton { size_t bufferInLength; std::string iccProfilePath; int limitInputPixels; + std::string density; std::string output; std::string outputFormat; void *bufferOut; @@ -129,6 +130,7 @@ struct PipelineBaton { PipelineBaton(): bufferInLength(0), limitInputPixels(0), + density(""), outputFormat(""), bufferOutLength(0), topOffsetPre(-1), @@ -201,8 +203,9 @@ class PipelineWorker : public AsyncWorker { if (inputImageType != ImageType::UNKNOWN) { try { image = VImage::new_from_buffer( - baton->bufferIn, baton->bufferInLength, nullptr, - VImage::option()->set("access", baton->accessMethod) + baton->bufferIn, baton->bufferInLength, nullptr, VImage::option() + ->set("access", baton->accessMethod) + ->set("density", baton->density.data()) ); } catch (...) { (baton->err).append("Input buffer has corrupt header"); @@ -217,8 +220,9 @@ class PipelineWorker : public AsyncWorker { if (inputImageType != ImageType::UNKNOWN) { try { image = VImage::new_from_file( - baton->fileIn.data(), - VImage::option()->set("access", baton->accessMethod) + baton->fileIn.data(), VImage::option() + ->set("access", baton->accessMethod) + ->set("density", baton->density.data()) ); } catch (...) { (baton->err).append("Input file has corrupt header"); @@ -997,6 +1001,8 @@ NAN_METHOD(pipeline) { baton->iccProfilePath = attrAsStr(options, "iccProfilePath"); // Limit input images to a given number of pixels, where pixels = width * height baton->limitInputPixels = attrAs(options, "limitInputPixels"); + // Density/DPI at which to load vector images via libmagick + baton->density = attrAsStr(options, "density"); // Extract image options baton->topOffsetPre = attrAs(options, "topOffsetPre"); baton->leftOffsetPre = attrAs(options, "leftOffsetPre"); diff --git a/test/fixtures/Wikimedia-logo.svg b/test/fixtures/Wikimedia-logo.svg deleted file mode 100644 index e7ef65e8..00000000 --- a/test/fixtures/Wikimedia-logo.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - diff --git a/test/fixtures/check.svg b/test/fixtures/check.svg new file mode 100644 index 00000000..fd681ebb --- /dev/null +++ b/test/fixtures/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/test/fixtures/expected/svg.png b/test/fixtures/expected/svg.png deleted file mode 100644 index dccdde685c3e22ff3197a3171a5fea76de61cc69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5817 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 B2Cx7C literal 0 HcmV?d00001 diff --git a/test/fixtures/expected/svg72.png b/test/fixtures/expected/svg72.png new file mode 100644 index 0000000000000000000000000000000000000000..6d37045a98a960abea8082b7ee6b328a6dd45504 GIT binary patch literal 938 zcmV;b16BNqP)I{+Ry0e)@_pAHLx8`_FhfJmFn4`l@ufFYHezx}-J zs(k=D7RIC#qC^ZlV4wjn-3z?)5R(Mx5}=xbl|h;SBS(xpCQusCbB}=sM2?tb0x3aM zpr%k$SfN^#JHHzMI#R;K$wp5-PSXj&2bdWqnQ#aPc<15Z3LqX~{2REN- zFo;Tl%)MCK9C=Lf>tfL6&j!d|w+8@S9w>6e;0FX3pr#-t9Q*;nhhorR5R(mLwl&FA zERqmdb|S_Ku7+Fzmxw2y8b0@*sG##*0n7%~6siibCF&e^P9j@nV~ENE_nN^Slihmh z2%Khc2K3Iuxf#$b{We>v`*9ulRZGkjv$@MANC?3Nc;^d5a3QUMRV;ZxHC^JaZ07Zk zl?$N3T8rmq@-CnX*rmnVNjfr5oD5u7?xzB1(nL<0dXJUC#^q+t+Ph|w+WD~4hb{6RQ3DWf7Jdl||p)G6`cMZOY_=f7l z1WuP_#9#;-ioFb18_b|3ZnUxI$Tv&OYqdLzRUuwrx0>nqQU}~`)&=lhTOASS^*azuK`rk z1Y#*lX9=q5z4Me!Y|VCMxBmZ+0Lxv^gTA%u0Q@_!8(vKJs@uTb1G@&g;z@C-y3Ga8 zZC?XZpW{~h?tt`{580{z)UVy{FZGM%x&4>U`%BdVu2=Y52HZRD?jF46v9tH~ajE{w zEUquK*H^a;^v@%HWA*2=_&?PH@ZNd=-dhjAd+PysZ#@9-tv3(&8_c%{8JYMT{Qv*} M07*qoM6N<$f>^w^5C8xG literal 0 HcmV?d00001 diff --git a/test/fixtures/index.js b/test/fixtures/index.js index 600aa225..28bb96ad 100644 --- a/test/fixtures/index.js +++ b/test/fixtures/index.js @@ -84,7 +84,7 @@ module.exports = { inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif - inputSvg: getPath('Wikimedia-logo.svg'), // http://commons.wikimedia.org/wiki/File:Wikimedia-logo.svg + inputSvg: getPath('check.svg'), // http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/check.svg inputPsd: getPath('free-gearhead-pack.psd'), // https://dribbble.com/shots/1624241-Free-Gearhead-Vector-Pack inputSvs: getPath('CMU-1-Small-Region.svs'), // http://openslide.cs.cmu.edu/download/openslide-testdata/Aperio/CMU-1-Small-Region.svs diff --git a/test/unit/io.js b/test/unit/io.js index 79fdf46a..20dfd241 100644 --- a/test/unit/io.js +++ b/test/unit/io.js @@ -634,20 +634,37 @@ describe('Input/output', function() { }); if (sharp.format.magick.input.file) { - it('Convert SVG, if supported, to PNG', function(done) { + it('Convert SVG to PNG at default 72DPI', function(done) { sharp(fixtures.inputSvg) - .resize(100, 100) + .resize(1024) + .extract({left: 290, top: 760, width: 40, height: 40}) .toFormat('png') .toBuffer(function(err, data, info) { if (err) { assert.strictEqual(0, err.message.indexOf('Input file is missing or of an unsupported image format')); done(); } else { - assert.strictEqual(true, info.size > 0); assert.strictEqual('png', info.format); - assert.strictEqual(100, info.width); - assert.strictEqual(100, info.height); - fixtures.assertSimilar(fixtures.expected('svg.png'), data, done); + assert.strictEqual(40, info.width); + assert.strictEqual(40, info.height); + fixtures.assertSimilar(fixtures.expected('svg72.png'), data, done); + } + }); + }); + it('Convert SVG to PNG at 300DPI', function(done) { + sharp(fixtures.inputSvg, { density: 1200 }) + .resize(1024) + .extract({left: 290, top: 760, width: 40, height: 40}) + .toFormat('png') + .toBuffer(function(err, data, info) { + if (err) { + assert.strictEqual(0, err.message.indexOf('Input file is missing or of an unsupported image format')); + done(); + } else { + assert.strictEqual('png', info.format); + assert.strictEqual(40, info.width); + assert.strictEqual(40, info.height); + fixtures.assertSimilar(fixtures.expected('svg1200.png'), data, done); } }); }); @@ -824,6 +841,27 @@ describe('Input/output', function() { }); + describe('Input options', function() { + it('Non-Object options fails', function() { + assert.throws(function() { + sharp(null, 'zoinks'); + }); + }); + it('Invalid density: string', function() { + assert.throws(function() { + sharp(null, { density: 'zoinks' } ); + }); + }); + it('Invalid density: float', function() { + assert.throws(function() { + sharp(null, { density: 0.5 } ); + }); + }); + it('Ignore unknown attribute', function() { + sharp(null, { unknown: true } ); + }); + }); + it('Queue length change events', function(done) { var eventCounter = 0; var queueListener = function(queueLength) {