2012-04-30 19 views
10

Estoy enviando una representación de cadena de un archivo SVG para el servidor y el uso de Imagick convertir esto en un archivo JPEG de la siguiente manera:cómo cambiar el tamaño de un SVG con 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(); 

Sin embargo Deseo Cambie el tamaño del SVG antes de rasterizarlo para que la imagen resultante sea más grande que las dimensiones especificadas dentro del archivo SVG.

he modificado mi código a la siguiente:

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

Este código debe trabajar a cabo la resolución y cambiar el tamaño de SVG en consecuencia. Funciona perfectamente si el lienzo SVG y sus elementos tienen anchos basados ​​en 'porcentaje', sin embargo, no parece funcionar con los elementos definidos en 'px'. Lo cual es lamentablemente un requisito.

Una cadena SVG típica que se enviará al servidor tiene el siguiente aspecto:

<?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> 

Como se puede ver los elementos que componen tiene anchos de definición de píxeles y alturas (usando porcentajes desgraciadamente, no es esta imagen SVG opción para esta aplicación)

¿Hay alguna forma de evitar esto? O cualquier otro método para convertir un SVG a png y renderizarlo en un tamaño determinado sin pérdida de calidad.

Gracias.

EDITAR: Aunque nunca logré encontrar la solución perfecta. En su lugar, terminé enviando los datos SVG como json, recorriendo su lado del servidor y escalando los píxeles a la altura deseada.

Luego, después de mucha prueba y error, me di cuenta de que imagemagick tenía problemas con los comandos estándar de transformación/rotación SVG, descartando cualquier elemento manipulado. Terminé cambiando demasiado 'inkscape' para representar el SVG resultante como una imagen rasterizada. Y todo está bien. Todavía estoy investigando una posible solución de fórmula para compensar las diferencias que crea imagemagick. Si tengo éxito, actualizaré esta pregunta nuevamente.

+0

Desde [su otra pregunta] (http://stackoverflow.com/questions/10400897/how-to-convert-an-svg-string-into-a-jpg-with-inkscape) Sospecho que usted tiene ahora cambiado a Inkscape (que puede escalar una imagen usando opciones en la línea de comando). Si ese es el caso, ¿agregarías un comentario debajo de la pregunta? ':)'. – halfer

+0

De hecho ... voy a editar mi pregunta en consecuencia. :-) – gordyr

+0

¡Me encantaría ver más detalles sobre su solución Inkscape! – supertrue

Respuesta

12

Como solución de php_imagick's bug, puede escalar la anchura de SVG = ".." y 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; 
} 

continuación, puede crear fácilmente lindo y transparente 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(); 
} 

Nota: He estado buscando una solución de la forma de reescalar SVG de su pequeño tamaño inicial. Sin embargo, parece que imagick :: setResolution está roto. Sin embargo, la biblioteca de ImageMagick está funcionando, por lo que puede usar exec ('convert ...') (puede ser deshabilitado por razones de seguridad por el proveedor de alojamiento).

Así que para crear 50x50 miniatura desde SVG más pequeña puede hacer:

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

El ancho y la altura deben estar en "px". –

+0

Otro error es que las imágenes no se escalan bien, están borrosas. La solución consiste en utilizar un ancho y una altura mayores (no afectará al tamaño relativo si utiliza svg viewBox = "0 0 width height" sin píxeles y svg width height con píxeles). –

0

Aquí hay un ejemplo de cómo tomar una imagen que ya está en una cadena (por ejemplo, desde una base de datos), cambiar su tamaño, agregar un borde e imprimirlo. Yo lo uso para mostrar logotipos revendedor

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

Gracias, chintu, desafortunadamente esto no me sirve de nada ya que deseo cambiar el tamaño del SVG antes de que sea rasterizado para garantizar la calidad total. Por supuesto, puedo simplemente cambiar el tamaño del jpeg final sin ningún problema, pero la imagen resultante perderá calidad. Cambiando el tamaño del SVG primero (ya que SVG es un formato vectorial) y "luego" rastrillando finalmente la imagen mantendría la calidad durante el proceso de conversión. Lo siento si no estaba lo suficientemente claro en mi publicación inicial. Gracias por intentarlo. :-) – gordyr

1

que estaba buscando una solución, y me encontré con this justo después de leer este post, y funciona como un encanto:

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

Créditos dirigirse al autor de ese comentario en la página de manuales de php.

1

No es necesario imagick para completar esta tarea. Por ejemplo, no va a cambiar el tamaño de su svg (w: 60px, h: 70px) => (w: 36px, h: 36px) para obtener un icono para un botón.

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

Tenga cuidado al establecer traducir (x, y), ya que puede ocurrir que el contenido SVG se puede establecer fuera de la caja y verá nada, excepto el fondo.

Mi script anterior funciona correctamente sólo si su inicial traducir está ajustado en (0,0). Se puede utilizar esta

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

para establecer traducirse en (0,0) y adaptar datos de la trayectoria a nuevos ajustes porque los datos de ruta depende de traducir y viceversa.

Yo uso esta dos scripts para generar iconos png cambiando el tamaño de mis iconos SVG dimensionar lo que necesito y su conversión a PNG sin pérdida de calidad.

espero que sea claro lo que quiero decir.

Cuestiones relacionadas