Search code examples
phpsvgimage-resizingimagick

How to resize SVG with PHP?


I am trying to resize (increase size) a SVG inside a PHP script and save it as a new SVG. Given SVG is generated by another script. It contains image and texts.

I have tried with imagick, but the generated svg is broken, and doesn't include any of the contents.

This is how I have done:

$image = new Imagick();
$image->setResolution(2000,2000);
$image->readImage('/pathe/to/my/svg.svg');
$image->scaleImage(1000,1000);
file_put_contents('/pathe/to/my/new.svg', $image->getImageBlob());

Solution

  • Consider that "SVG" stands for "Scalable Vector Grafics".

    1. Imagick is a library for manipulating pixel-oriented grafics and as such unfit for vector grafic formats like SVG.

    2. SVGs are by their nature scalable. In most cases you will not need to rewrite them to change their size. If you display SVGs inside a webpage (via <img> or <object> tags, or as a CSS background-image, it will be displayed at the size defined for these elements, and if there is size information stored in the SVG file, that will be simply ignored. (Note that a lot of SVG files contain no display size information at all.)

    3. The only case you may need to set size information in the SVG file is if you display it as a standalone file (for example if you have a link pointing to the .svg itself).

      In this case, you can take advantage of the fact that SVG is a XML file:

      $dom = new DOMDocument('1.0', 'utf-8');
      $dom->load('/pathe/to/my/svg.svg');
      $svg = $dom->documentElement;
      
      if ( ! $svg->hasAttribute('viewBox') ) { // viewBox is needed to establish
                                               // userspace coordinates
           $pattern = '/^(\d*\.\d+|\d+)(px)?$/'; // positive number, px unit optional
      
           $interpretable =  preg_match( $pattern, $svg->getAttribute('width'), $width ) &&
                             preg_match( $pattern, $svg->getAttribute('height'), $height );
      
           if ( $interpretable ) {
              $view_box = implode(' ', [0, 0, $width[0], $height[0]]);
              $svg->setAttribute('viewBox', $view_box);
          } else { // this gets sticky
               throw new Exception("viewBox is dependent on environment");
          }
      }
      
      $svg->setAttribute('width', '1000');
      $svg->setAttribute('height', '1000');
      $dom->save('/pathe/to/my/new.svg');
      

      There are some tricky cases if the element has no viewBox attribute and the width and height have non-px units. Handling them requires a deeper understanding of SVG. It would be best to assure that the SVG files produced by the "other script" always have a viewBox. Then the conditional section can be left out.

      The term "resolution" is inapplicable for vector grafics. Width and height can be dimensionless numbers (representing pixel for the purpose of rendering into an output device, for example the screen), or any valid CSS length unit: em, ex, px, pt, pc, cm, mm, in, and percentages.