2012-04-30 30 views
10

Tôi gửi một chuỗi đại diện của một tập tin SVG đến máy chủ và sử dụng Imagick để tắt chức năng này thành một jpeg theo cách sau đây:làm thế nào để thay đổi kích thước tập tin SVG lên với 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(); 

Tuy nhiên tôi muốn thay đổi kích thước SVG trước khi rasterize nó để hình ảnh kết quả lớn hơn kích thước được chỉ định trong tệp SVG.

tôi sửa đổi mã của tôi như sau:

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

Mã này nên làm việc ra các quyết định và thay đổi kích thước SVG cho phù hợp. Nó hoạt động hoàn hảo nếu canvas SVG và các phần tử của nó có độ rộng dựa trên 'phần trăm', tuy nhiên nó không hoạt động với các phần tử được xác định trong 'px'. Thật không may là một yêu cầu.

Một chuỗi SVG điển hình mà sẽ được gửi đến máy chủ trông như thế này:

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

Như bạn có thể thấy các yếu tố tạo nên SVG này có độ rộng định nghĩa pixel và chiều cao (sử dụng phần trăm là tiếc không phải là một tùy chọn cho ứng dụng này)

Có cách nào xung quanh vấn đề này không? Hoặc bất kỳ phương pháp chuyển đổi SVG sang png khác và hiển thị nó ở kích thước nhất định mà không làm giảm chất lượng.

Cảm ơn.

EDIT: Mặc dù tôi chưa bao giờ thực sự tìm được giải pháp hoàn hảo. Thay vào đó, tôi đã kết thúc việc gửi dữ liệu SVG dưới dạng json, lặp qua phía máy chủ và chia tỷ lệ pixel thành chiều cao dự kiến.

Sau đó, sau nhiều lần thử và lỗi, tôi nhận ra rằng imagemagick có vấn đề với các lệnh chuyển đổi/xoay SVG chuẩn, ném bất kỳ phần tử thao tác nào ra khỏi vỏ. Tôi đã kết thúc quá trình chuyển đổi 'inkscape' để hiển thị SVG kết quả dưới dạng hình ảnh rasterised. Và tất cả là tốt. Tôi vẫn đang đào sâu vào một giải pháp có tiềm năng để bù đắp những khác biệt mà tưởng tượng tạo ra. Nếu tôi có bất kỳ thành công nào, tôi sẽ cập nhật lại câu hỏi này.

+0

Từ [câu hỏi khác của bạn] (http://stackoverflow.com/questions/10400897/how-to-convert-an-svg-string-into-a-jpg-with-inkscape) Tôi nghi ngờ bạn hiện có chuyển sang Inkscape (có thể chia tỷ lệ hình ảnh bằng các tùy chọn trên dòng lệnh). Nếu đó là trường hợp, bạn sẽ thêm một bình luận bên dưới câu hỏi? ':)'. – halfer

+0

Thật vậy ... tôi sẽ chỉnh sửa câu hỏi của mình cho phù hợp. :-) – gordyr

+0

Rất thích xem thêm chi tiết về giải pháp Inkscape của bạn! – supertrue

Trả lời

12

Là một workaround của php_imagick's bug, bạn có thể mở rộng chiều rộng svg của = ".." và 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; 
} 

Sau đó, bạn có thể dễ dàng tạo ra PNG trong suốt đẹp!

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

Lưu ý: Tôi đã tìm kiếm một giải pháp làm thế nào để rescale SVG từ kích thước nhỏ ban đầu của nó. Tuy nhiên có vẻ như imagick :: setResolution bị hỏng. Tuy nhiên, thư viện ImageMagick đang hoạt động, vì vậy bạn có thể sử dụng exec ('convert ...') (có thể bị vô hiệu vì lý do bảo mật bởi nhà cung cấp hosting).

Vì vậy, để tạo 50x50 thumbnail từ svg nhỏ bạn sẽ làm gì:

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

Chiều rộng và chiều cao phải nằm trong "px". –

+0

Một lỗi khác là hình ảnh không có quy mô tốt, chúng bị mờ. Cách giải quyết là sử dụng chiều rộng và chiều cao lớn hơn (nó sẽ không ảnh hưởng đến kích thước tương đối nếu bạn sử dụng svg viewBox = "0 0 chiều rộng chiều rộng" không có pixel và chiều cao chiều rộng svg bằng pixel). –

0

Dưới đây là ví dụ về cách chụp ảnh đã có trong chuỗi (ví dụ, từ cơ sở dữ liệu) và thay đổi kích thước, thêm đường viền và in ra. Tôi sử dụng này cho thấy logo đại lý bán lẻ

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

Cảm ơn chintu, thật không may, điều này là không có sự giúp đỡ cho tôi vì tôi muốn thay đổi kích thước SVG trước khi nó được rasterized để đảm bảo chất lượng đầy đủ. Tôi có thể tất nhiên chỉ đơn giản là thay đổi kích thước jpeg cuối cùng mà không có bất kỳ vấn đề, nhưng hình ảnh kết quả sẽ mất chất lượng. Thay đổi kích thước SVG trước (vì SVG là định dạng vectơ) và 'then' cuối cùng là rastering hình ảnh sẽ giữ chất lượng trong quá trình upsize. Tôi xin lỗi nếu tôi không đủ rõ ràng trong bài đăng đầu tiên của tôi. Cảm ơn bạn đã thử. :-) – gordyr

1

tôi đang tìm kiếm một giải pháp, và tôi tìm thấy this chỉ sau khi đọc bài này, và làm việc như một nét duyên dáng:

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

Tín dụng chuyển đến tác giả của nhận xét đó trong trang hướng dẫn sử dụng php.

1

Bạn không cần tưởng tượng để hoàn thành tác vụ này. Ví dụ: bạn sẽ không đổi kích thước svg của mình (w: 60px, h: 70px) => (w: 36px, h: 36px) để nhận biểu tượng cho một nút.

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

Hãy cẩn thận bằng cách đặt dịch (x, y) vì có thể nội dung svg của bạn có thể được đặt bên ngoài hộp và bạn sẽ không thấy gì ngoại trừ nền.

Tập lệnh của tôi ở trên chỉ hoạt động bình thường nếu bản dịch ban đầu của bạn được đặt thành (0,0). Bạn có thể sử dụng số này

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

để đặt thành (0,0) và điều chỉnh dữ liệu đường dẫn đến cài đặt mới vì dữ liệu đường dẫn phụ thuộc vào dịch và ngược lại.

Tôi sử dụng hai tập lệnh này để tạo biểu tượng png bằng cách đổi kích thước biểu tượng svg của tôi thành thứ nguyên tôi cần và chuyển đổi thành png mà không làm giảm chất lượng.

Tôi hy vọng điều đó có ý nghĩa rõ ràng.

Các vấn đề liên quan