Convertir une image SVG en PNG avec PHP

111

Je travaille sur un projet Web qui implique une carte générée dynamiquement des États-Unis colorant différents états en fonction d'un ensemble de données.

Ce fichier SVG me donne une bonne carte vierge des États-Unis et il est très facile de changer la couleur de chaque état. La difficulté est que les navigateurs IE ne prennent pas en charge SVG, donc pour que j'utilise la syntaxe pratique offerte par svg, je devrai le convertir en JPG.

Idéalement, j'aimerais faire cela avec uniquement la bibliothèque GD2, mais je pourrais également utiliser ImageMagick. Je n'ai absolument aucune idée de comment procéder.

Toute solution qui me permettrait de changer dynamiquement les couleurs des États sur une carte des États-Unis sera considérée. La clé est qu'il est facile de changer les couleurs à la volée et que c'est un navigateur croisé. Solutions PHP / Apache uniquement, s'il vous plaît.

Michael Berkompas
la source
existe-t-il des classes conçues pour porter SVG vers VML? de cette façon, vous pourriez toujours avoir une solution de type 'HTML5'
Patrick
jetez un œil à ma réponse. exactement ce dont vous avez besoin

Réponses:

142

C'est drôle que vous ayez posé cette question, je viens de le faire récemment pour le site de mon travail et je pensais que je devrais écrire un tutoriel ... Voici comment le faire avec PHP / Imagick, qui utilise ImageMagick:

$usmap = '/path/to/blank/us-map.svg';
$im = new Imagick();
$svg = file_get_contents($usmap);

/*loop to color each state as needed, something like*/ 
$idColorArray = array(
     "AL" => "339966"
    ,"AK" => "0099FF"
    ...
    ,"WI" => "FF4B00"
    ,"WY" => "A3609B"
);

foreach($idColorArray as $state => $color){
//Where $color is a RRGGBB hex value
    $svg = preg_replace(
         '/id="'.$state.'" style="fill:#([0-9a-f]{6})/'
        , 'id="'.$state.'" style="fill:#'.$color
        , $svg
    );
}

$im->readImageBlob($svg);

/*png settings*/
$im->setImageFormat("png24");
$im->resizeImage(720, 445, imagick::FILTER_LANCZOS, 1);  /*Optional, if you need to resize*/

/*jpeg*/
$im->setImageFormat("jpeg");
$im->adaptiveResizeImage(720, 445); /*Optional, if you need to resize*/

$im->writeImage('/path/to/colored/us-map.png');/*(or .jpg)*/
$im->clear();
$im->destroy();

les étapes de remplacement des couleurs regex peuvent varier en fonction du chemin svg xml et de la façon dont vos valeurs d'identifiant et de couleur sont stockées. Si vous ne souhaitez pas stocker de fichier sur le serveur, vous pouvez générer l'image en base 64 comme

<?php echo '<img src="data:image/jpg;base64,' . base64_encode($im) . '"  />';?>

(avant d'utiliser clear / destroy) mais c'est-à-dire a des problèmes avec PNG comme base64 donc vous devrez probablement sortir base64 comme jpeg

vous pouvez voir un exemple ici que j'ai fait pour la carte du territoire de vente d'un ancien employeur:

Début: https://upload.wikimedia.org/wikipedia/commons/1/1a/Blank_US_Map_(states_only).svg

Terminer: entrez la description de l'image ici

Éditer

Depuis l'écriture de ce qui précède, j'ai mis au point 2 techniques améliorées:

1) au lieu d'une boucle regex pour changer l'état de remplissage, utilisez CSS pour créer des règles de style comme

<style type="text/css">
#CA,#FL,HI{
    fill:blue;
}
#Al, #NY, #NM{
    fill:#cc6699;
}
/*etc..*/
</style>

puis vous pouvez faire un seul remplacement de texte pour injecter vos règles css dans le svg avant de procéder à la création imagick jpeg / png. Si les couleurs ne changent pas, assurez-vous qu'aucun style de remplissage en ligne dans vos balises de chemin ne remplace le css.

