Konwertuj obraz SVG na PNG za pomocą PHP

111

Pracuję nad projektem internetowym, który obejmuje dynamicznie generowaną mapę Stanów Zjednoczonych, kolorującą różne stany w oparciu o zestaw danych.

Ten plik SVG daje mi dobrą, pustą mapę Stanów Zjednoczonych i bardzo łatwo jest zmienić kolor każdego stanu. Trudność polega na tym, że przeglądarki IE nie obsługują SVG, więc aby móc korzystać z wygodnej składni, jaką oferuje svg, będę musiał przekonwertować go na JPG.

Idealnie chciałbym to zrobić tylko z biblioteką GD2, ale przydałby się również ImageMagick. Nie mam absolutnie pojęcia, jak to zrobić.

Rozważone zostanie każde rozwiązanie, które pozwoliłoby mi dynamicznie zmieniać kolory stanów na mapie USA. Najważniejsze jest to, że zmiana kolorów jest łatwa w locie i że jest to przeglądarka internetowa. Tylko rozwiązania PHP / Apache, proszę.

Michael Berkompas
źródło
czy są jakieś klasy zaprojektowane do przenoszenia SVG na VML? w ten sposób nadal możesz mieć rozwiązanie typu „HTML5”
Patrick
spójrz na moją odpowiedź. dokładnie to, czego potrzebujesz

Odpowiedzi:

142

To zabawne, że o to zapytałeś, niedawno zrobiłem to dla strony mojej pracy i pomyślałem, że powinienem napisać tutorial ... Oto jak to zrobić z PHP / Imagick, który używa 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();

kroki zastępowania kolorów wyrażeń regularnych mogą się różnić w zależności od ścieżki svg xml oraz sposobu przechowywania wartości identyfikatora i koloru. Jeśli nie chcesz przechowywać pliku na serwerze, możesz wyprowadzić obraz jako podstawowy 64, taki jak

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

(przed użyciem wyczyść / zniszcz), ale np. ma problemy z PNG jako base64, więc prawdopodobnie będziesz musiał wypisać base64 jako jpeg

możesz zobaczyć tutaj przykład, który zrobiłem dla mapy obszaru sprzedaży byłego pracodawcy:

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

Koniec: wprowadź opis obrazu tutaj

Edytować

Od czasu napisania powyższego wymyśliłem 2 ulepszone techniki:

1) zamiast pętli regex, aby zmienić stan wypełnienia, użyj CSS, aby utworzyć reguły stylu, takie jak

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

a następnie możesz wykonać pojedynczą zamianę tekstu, aby wstrzyknąć swoje reguły css do svg przed kontynuowaniem tworzenia imagick jpeg / png. Jeśli kolory się nie zmieniają, sprawdź, czy w znacznikach ścieżki nie ma żadnych stylów wypełnienia wbudowanego, które przesłaniają css.

2) Jeśli nie musisz faktycznie tworzyć pliku obrazu jpeg / png (i nie musisz obsługiwać przestarzałych przeglądarek), możesz manipulować plikiem svg bezpośrednio za pomocą jQuery. Nie możesz uzyskać dostępu do ścieżek svg podczas osadzania svg za pomocą img lub tagów obiektu, więc musisz bezpośrednio dołączyć svg xml do html swojej strony internetowej, na przykład:

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

wtedy zmiana kolorów jest tak prosta, jak:

<script type="text/javascript" src="/path/to/jquery.js"></script>
<script type="text/javascript">
    $('#CA').css('fill', 'blue');
    $('#NY').css('fill', '#ff0000');
