Search code examples
androidsvgandroid-vectordrawable

Adjust origin of SVG graphic


tl;dr: Given arbitrary SVG <path> how can I adjust the path commands to set a new origin for the file?

Details

I'm using Adobe Illustrator to create vector graphics for use in Android. Illustrator has a bug where under (unknown) circumstances the viewBox and all coordinates created for your graphics do not match the coordinates you set for the canvas and objects in Illustrator. Instead of (for example) viewBox="-7 0 14 20" you might get viewBox="-53 75 14 20". Usually the the path commands will be translated to fit inside that viewBox. Sometimes even that fails.

In a normal SVG file (a) this would rarely even be noticeable, and (b) it would be easy to fix by wrapping everything in a root-level <g transform="translate(46,-75)">. In an Android VectorDrawable this requires an extra <group> that I don't want to add, and (unlike SVG) I can't add a translate to the <path> itself. And in my code, the origin matters.

How can I adjust the path commands for all the paths in an SVG file to offset them by a fixed amount, allowing me to place the origin where I'd like?


Solution

  • A solution is to add explicit transform="translate(…,…)" onto each path that you want to adjust, view the SVG in a web browser, use the code in this question [note: my own] to bake the translate into the path data, and then use the Web inspector to get the changed values. Cumbersome.


    Better: I've created a little online tool SVG Reoriginizer [no ads, no revenue] that lets you paste SVG code into the top box, click (or use alt+shift+arrow keys) to adjust the origin, and see the translated path commands shown in the bottom.

    The core code is this function, which offsets the viewBox and all absolute path commands by the specified amount, and returns the changed SVG as a string:

    function offsetOrigin(svg,dx,dy){
      svg.viewBox.baseVal.x -= dx;
      svg.viewBox.baseVal.y -= dy;
      [].forEach.call(svg.querySelectorAll('path'),function(path){
        var segs = path.pathSegList;
        for (var i=segs.numberOfItems;i--;){
          var seg=segs.getItem(i), c=seg.pathSegTypeAsLetter;
          if (/[MLHVCSQTA]/.test(c)){
            if ('x'  in seg) seg.x -=dx;
            if ('y'  in seg) seg.y -=dy;
            if ('x1' in seg) seg.x1-=dx;
            if ('x2' in seg) seg.x2-=dx;
            if ('y1' in seg) seg.y1-=dy;
            if ('y2' in seg) seg.y2-=dy;
          }
        }
      });
    
      return (new XMLSerializer).serializeToString(svg);
    }