how to resize an SVG with Imagick/ImageMagick

后端 未结 4 678

I am sending a string representation of an SVG file to the server and using Imagick to turn this into a jpeg in the following manner:

$image = stripslashes($         


        
相关标签:
4条回答
  • 2020-12-29 10:36

    I was looking for a solution, and i found this just after reading this post, and work like a charm:

    $im = new Imagick();
    $im->readImage("/path/to/image.svg");
    $res = $im->getImageResolution();
    $x_ratio = $res['x'] / $im->getImageWidth();
    $y_ratio = $res['y'] / $im->getImageHeight();
    $im->removeImage();
    $im->setResolution($width_in_pixels * $x_ratio, $height_in_pixels * $y_ratio);
    $im->readImage("/path/to/image.svg");
    // Now you can do anything with the image, such as convert to a raster image and output it to the browser:
    $im->setImageFormat("png");
    header("Content-Type: image/png");
    echo $im;
    

    Credits go to the author of that comment in php manuals page.

    0 讨论(0)
  • 2020-12-29 10:37

    Here is an example on how to take an image that is already in a string (say, from a database), and resize it, add a border, and print it out. I use this for showing reseller logos

      // Decode image from base64
      $image=base64_decode($imagedata);
    
      // Create Imagick object
      $im = new Imagick();
    
      // Convert image into Imagick
      $im->readimageblob($image);
    
      // Create thumbnail max of 200x82
      $im->thumbnailImage(200,82,true);
    
      // Add a subtle border
      $color=new ImagickPixel();
      $color->setColor("rgb(220,220,220)");
      $im->borderImage($color,1,1);
    
      // Output the image
      $output = $im->getimageblob();
      $outputtype = $im->getFormat();
    
      header("Content-type: $outputtype");
      echo $output;
    
    0 讨论(0)
  • 2020-12-29 10:51

    As a workaround of php_imagick's bug, you can scale svg's width=".." and height="..":

    function svgScaleHack($svg, $minWidth, $minHeight)
    {
        $reW = '/(.*<svg[^>]* width=")([\d.]+px)(.*)/si';
        $reH = '/(.*<svg[^>]* height=")([\d.]+px)(.*)/si';
        preg_match($reW, $svg, $mw);
        preg_match($reH, $svg, $mh);
        $width = floatval($mw[2]);
        $height = floatval($mh[2]);
        if (!$width || !$height) return false;
    
        // scale to make width and height big enough
        $scale = 1;
        if ($width < $minWidth)
            $scale = $minWidth/$width;
        if ($height < $minHeight)
            $scale = max($scale, ($minHeight/$height));
    
        $width *= $scale*2;
        $height *= $scale*2;
    
        $svg = preg_replace($reW, "\${1}{$width}px\${3}", $svg);
        $svg = preg_replace($reH, "\${1}{$height}px\${3}", $svg);
    
        return $svg;
    }
    

    Then you can easily create nice transparent PNG!

    createThumbnail('a.svg', 'a.png');
    
    function createThumbnail($filename, $thname, $size=50)
    {
        $im = new Imagick();
        $svgdata = file_get_contents($filename);
        $svgdata = svgScaleHack($svgdata, $size, $size);
    
        $im->setBackgroundColor(new ImagickPixel('transparent'));
        $im->readImageBlob($svgdata);
    
        $im->setImageFormat("png32");
        $im->resizeImage($size, $size, imagick::FILTER_LANCZOS, 1);
    
        file_put_contents($thname, $im->getImageBlob());
        $im->clear();
        $im->destroy();
    }
    

    Note: I've been searching for a solution how to rescale SVG from its initial small size. However it seems that imagick::setResolution is broken. However, ImageMagick library itself is working, so you can use exec('convert...') (might be disabled for security reasons by hosting provider).

    So to create thumbnail 50x50 from smaller svg you would do:

    convert -density 500 -resize 50 50 -background transparent a.svg PNG32:a.png
    
    0 讨论(0)
  • 2020-12-29 10:58

    You don't need imagick to complete this task. For example you wont to resize your svg (w: 60px, h: 70px) => (w: 36px, h: 36px) to get an icon for a button.

    $svg = file_get_contents($your_svg_file);
    
    // I prefer to use DOM, because it's safer and easier as to use preg_match
    $svg_dom = new DOMDocument();
    
    libxml_use_internal_errors(true);
    $svg_dom->loadXML($svg);
    libxml_use_internal_errors(false);
    
    //get width and height values from your svg
    $tmp_obj = $svg_dom->getElementsByTagName('svg')->item(0);
    $svg_width = floatval($tmp_obj->getAttribute('width'));
    $svg_height = floatval($tmp_obj->getAttribute('height'));
    
    // set width and height of your svg to preferred dimensions
    $tmp_obj->setAttribute('width', 36);
    $tmp_obj->setAttribute('height', 36);
    
    // check if width and height of your svg is smaller than the width and 
    // height you set above => no down scaling is needed
    if ($svg_width < 36 && $svg_height < 36) {
        //center your svg content in new box
        $x = abs($svg_width - 36) / 2;
        $y = abs($svg_height - 36) / 2;
        $tmp_obj->getElementsByTagName('g')->item(0)->setAttribute('transform', "translate($x,$y)");
    } else {
        // scale down your svg content and center it in new box
        $scale = 1;
    
        // set padding to 0 if no gaps are desired
        $padding = 2;
    
        // get scale factor
        if ($svg_width > $svg_height) {
            $scale = (36 - $padding) / $svg_width;
        } else {
            $scale = (36 - $padding) / $svg_height;
        }
    
        $x = abs(($scale * $svg_width) - 36) / 2;
        $y = abs(($scale * $svg_height) - 36) / 2;
        $tmp_obj->getElementsByTagName('g')->item(0)->setAttribute('transform', "translate($x,$y) scale($scale,$scale)");
    
        file_put_contents('your_new_svg.svg', $svg_dom->saveXML());
    }
    

    Be careful by setting translate(x,y), because it may happen that your svg content can be set outside of the box and you will see nothing except the background.

    My script above works proper only if your initial translate is set to (0,0). You can use this

    $svg_path = $svg_dom->getElementsByTagName('path')->item(0);
    $svg_g = $svg_dom->getElementsByTagName('g')->item(0);
    $transform = $svg_g->getAttribute('transform');
    
    // get x and y of translate
    $transform = substr($transform, strlen('translate('));
    $transform = substr($transform, 0, strlen($transform)-1);
    $transform_data = explode(',', $transform);
    
    // get path data
    $d = $svg_path->getAttribute('d');
    $d_data = explode(' ', $d);
    $tmp = explode(',', $d_data[1]);
    $d_data[1] = ($tmp[0] + $transform_data[0]).','.($tmp[1]+$transform_data[1]);
    $svg_path->setAttribute('d', implode(' ', $d_data));
    $svg_g->setAttribute('transform','translate(0,0)');
    file_put_contents('your_new_svg.svg',$svg_dom->saveXML());
    

    to set translate to (0,0) and adapt path data to new settings because path data depends on translate and vice versa.

    I use this two scripts to generate png icons by resizing my svg icons to dimension I need and converting them to png without loss of quality.

    I hope it's clear what I mean.

    0 讨论(0)
提交回复
热议问题