想象通过rsvg转换SVG会从PHP渲染图像空白,但是从命令行可以

时间:2018-05-21 10:30:00

标签: php svg imagemagick imagick librsvg

我已经阅读了一系列问题和网络文章,但似乎与砖墙相抗衡。我已将问题简化为一个非常简单的SVG文件,其中包含一个图像。当通过imagick从PHP转换时,图像呈现为空白,但是使用相同的rsvg引擎从命令行渲染它会很好。

这个项目在我当地的Fedora开发机器上起了很大的作用,只有当我在Centos上进行生产时我遇到了这个麻烦。

环境:Centos 6.2服务器,PHP 5.6.36使用php-fpm,Imagick模块版本3.4.3使用ImageMagick库版本6.7.8-9 2016-06-16

注意:我的apache服务器根本没有jail或chroot文件系统,因此所有正常的路径引用应该可以正常工作,例如:它使用绝对路径名写入Web根目录中的日志文件,没有问题。

$ identify -list format | grep -i svg
     MSVG  SVG       rw+   ImageMagick's own SVG internal renderer
      SVG  SVG       rw+   Scalable Vector Graphics (RSVG 2.39.0)
     SVGZ  SVG       rw+   Compressed Scalable Vector Graphics (RSVG 2.39.0)


$ identify -list delegate | grep -i svg
        cdr =>          "uniconvertor" "%i" "%o.svg"; mv "%o.svg" "%o"
        dot =>          "dot" -Tsvg "%i" -o "%o"
        svg =>          "rsvg-convert" -o "%o" "%i"

所以我相信格式'SVG'表示它将使用RSVG进行PHP转换。

我的SVG(从Inkscape保存):

<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="620mm" height="640mm" viewBox="0 0 620 640" version="1.1" id="svg28" inkscape:version="0.92.3 (2405546, 2018-03-11)" sodipodi:docname="radcover-620x640b.svg">
  <defs id="defs22"/>
  <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="0.24748737" inkscape:cx="1386.1321" inkscape:cy="1147.0763" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" inkscape:window-width="1920" inkscape:window-height="1016" inkscape:window-x="2048" inkscape:window-y="27" inkscape:window-maximized="1" inkscape:snap-global="false"/>
  <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,343)">
  <image fill-opacity="0" stroke="none" stroke-opacity="0" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" x="54.336063385009766" y="-280.25213623046875" width="280.0984802246094" height="175.3288116455078" preserveAspectRatio="none" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="image.jpg" data-number="1" transform="matrix(0.98887052,-0.14877865,0.14877865,0.98887052,0.00000000,0.00000000)"/>
  </g>
</svg>

我的PHP代码:

<?php
$widthPx = 500;
$heightPx = 500;
$outFilename = 'out.png';

$svg = file_get_contents('src.svg');

$im = new Imagick();
$im->readImageBlob('<?xml version="1.0"?>' . $svg);
$im->setImageFormat('png24');

$im->resizeImage($widthPx, $heightPx, imagick::FILTER_LANCZOS, 1);

$im->writeImage($outFilename);

echo "Done.\n";

来自命令行的同样尝试:

convert rsvg:src.svg cli.jpg

使用rsvg-convert(如上面的委托输出中所示):

rsvg-convert src.svg -o rsvg-convert.jpg

结果:两个命令行尝试cli.jpgrsvg-convert.jpg都显示图像就好了。来自PHP的尝试显示空白输出,即<image>元素未呈现。

没有抛出异常,或者我可以找到错误输出。

我试过了:

  • xlink:href设为具有绝对路径的file://网址,即xlink:href="file:///home/myuser/public_html/test/svg-problem/image.jpg"
  • xlink:href设为file://网址,相对于路径当前目录,即xlink:href="file://./image.jpg"
  • xlink:href设为file://网址,不设路径,即xlink:href="file://image.jpg"
  • 使用$im->setFormat('RSVG')强制'RSVG'使用..它会引发Unable to set format的异常。我不明白为什么。

由于通过PHP的转换完成而没有调用setFormat,并且上面显示的格式列表显示它默认使用RSVG,我认为它正在使用RSVG,即使我试图强制RSVG格式失败。

我已经读过你可以重新编译rsvg库,以便在加载文件失败时显示更多错误输出,但是在我的生产cPanel托管服务器上,我很不情愿尝试重新编译任何内容。

任何想法接下来要尝试什么?拔出我的头发:))

2 个答案:

答案 0 :(得分:1)

这里的问题是我不知道ImageMagick如何详细使用librsvg接口。但是最新的librsvg doc中有一些突出的东西。虽然这些段落仅在v2.42文档中添加,但我认为它们也适用于旧版本,根据我在Gnome系统中观察到的内容。

  

当处理SVG时,librsvg只会加载引用的文件,如果它们与基本文件在同一目录中,或者在它的子目录中...这样恶意SVG文件就不能包含目录中的文件上方。

     

如果内存中已有SVG数据,则可以创建内存输入流...请注意,在这种情况下,为内存中的SVG数据指定base_file非常重要。 Librsvg使用base_file来解析指向外部内容的链接,如光栅图像。

根据ImageMagick的实现,这会产生两种可能的解决方案:

  1. 使用

    直接从文件加载SVG
    $im->readImage('src.svg');
    
  2. 使用字符串,但在

    中设置第二个文件参数
    $im->readImageBlob('<?xml version="1.0"?>' . $svg, 'src.svg')
    
  3. 仅作为旁白,$im->setFormat('RSVG')没有任何意义,因为&#34; RSVG&#34;只是库的名称而不是文件格式。

答案 1 :(得分:0)

我无法使其通过IMagick PHP库工作,但是使用proc_open会得到与从命令行调用时完全相同的结果:

$imgPath = '/path/to/image.svg';
$svg = file_get_contents($imgPath);

// ... manipulate svg content here if needed

$descriptorSpec = array(
  0 => array("pipe", "r"), // STDIN
  1 => array("pipe", "w"), // STDOUT
  2 => array("pipe", "w"), // STDERR
);

// fd:0 - STDIN, fd:1: STDOUT,
// svg: and png: tell ImageMagick input/output formats
$cmd = "convert svg:fd:0 png:fd:1";

$proc = proc_open($cmd, $descriptorSpec, $pipes, null, null);

if (!is_resource($proc)) {
  throw new RuntimeException("Failed to proc_open: $cmd");
}

fwrite($pipes[0], $svg);
fclose($pipes[0]);

$png = stream_get_contents($pipes[1]);
fclose($pipes[1]);

if (!$png) {
  throw new RuntimeException("Command returned no output: $cmd");
}

$stdErr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
if ($stdErr) {
  throw new RuntimeException("Command $cmd returned error: $stdErr");
}

$exitStatus = proc_close($proc);

if ($exitStatus) {
  throw new RuntimeException("Command returned $exitStatus: $cmd");
}

// Render PNG with appropriate headers, or save to file, or return etc