Search code examples
xmlalgorithmsvgcommand-linematrix-multiplication

SVG "autocrop" algorithm. commandLine preferred


I have a large collection of SVG files that are accurate in size (em or inches) but variable margins of space around the outside. There is a large variety of drawing elements inside, with transforms applied. Is there a tool or code algorithm that can parse through the svg elements, add up the highest and lowest vertical/horizontal limits, and adjust the viewbox/width/height elements in the svg element with totals...effectively auto-cropping the svg to the edge of what is drawn without distorting the size of the image.

Note: these pdfs were created with pdf2svg. I tried searching for possible features there, but missed any features that might already do this.

Example input:

<svg height="100" width="100" viewbox="0 0 100 100>
  <circle cx="50" cy="50" r="30" stroke="black" stroke-width="3" fill="red" />
</svg>

Example output:

<svg height="66" width="66" viewbox="17 17 66 66>
  <circle cx="50" cy="50" r="30" stroke="black" stroke-width="3" fill="red" />
</svg>

It's a simplified example, and hopefully my headmath is accurate, but the first line svg element was only changed, to shrink the svg as much as possible while still allowing for the stroke-width=3 to be fully shown in the image.

I theoretically know how to do this, but the nature of the problem seems "super exhaustive and prone to bugs around matrix math and svg xml implementation, and necessary enough to possibly be already implemented, so creating it could be reinventing the wheel"


Solution

  • If you can run javascript, you might use getBBox() for that:

    const s = document.querySelector('svg');
    
    const r = s.getBBox({stroke: true});
    s.setAttribute('viewBox', `${r.x} ${r.y} ${r.width} ${r.height}`);
    <svg height="100" width="100" viewbox="0 0 100 100">
      <circle cx="50" cy="50" r="30" stroke="black" stroke-width="3" fill="red" />
      <rect x="20" y="10" width="30" height="30" fill="blue" transform="rotate(45) translate(20)"/>
      <rect id="BB" x="50" y="50" width="2" height="2" 
    stroke="red" fill="yellow" opacity="0.4"/>
    </svg>
    </svg>

    Notice that there is some vertical spacing. The reason is that we have given width and height properties to the svg, and the bounding rectangle does not have the same proportions. Also, the stroke: true option seems to be ignored by Chrome, as it is experimental. In that case, the stroke may be cropped.