2012-04-30 5 views
10

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

$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://www.songbanc.com/assets/embed/photos/full/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://www.songbanc.com/assets/embed/humourise/elements/thumb/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. Если у меня будет какой-то успех, я снова обновлю этот вопрос.

+0

Из [вашего другого вопроса] (http://stackoverflow.com/questions/10400897/how-to-convert-an-svg-string-into-a-jpg-with-inkscape) Я подозреваю, что у вас есть переключился на Inkscape (который может масштабировать изображение с помощью опций в командной строке). Если это так, добавили ли вы комментарий под вопрос? ':)'. – halfer

+0

Действительно ... я отредактирую свой вопрос соответственно. :-) – gordyr

+0

Хотелось бы увидеть более подробную информацию о вашем решении Inkscape! – supertrue

ответ

12

В качестве обходного пути из php_imagick's bug, вы можете масштабировать ширину SVG в = «..» и высота = «..»:

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

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

Так, для создания миниатюр 50х50 от меньшего SVG вы могли бы сделать:

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

Ширина и высота должны быть в «px». –

+0

Еще одна ошибка заключается в том, что изображения не масштабируются хорошо, они размыты. Обходной путь заключается в использовании большей ширины и высоты (он не будет влиять на относительный размер, если вы используете svg viewBox = "0 0 width height" без пикселей, а ширина ширины svg с пикселями). –

0

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

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

Спасибо, chintu, к сожалению, это не помогает мне, поскольку я хочу изменить размер SVG до того, как он будет растеризован для обеспечения полного качества. Я могу, конечно, просто изменить размер конечного jpeg без каких-либо проблем, но полученный образ потеряет качество. Сначала изменение размера SVG (поскольку SVG является векторным форматом), а «затем», наконец, растрирование изображения будет сохранять качество во время увеличения. Извините, если я не был достаточно ясен в своем первоначальном посте. Спасибо за попытку. :-) – gordyr

1

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

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

1

Для выполнения этой задачи вам не нужно воображать. Например, вы не будете изменять размер 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()); 
} 

Будьте осторожны, установив перевод (х, у), так как это может случиться так, что ваше содержимое 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 без потери качества.

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

Смежные вопросы