как изменить размер SVG с помощью Imagick / ImageMagick

Я отправляю строковое представление SVG-файла на сервер и используя Imagick, чтобы превратить это в jpeg следующим образом:

$image = stripslashes($_POST['json']); $filename = $_POST['filename']; $unique = time(); $im = new Imagick(); $im->readImageBlob($image); $im->setImageFormat("jpeg"); $im->writeImage('../photos/' . $type . '/humourised_' . $unique . $filename); $im->clear(); $im->destroy(); 

Однако я хочу изменить размер SVG до его растеризации, чтобы получившееся изображение было больше размеров, указанных в файле SVG.

Я изменил свой код на следующее:

 $image = stripslashes($_POST['json']); $filename = $_POST['filename']; $unique = time(); $im = new Imagick(); $im->readImageBlob($image); $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->readImageBlob($image); $im->setImageFormat("jpeg"); $im->writeImage('../photos/' . $type . '/humourised_' . $unique . $filename); $im->clear(); $im->destroy(); 

Этот код должен выработать разрешение и соответствующим образом изменить размер SVG. Он отлично работает, если холст SVG и его элементы имеют ширину, основанную на процентах, однако она не работает с элементами, определенными в 'px'. К сожалению, это требование.

Типичная строка SVG, которая будет отправлена ​​на сервер, выглядит так:

 <?xml version="1.0" encoding="ISO-8859-1" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/SVG/DTD/svg10.dtd"> <svg id="tempsvg" style="overflow: hidden; position: relative;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="333" version="1.1" height="444"> <image transform="matrix(1,0,0,1,0,0)" preserveAspectRatio="none" x="0" y="0" width="333" height="444" xlink:href="http://img.ruphp.com/php/133578615720079914224f9e7aad9ac871.jpg"></image> <image transform="matrix(1,0,0,1,0,0)" preserveAspectRatio="none" x="85.5" y="114" width="50" height="38" xlink:href="http://img.ruphp.com/php/thumb_lips4.png"></image> <path transform="matrix(1,0,0,1,0,0)" fill="none" stroke="#000" d="M110.5,133L140.5,133" stroke-dasharray="- " opacity="0.5"></path> <circle transform="matrix(1,0,0,1,0,0)" cx="140.5" cy="133" r="5" fill="#000" stroke="#000"></circle> <path transform="matrix(1,0,0,1,0,0)" fill="none" stroke="#000" d="M110.5,133L110.5,155.8" stroke-dasharray="- " opacity="0.5"></path> <circle transform="matrix(1,0,0,1,0,0)" cx="110.5" cy="155.8" r="5" fill="#000" stroke="#000"></circle> <circle transform="matrix(1,0,0,1,0,0)" cx="110.5" cy="133" r="5" fill="#000" stroke="#000"></circle> </svg> 

Как вы можете видеть, элементы, которые составляют этот SVG, имеют ширину и высоту определения пикселов (использование процентов, к сожалению, не является вариантом для этого приложения)

Есть ли способ обойти это? Или любой другой способ преобразования SVG в png и рендеринг его при заданном размере без потери качества.

Благодарю.

EDIT: Хотя мне никогда не удавалось найти идеальное решение. Вместо этого я закончил передачу SVG-данных как json, прокручивая его на стороне сервера и масштабируя пиксели до предполагаемой высоты.

Затем, после долгих проб и ошибок, я понял, что у imagemagick есть проблемы с стандартными командами трансформирования / поворота SVG, которые бросают любые управляемые элементы из-под удара. В итоге я переключил слишком «inkscape», чтобы визуализировать полученный SVG как растрированное изображение. И все хорошо. Я все еще вникаю в потенциальное формульное решение, чтобы компенсировать различия, которые создает imagemagick. Если у меня будет какой-то успех, я снова обновлю этот вопрос.

Related of "как изменить размер SVG с помощью Imagick / ImageMagick"

В качестве обхода ошибки php_imagick вы можете масштабировать ширину svg = ".." и 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; } 

Тогда вы можете легко создать красивый прозрачный 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(); } 

Примечание. Я искал решение о том, как масштабировать SVG с его начального малого размера. Однако кажется, что imagick :: setResolution нарушен. Однако сама библиотека ImageMagick работает, поэтому вы можете использовать exec ('convert …') (может быть отключен по соображениям безопасности хостинг-провайдером).

Поэтому для создания миниатюры 50×50 из меньшего svg вы бы сделали:

 convert -density 500 -resize 50 50 -background transparent a.svg PNG32:a.png 

Я искал решение, и я нашел это сразу после прочтения этого сообщения и работал как шарм:

 $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; 

Кредиты отправляются автору этого комментария на странице справочников php.

Вам не нужно воображать, чтобы выполнить эту задачу. Например, вы не будете изменять размер svg (w: 60px, h: 70px) => (w: 36px, h: 36px), чтобы получить значок для кнопки.

 $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()); } 

Будьте осторожны, установив translate (x, y), потому что может случиться так, что ваш svg-контент можно установить за пределами поля, и вы не увидите ничего, кроме фона.

Мой скрипт выше работает правильно, только если ваш начальный перевод имеет значение (0,0). Вы можете использовать это

 $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()); 

установить перевод на (0,0) и адаптировать данные пути к новым настройкам, поскольку данные пути зависят от перевода и наоборот.

Я использую эти два сценария для создания значков png, изменяя размеры моих значков svg до необходимого размера и преобразовывая их в png без потери качества.

Надеюсь, ясно, что я имею в виду.

Вот пример того, как взять образ, который уже находится в строке (скажем, из базы данных), и изменить его размер, добавить границу и распечатать ее. Я использую это для отображения логотипов реселлера

  // 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;