</script>
WebChemist
źródło
1
Dziękuję za bardzo dokładny i przydatny samouczek, jak to zrobić. Z pewnością użyję twojego rozwiązania jako kopii zapasowej, ale chętnie spróbuję po prostu uzyskać zgodność svg we wszystkich głównych przeglądarkach.
Michael Berkompas
1
Format SVG nie jest obsługiwany w wersji IE8 lub niższej bez konieczności instalowania przez użytkownika wtyczki przeglądarki SVG - ze strony Wikipedii SVG: „Wszystkie główne nowoczesne przeglądarki internetowe obsługują i renderują znaczniki SVG bezpośrednio z bardzo godnym uwagi wyjątkiem Microsoft Internet Explorer (IE) [ 3] Internet Explorer 9 beta obsługuje podstawowy zestaw funkcji SVG. [4] Obecnie obsługa przeglądarek działających pod kontrolą systemu Android jest również ograniczona. ”
WebChemist
1
Tak, ale svgweb wydaje się eliminować wszystkie niezgodności przy użyciu odrobiny js i flasha. To rozwiązanie, które wybrałem.
Michael Berkompas
2
Podoba mi się twoje czyste i szybkie rozwiązanie. Osobiście podczas interakcji z plikami xml wolę używać parsera dom, aby czuć się bezpieczniej niż z wyrażeniami regularnymi. Coś jak:$dom = new DOMDocument(); $dom->loadXML( $svg ); $dom->getElementsByTagName('image')->item(0)->setAttribute('id', $state); $svg = $dom->saveXML();
Tapper
parser xml byłby bezpieczniejszym, choć nieco wolniejszym rozwiązaniem z każdym innym svg ... w tym przypadku wyrażenie regularne jest bezpieczne, ponieważ upewniłem się, że atrybuty każdego stanu zostały sformatowane dokładnie tak, jak (id = "XX" style = "fill: # XXXXXX ”).
WebChemist
11

Wspominasz, że robisz to, ponieważ IE nie obsługuje formatu SVG.

Dobrą wiadomością jest to, że IE robi grafiki wektorowej wsparcie. OK, więc jest w formie języka zwanego VML, który obsługuje tylko IE, a nie SVG, ale jest i możesz go używać.

Mapy Google, między innymi, wykryją możliwości przeglądarki, aby określić, czy mają obsługiwać SVG, czy VML.

Jest też biblioteka Raphael , która jest biblioteką graficzną opartą na przeglądarce Javascript, która obsługuje SVG lub VML, ponownie w zależności od przeglądarki.

Kolejny, który może pomóc: SVGWeb .

Wszystko to oznacza, że ​​możesz wspierać użytkowników IE bez konieczności uciekania się do grafiki bitmapowej.

Zobacz także najlepszą odpowiedź na to pytanie, na przykład: XSL Transform SVG to VML

Spudley
źródło
+1 za wspomnienie o raphaelu, co jest zdecydowanie dobrym rozwiązaniem i warte zbadania ze względu na doskonałą implementację grafiki wektorowej w różnych przeglądarkach.
dmp
10

Konwertując SVG do przezroczystego PNG, nie zapomnij umieścić tego PRZED $imagick->readImageBlob():

$imagick->setBackgroundColor(new ImagickPixel('transparent'));
psycho brm
źródło
Jak to możliwe, aby wywołać tę metodę przed odczytaniem obrazu, pojawia się błąd „Nie można przetworzyć pustego obiektu Imagick”. I tak, moje rozszerzenie imagick jest instalowane, ponieważ działa i konwertuje obrazy.
Denis2310
6

To bardzo łatwe, pracowałem nad tym przez ostatnie kilka tygodni.

Potrzebujesz Batik SVG Toolkit . Pobierz i umieść pliki w tym samym katalogu, co plik SVG, który chcesz przekonwertować do formatu JPEG , a także upewnij się, że najpierw go rozpakujesz.

Otwórz terminal i uruchom to polecenie:

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

To powinno wyprowadzić JPEG z pliku SVG. Naprawdę proste. Możesz nawet umieścić go w pętli i przekonwertować wiele plików 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
źródło
To jest świetne. Dzięki za wskazówkę. Zamierzam użyć go w połączeniu z Perlem do przetwarzania wsadowego plików SVG, które utworzyłem z szablonu.
simbabque
2

Nie znam samodzielnego rozwiązania PHP / Apache, ponieważ wymagałoby to biblioteki PHP, która może odczytywać i renderować obrazy SVG. Nie jestem pewien, czy taka biblioteka istnieje - nie znam żadnej.

ImageMagick jest w stanie rasteryzować pliki SVG za pomocą wiersza poleceń lub powiązania PHP, IMagick , ale wydaje się, że ma wiele dziwactw i zewnętrznych zależności, jak pokazano np. W tym wątku na forum . Myślę, że to wciąż najbardziej obiecująca droga, to pierwsza rzecz, na którą bym spojrzał, gdybym był tobą.

Pekka
źródło
2

Jest to metoda konwersji obrazu svg do gif przy użyciu standardowych narzędzi php GD

1) Umieszczasz obraz w elemencie canvas w przeglądarce:

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

Następnie przekonwertuj go na serwerze (ProcessPicture.php) z (domyślnego) png do gif i zapisz. (mogłeś również zapisać jako png, a następnie użyć imagepng zamiast obrazu 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);
olewiolin
źródło
-1

Możesz użyć Raphaël — JavaScript Library i łatwo to osiągnąć. Będzie działać również w IE.

Lokesh Kumar Ravi
źródło
2
Dodaj szczegóły obecne w linku. Link może się zepsuć w dowolnym momencie
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);

lub używając: potrace demo: Tool4dev.com

Thành NV
źródło