2) Si vous n'avez pas à créer un fichier image jpeg / png (et que vous n'avez pas besoin de prendre en charge les navigateurs obsolètes), vous pouvez manipuler le svg directement avec jQuery. Vous ne pouvez pas accéder aux chemins svg lors de l'incorporation du svg à l'aide de balises img ou object, vous devrez donc inclure directement le svg xml dans votre page Web HTML comme:

<div>
<?php echo file_get_contents('/path/to/blank/us-map.svg');?>
</div>

puis changer les couleurs est aussi simple que:

<script type="text/javascript" src="/path/to/jquery.js"></script>
<script type="text/javascript">
    $('#CA').css('fill', 'blue');
    $('#NY').css('fill', '#ff0000');
</script>
WebChemist
la source
1
Merci pour le tutoriel très précis et utile sur la façon de procéder. J'utiliserai certainement votre solution comme sauvegarde, mais je suis impatient d'essayer d'obtenir la compatibilité svg sur tous les principaux navigateurs.
Michael Berkompas
1
SVG n'est pas pris en charge dans ie8 ou une version antérieure sans que l'utilisateur n'ait besoin d'installer un plugin de visualisation svg - à partir de la page SVG Wikipedia: "Tous les principaux navigateurs Web modernes prennent en charge et affichent le balisage SVG directement à l'exception très notable de Microsoft Internet Explorer (IE) [ 3] La version bêta d'Internet Explorer 9 prend en charge l'ensemble des fonctionnalités SVG de base. [4] Actuellement, la prise en charge des navigateurs fonctionnant sous Android est également limitée. "
WebChemist
1
Oui, mais svgweb semble aplanir toutes les incompatibilités en utilisant un peu de js et de flash. C'est la solution avec laquelle je suis allé.
Michael Berkompas
2
J'aime votre solution propre et rapide. Personnellement, lorsque j'interagis avec des fichiers xml, je préfère utiliser un analyseur dom pour me sentir plus en sécurité qu'avec regex. Sth comme:$dom = new DOMDocument(); $dom->loadXML( $svg ); $dom->getElementsByTagName('image')->item(0)->setAttribute('id', $state); $svg = $dom->saveXML();
Tapper
un analyseur xml serait une solution plus sûre, quoique légèrement plus lente, avec n'importe quel autre svg ... dans ce cas, l'expression régulière est sûre parce que je me suis assuré que les attributs de chaque état étaient formatés exactement comme (id = "XX" style = "fill: # XXXXXX ").
WebChemist
11

Vous mentionnez que vous faites cela parce que IE ne prend pas en charge SVG.

La bonne nouvelle est que IE prend en charge les graphiques vectoriels. D'accord, c'est donc sous la forme d'un langage appelé VML que seul IE prend en charge, plutôt que SVG, mais il est là, et vous pouvez l'utiliser.

Google Maps, entre autres, détectera les capacités du navigateur pour déterminer s'il faut servir SVG ou VML.

Ensuite, il y a la bibliothèque Raphael , qui est une bibliothèque graphique basée sur un navigateur Javascript, qui prend en charge SVG ou VML, toujours en fonction du navigateur.

Un autre qui peut aider: SVGWeb .

Tout cela signifie que vous pouvez prendre en charge vos utilisateurs IE sans avoir à recourir à des graphiques bitmap.

Voir aussi la réponse principale à cette question, par exemple: XSL Transformer SVG en VML

Spudley
la source
+1 pour avoir mentionné Raphael, qui est certainement une bonne solution et mérite d'être étudiée pour son excellente implémentation de graphiques vectoriels multi-navigateurs.
dmp le
10

Lors de la conversion de SVG en PNG transparent, n'oubliez pas de mettre ceci AVANT $imagick->readImageBlob():

$imagick->setBackgroundColor(new ImagickPixel('transparent'));
psycho brm
la source
Comment est-ce possible d'appeler cette méthode avant de lire l'image, je reçois une erreur "Impossible de traiter l'objet Imagick vide". Et oui, mon extension imagick est installée car elle fonctionne et convertit les images.
Denis2310
6

C'est très simple, je travaille sur ce sujet depuis quelques semaines.

