Konversi gambar SVG ke PNG dengan PHP


111

Saya sedang mengerjakan proyek web yang melibatkan peta AS yang dibuat secara dinamis, mewarnai berbagai negara bagian berdasarkan kumpulan data.

File SVG ini memberi saya peta kosong AS yang bagus dan sangat mudah untuk mengubah warna setiap negara bagian. Kesulitannya adalah browser IE tidak mendukung SVG jadi agar saya dapat menggunakan sintaks praktis yang ditawarkan oleh svg, saya harus mengubahnya menjadi JPG.

Idealnya, saya ingin melakukan ini hanya dengan pustaka GD2 tetapi juga dapat menggunakan ImageMagick. Saya sama sekali tidak tahu bagaimana melakukan ini.

Solusi apa pun yang memungkinkan saya mengubah warna negara bagian secara dinamis pada peta AS akan dipertimbangkan. Kuncinya adalah mudah untuk mengubah warna dengan cepat dan itu adalah lintas browser. Mohon solusi PHP / Apache saja.


apakah ada kelas yang dirancang untuk mem-port SVG ke VML? dengan cara itu Anda masih bisa memiliki 'HTML5'-jenis solusi
Patrick

lihat jawaban saya. persis apa yang Anda butuhkan

Jawaban:


142

Lucu sekali Anda menanyakan hal ini, saya baru saja melakukan ini untuk situs pekerjaan saya dan saya berpikir saya harus menulis tutorial ... Berikut adalah cara melakukannya dengan PHP / Imagick, yang menggunakan 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();

langkah-langkah penggantian warna regex dapat bervariasi tergantung pada jalur svg xml dan cara Anda menyimpan nilai id & warna. Jika Anda tidak ingin menyimpan file di server, Anda dapat menampilkan gambar sebagai basis 64 seperti

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

(sebelum Anda menggunakan hapus / hancurkan) tetapi memiliki masalah dengan PNG sebagai base64 jadi Anda mungkin harus mengeluarkan base64 sebagai jpeg

Anda dapat melihat contoh di sini yang saya lakukan untuk peta wilayah penjualan perusahaan sebelumnya:

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

Selesai: masukkan deskripsi gambar di sini

Edit

Sejak menulis di atas, saya telah menemukan 2 teknik yang ditingkatkan:

1) alih-alih loop regex untuk mengubah status isian, gunakan CSS untuk membuat aturan gaya seperti

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

dan kemudian Anda dapat melakukan satu penggantian teks untuk memasukkan aturan css Anda ke dalam svg sebelum melanjutkan dengan pembuatan imagick jpeg / png. Jika warna tidak berubah, periksa untuk memastikan Anda tidak memiliki gaya isian sebaris di tag jalur Anda yang menggantikan css.

2) Jika Anda tidak harus benar-benar membuat file gambar jpeg / png (dan tidak perlu mendukung browser lama), Anda dapat memanipulasi svg langsung dengan jQuery. Anda tidak dapat mengakses jalur svg saat menyematkan svg menggunakan img atau tag objek, jadi Anda harus langsung menyertakan svg xml di halaman web Anda html seperti:

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

lalu mengubah warnanya semudah:

<script type="text/javascript" src="/path/to/jquery.js"></script>
<script type="text/javascript">
    $('#CA').css('fill', 'blue');
    $('#NY').css('fill', '#ff0000');
</script>

1
Terima kasih atas tutorial yang sangat tepat dan berguna tentang cara melakukan ini. Saya pasti akan menggunakan solusi Anda sebagai cadangan tetapi saya ingin sekali mencoba dan hanya mendapatkan kompatibilitas svg di semua browser utama.
Michael Berkompas

1
SVG tidak didukung di ie8 atau lebih rendah tanpa mengharuskan pengguna menginstal plugin penampil svg - dari halaman Wikipedia SVG: "Semua browser web modern utama, mendukung dan merender markup SVG secara langsung dengan pengecualian yang sangat terkenal dari Microsoft Internet Explorer (IE) [ 3] Internet Explorer 9 beta mendukung rangkaian fitur SVG dasar. [4] Saat ini, dukungan untuk browser yang berjalan di bawah Android juga terbatas. "
WebChemist

1
Ya, tetapi svgweb tampaknya mengatasi semua ketidaksesuaian dengan menggunakan sedikit js dan flash. Itulah solusi yang saya gunakan.
Michael Berkompas

