SVG ICONs - Automated compiling using GRUNT and Optimize sizing

As part of building a modified version of the Matrix template I wanted to use different style of icons. There are numerous sites and resources so finding appropriate ones has not been too difficult. The challenge has been getting them all into a single SVG file and to display correctly. Patrick has provided excellent instructions as part of the matrix install.

However the solution doesn’t work quite so well with other icons and SVG files from the wild. Abit of digging around found some simple setup and automation tools which also then helped me solve the issue of icons not always displaying well. (Part Two).

Let’s get start with Part One and Grunt.
Grunt is a Node based javascript task runner into which you can install various plugins to perform certain tasks and runs off

https://gruntjs.com/

The great news is if you are using openHabian you already have node installed (or available to install). There is an excellent getting started guide which explains all the details as you want to.

Install Grunt
sudo npm install -g grunt-cli

Working Directory.
Now that Grunt is installed we need to setup some file in our working project directory to tell it what to do. For this tutorial let’s assume you are working on a new html theme called ‘jungle’ . (odd same theme as me). (This is not a tutorial on project directory structure)

So using the openhab directory structure

html/jungle

At the root of our jungle folder we need to create a config.json file.

nano config.json

Copy in the following code.

{
  "name": "jungle habpanel dashboard",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "^1.0.4",
    "grunt-contrib-nodeunit": "^2.0.0",
    "grunt-svg-sprite": "^1.5.0",
    "minimatch": "^3.0.4"
  },
}

(note the versions differ from getting started which caused me no end of problems with install grunt-svg-sprite)

Install the plugins.

sudo npm install grunt --save-dev
sudo npm install grunt-svg-sprite --save-dev

Hopefully node should install all the modules with no errors - Please let me know if there are any as I had to muck around abit before getting everything working and I may have missed something.

Create Gruntfile.js
The gruntfile contains the task instructions/configuration of what we want grunt to do. In this case clean and compress our SVG files. This is done by the grunt-svg-sprite plugin.

There is however a very handy gui configurator I found (after reading down in the underlying svg-sprite documentation).

http://jkphl.github.io/svg-sprite/#json

Let get a basic gruntfile setup. - Full file is in the code fence below.

Main Output Directory = out
Logging = Your call
Toggle SVG shape properties open
Toggle Dimensions open
Max. shape width = 50
Max. shape height = 50
Toggle Spacing
Padding on all edges = 10
Toggle Output modes
defs sprite = configure
Render HTML example = enable (optional but nice to see check things)
(see notes at end)

Now at the bottom you can select the ‘Gruntfile’ panel and there is your almost completed file.

'use strict';

var baseDir  = 'svg/base/dir',   // <-- Set to your SVG base directory
svgGlob      = '**/*.svg',       // <-- Glob to match your SVG files
outDir       = 'output/dir',     // <-- Main output directory
config       = {
    "dest": "out",
    "shape": {
        "dimension": {
            "maxWidth": 50,
            "maxHeight": 50
        },
        "spacing": {
            "padding": 10
        }
    },
    "mode": {
        "defs": {
            "example": true
        }
    }
};

module.exports = function(grunt) {

    // Project configuration
    grunt.initConfig({

        // svg-sprite configuration
        svg_sprite        : {
            dist          : {
                expand    : true,
                cwd       : baseDir,
                src       : [svgGlob],
                dest      : outDir,
                options   : config
            }
        }
    });

    // These plugins provide necessary tasks
    grunt.loadNpmTasks('grunt-svg-sprite');

    // By default, compile the sprite(s)
    grunt.registerTask('default', ['svg_sprite']);
};

Copy over to your favourite text editor and make the following updates.

var baseDir  = 'original_svgs',   // <-- Set to your SVG base directory
svgGlob      = '**/*.svg',       // <-- Glob to match your SVG files
outDir       = 'out',     // <-- Main output directory

Ready to go!
Load all your individual svgs into the ‘original_svgs’ folder
From a command line at the root of the project folder ‘/html/jungle’ run grunt.

grunt

If all goes well then you will have a new directory structure under /out which looks like this.

With the compiled file ‘sprite.def.svg’ . You can now copy this back to your root html for direct calls in your html code/habpanel. (there is probably a grunt plugin that does this as well!).

Happy grunting.

Note: defs vs symbols.
When I was working this all out I found that the compiled symbols file icons did not appear when referenced by ID, so I tried defs and had much better success with symbols appearing in my panel. (helpful hint for Chrome user press control key while clicking refresh forces a cache reload).

Since my workflow of compiling icons is setup I was able to focus on some icons did not display well, or inconsistent sized. Part 2 will cover this in Inkscape. This may also resolve the defs vs symbols issue.

1 Like

Part Two
Have found that often compiled icons do not show correctly or don’t appear to be the same size as other icons. Again a lot of research for what is actually a very quick fix using Inkscape.

Let use my recycle icon as an example as it now showing up at all. Open it up with InkScape and I see this.

2019-04-08_13-52-13

A small icon surrounded by a big page.

File -> Document Properties
Expand the ‘Resize page to content toggle’
Click on ‘Resize page to drawing or selection’
Close and save the icon

We should now see something like this
2019-04-08_15-51-20

Recompile your icon file and if all goes well the icon should now been send and rendered at a consistent size. It has it is all tied up with the viewport and viewbox which someone may be able to provide a good explanation of as abit over my head. This technique however does seem to work thou without having to understand to deeply what they are.