Vous avez besoin de la boîte à outils Batik SVG . Téléchargez et placez les fichiers dans le même répertoire que le SVG que vous souhaitez convertir en JPEG , assurez-vous également de le décompresser en premier.

Ouvrez le terminal et exécutez cette commande:

java -jar batik-rasterizer.jar -m image/jpeg -q 0.8 NAME_OF_SVG_FILE.svg

Cela devrait générer un JPEG du fichier SVG. Vraiment facile. Vous pouvez même simplement le placer dans une boucle et convertir des charges de SVG,

import os

svgs = ('test1.svg', 'test2.svg', 'etc.svg') 
for svg in svgs:
    os.system('java -jar batik-rasterizer.jar -m image/jpeg -q 0.8 '+str(svg)+'.svg')
Willi Mentzel
la source
C'est bien. Merci pour le conseil. Je vais l'utiliser en conjonction avec perl pour traiter des charges de fichiers SVG que j'ai créés à partir d'un modèle.
simbabque
2

Je ne connais pas de solution PHP / Apache autonome, car cela nécessiterait une bibliothèque PHP capable de lire et de rendre des images SVG. Je ne suis pas sûr qu'une telle bibliothèque existe - je n'en connais pas.

ImageMagick est capable de pixelliser les fichiers SVG, soit via la ligne de commande ou la liaison PHP, IMagick , mais semble avoir un certain nombre de bizarreries et de dépendances externes comme indiqué par exemple dans ce fil de discussion du forum . Je pense que c'est toujours la voie la plus prometteuse à faire, c'est la première chose que j'examinerais si j'étais toi.

Pekka
la source
2

Ceci est une méthode pour convertir une image svg en gif à l'aide des outils standard de PHP GD

1) Vous mettez l'image dans un élément de canevas dans le navigateur:

<canvas id=myCanvas></canvas>

<script>
var Key='picturename'
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
base_image = new Image();
base_image.src = myimage.svg;
base_image.onload = function(){

    //get the image info as base64 text string

    var dataURL = canvas.toDataURL();
    //Post the image (dataURL) to the server using jQuery post method
    $.post('ProcessPicture.php',{'TheKey':Key,'image': dataURL ,'h': canvas.height,'w':canvas.width,"stemme":stemme } ,function(data,status){ alert(data+' '+status) });
}
</script>    

Et puis convertissez-le sur le serveur (ProcessPicture.php) de (par défaut) png en gif et enregistrez-le. (vous auriez pu aussi enregistrer au format png puis utiliser imagepng au lieu de image gif):

//receive the posted data in php
$pic=$_POST['image'];
$Key=$_POST['TheKey'];
$height=$_POST['h'];
$width=$_POST['w'];
$dir='../gif/'
$gifName=$dir.$Key.'.gif';
 $pngName=$dir.$Key.'.png';

//split the generated base64 string before the comma. to remove the 'data:image/png;base64, header  created by and get the image data
$data = explode(',', $pic);
$base64img = base64_decode($data[1]);
$dimg=imagecreatefromstring($base64img); 

//in order to avoid copying a black figure into a (default) black background you must create a white background

$im_out = ImageCreateTrueColor($width,$height);
$bgfill = imagecolorallocate( $im_out, 255, 255, 255 );
imagefill( $im_out, 0,0, $bgfill );

//Copy the uploaded picture in on the white background
ImageCopyResampled($im_out, $dimg ,0, 0, 0, 0, $width, $height,$width, $height);

//Make the gif and png file 
imagegif($im_out, $gifName);
imagepng($im_out, $pngName);
olévioline
la source
-1

Vous pouvez utiliser Raphaël — JavaScript Library et y parvenir facilement. Cela fonctionnera également dans IE.

Lokesh Kumar Ravi
la source
2
Ajoutez les détails présents dans le lien. Le lien peut se rompre à tout moment
Sagar Jain
-1
$command = 'convert -density 300 ';
                        if(Input::Post('height')!='' && Input::Post('width')!=''){
                            $command.='-resize '.Input::Post('width').'x'.Input::Post('height').' ';
                        }
                        $command.=$svg.' '.$source;
                        exec($command);
                        @unlink($svg);

ou en utilisant: démo potrace : Tool4dev.com

Thành NV
la source