Search code examples
javascriptsvggulpimagemin

gulp imagemin breaking SVG mask


I have an SVG file like this:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient gradientUnits="objectBoundingBox" id="gradient" x2="0" y2="1">
      <stop stop-offset="0" stop-color="transparent" />
      <stop stop-color="rgba(255, 255, 255, 0.25)" offset="1"/>
    </linearGradient>
    <mask id="mask" maskUnits="objectBoundingBox" maskContentUnits="objectBoundingBox" x="0" y="0" width="100%" height="100%">
      <rect width="1" height="1" fill="url(#gradient)"/>
    </mask>
  </defs>
</svg>

I'm using gulp-imagemin to compress all my images including SVGs. The specific task looks like this:

gulp.task('images', function() {
  return gulp.src('/src/img/**/*.*')
    .pipe(imagemin().on('error', gutil.log))
    .pipe(gulp.dest('/dist/img'));
});

Works great for most of my images. However for the SVG file above, the output is:

<svg xmlns="http://www.w3.org/2000/svg"><defs><linearGradient gradientUnits="objectBoundingBox" id="a" x2="0" y2="1"><stop stop-color="transparent"/><stop stop-color="rgba(255, 255, 255, 0.25)" offset="1"/></linearGradient></defs></svg>

Notice the mask has been removed (which is essential) and the id changed. Is there a way to stop this happening, or even to disable SVG compression with this plugin? I've looked at the documentation and can't see a way.


Solution

  • The following worked for me:

    gulp.task('images', function() {
      return gulp.src('src/img/**/*.*')
       .pipe(imagemin([
         imagemin.svgo({
           plugins: [ 
             { removeUselessDefs: false },
             { cleanupIDs: false} 
           ]
         }),
         imagemin.gifsicle(),
         imagemin.jpegtran(),
         imagemin.optipng()
       ]).on('error', gutil.log))
       .pipe(gulp.dest('dist/img'));
    });
    

    This was the resulting SVG that it produced:

    <svg xmlns="http://www.w3.org/2000/svg"><defs><linearGradient gradientUnits="objectBoundingBox" id="gradient" x2="0" y2="1"><stop stop-color="transparent"/><stop stop-color="rgba(255, 255, 255, 0.25)" offset="1"/></linearGradient><mask id="mask" maskContentUnits="objectBoundingBox" x="0" y="0" width="100%" height="100%"><path fill="url(#gradient)" d="M0 0h1v1H0z"/></mask></defs></svg>
    

    I basically just disabled two options for the imagemin.svgo() plugin:

    • Disabling removeUselessDefs preserved the <mask>
    • Disabling cleanupIDs preserved the id attributes

    If for some reason this doesn't work for you or there are other optimizations that are causing you problems, there's a whole list of options that you can enable and disable. Just try them until you find the ones that fit your use case.

    If everything else fails, just remove the entire imagemin.svgo() call from the array that is passed to imagemin(). That way only .gif, .jpg and .png files will be minimized.