2
Saya suka solusi bersih dan cepat Anda. Secara pribadi ketika berinteraksi dengan file xml saya lebih suka menggunakan parser dom agar merasa lebih aman daripada dengan regex. Sth seperti:$dom = new DOMDocument(); $dom->loadXML( $svg ); $dom->getElementsByTagName('image')->item(0)->setAttribute('id', $state); $svg = $dom->saveXML();
Penyadap

pengurai xml akan menjadi solusi yang lebih aman, meskipun sedikit lebih lambat, dengan svg lainnya ... dalam hal ini regex aman karena saya memastikan atribut setiap negara diformat persis seperti (id = "XX" style = "fill: # XXXXXX ").
WebChemist

11

Anda menyebutkan bahwa Anda melakukan ini karena IE tidak mendukung SVG.

Kabar baiknya adalah bahwa IE tidak grafis dukungan vektor. Oke, jadi itu dalam bentuk bahasa yang disebut VML yang hanya mendukung IE, bukan SVG, tetapi bahasa itu ada, dan Anda dapat menggunakannya.

Google Maps antara lain akan mendeteksi kapabilitas browser untuk menentukan apakah akan menyajikan SVG atau VML.

Lalu ada pustaka Raphael , yang merupakan pustaka grafis berbasis peramban Javascript, yang mendukung SVG atau VML, sekali lagi tergantung pada peramban.

Satu lagi yang mungkin bisa membantu: SVGWeb .

Semua itu berarti Anda dapat mendukung pengguna IE Anda tanpa harus menggunakan grafik bitmap.

Lihat juga jawaban teratas untuk pertanyaan ini, misalnya: XSL Mengubah SVG menjadi VML


1 untuk menyebutkan raphael, yang jelas merupakan solusi yang baik dan patut diselidiki untuk implementasi yang sangat baik dari grafik vektor lintas browser.
dmp

10

Saat mengonversi SVG menjadi PNG transparan, jangan lupa untuk meletakkan ini SEBELUM $imagick->readImageBlob():

$imagick->setBackgroundColor(new ImagickPixel('transparent'));

Bagaimana mungkin untuk memanggil metode itu sebelum membaca gambar, saya menerima pesan kesalahan "Tidak dapat memproses objek Imagick kosong". Dan ya, ekstensi imagick saya dipasang saat berfungsi dan mengonversi gambar.
Denis2310

6

Ini sangat mudah, telah melakukan pekerjaan ini selama beberapa minggu terakhir.

Anda membutuhkan Perangkat SVG Batik . Unduh, dan letakkan file di direktori yang sama dengan SVG yang ingin Anda ubah menjadi JPEG , juga pastikan Anda mengekstraknya terlebih dahulu.

Buka terminal, dan jalankan perintah ini:

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

Itu harus menghasilkan JPEG dari file SVG. Sangat mudah. Anda bahkan dapat menempatkannya dalam satu lingkaran dan mengonversi banyak 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')

Ini bagus. Terima kasih atas tipnya. Saya akan menggunakannya bersama dengan perl untuk banyak proses batch file SVG yang saya buat dari template.
simbabque

2

Saya tidak tahu tentang solusi PHP / Apache mandiri, karena ini akan memerlukan pustaka PHP yang dapat membaca dan merender gambar SVG. Saya tidak yakin perpustakaan seperti itu ada - saya tidak tahu satu pun.

ImageMagick dapat meraster file SVG, baik melalui baris perintah atau pengikatan PHP, IMagick , tetapi tampaknya memiliki sejumlah kebiasaan dan ketergantungan eksternal seperti yang ditunjukkan misalnya di utas forum ini . Saya pikir itu masih cara yang paling menjanjikan untuk dilakukan, itu hal pertama yang akan saya perhatikan jika saya menjadi Anda.


2

Ini adalah metode untuk mengonversi gambar svg menjadi gif menggunakan alat GD php standar

1) Anda memasukkan gambar ke dalam elemen kanvas di browser:

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

Dan kemudian mengubahnya di server (ProcessPicture.php) dari (default) png menjadi gif dan menyimpannya. (Anda juga bisa menyimpan sebagai png kemudian gunakan imagepng daripada gambar 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);


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

atau menggunakan: potrace demo: Tool4dev.com

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.