简体   繁体   English

Imagick 通过 rsvg 转换 SVG 会使图像从 PHP 呈现空白,但从命令行可以

[英]Imagick converting SVG via rsvg renders image blank from PHP, but ok from command line

I've read a range of Questions and web articles but seem to be up against a brick wall.我已经阅读了一系列问题和网络文章,但似乎遇到了障碍。 I have reduced my problem to a very simple SVG file, containing a single image.我已将我的问题简化为一个非常简单的 SVG 文件,其中包含一个图像。 When converted from PHP via imagick, the image is rendered blank, but from the command line using the same rsvg engine it renders fine.当通过 imagick 从 PHP 转换时,图像呈现为空白,但从使用相同的 rsvg 引擎的命令行它呈现良好。

This project has worked like a charm on my local Fedora dev machine, only when moving to production on Centos have I had this trouble.这个项目在我本地的 Fedora 开发机器上非常有效,只有当我在 Centos 上进行生产时才会遇到这个问题。

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

Note : my apache server does not jail or chroot the file system at all, so all normal path references should work fine, eg it writes to a log file in the web root using an absolute path name without problem.注意:我的 apache 服务器根本不会监狱或 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"

So I believe the format 'SVG' indicates it will use RSVG for the conversion from PHP.所以我相信'SVG'格式表明它将使用RSVG从PHP进行转换。

My SVG (saved from Inkscape):我的 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>

My PHP code:我的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";

The same attempt from command line:命令行中的相同尝试:

convert rsvg:src.svg cli.jpg

The same using rsvg-convert (as indicated in delegates output above):使用rsvg-convert (如上面的委托输出所示):

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

Result: Both command line attempts, cli.jpg and rsvg-convert.jpg , show the image just fine.结果:命令行尝试cli.jpgrsvg-convert.jpg可以很好地显示图像。 The attempt from PHP shows a blank output ie the <image> element did not render. PHP 的尝试显示了一个空白输出,即<image>元素没有呈现。

There is no exception thrown, or error output that I can find.没有抛出异常或我可以找到的错误输出。

I have tried:我试过了:

  • Make the xlink:href be a file:// URL with absolute path, ie xlink:href="file:///home/myuser/public_html/test/svg-problem/image.jpg"使xlink:href成为具有绝对路径的file:// URL,即xlink:href="file:///home/myuser/public_html/test/svg-problem/image.jpg"
  • Make the xlink:href be a file:// URL with relative to path current directory ie xlink:href="file://./image.jpg"使xlink:href成为一个file:// URL,相对于路径当前目录即xlink:href="file://./image.jpg"
  • Make the xlink:href be a file:// URL with no path ie xlink:href="file://image.jpg"使xlink:href成为没有路径的file:// URL,即xlink:href="file://image.jpg"
  • Force 'RSVG' usage with $im->setFormat('RSVG') .. it throws an exception with Unable to set format .使用$im->setFormat('RSVG')强制使用$im->setFormat('RSVG') .. 它会抛出Unable to set format异常。 I cannot see why.我不明白为什么。

Since the conversion via PHP completes without calling setFormat , and the formats list shows above says it will use RSVG by default, I think it's using RSVG, even though my attempt to force RSVG format failed.由于通过 PHP 的转换无需调用setFormat即可完成,并且上面显示的格式列表表示默认情况下将使用 RSVG,我认为它使用的是 RSVG,即使我尝试强制 RSVG 格式失败。

I have read that you can recompile the rsvg library to show more error output when it fails to load a file, but on my production cPanel managed server I am very loath to try recompiling anything.我读到您可以重新编译 rsvg 库以在无法加载文件时显示更多错误输出,但是在我的生产 cPanel 托管服务器上,我非常不愿意尝试重新编译任何内容。

Any ideas what to try next?有什么想法接下来要尝试什么吗? Pulling my hair out here :)在这里拉我的头发:)

The problem here is that I don't know how ImageMagick uses the librsvg interface in detail.这里的问题是我不知道 ImageMagick 是如何详细使用 librsvg 接口的。 But a few things stand out in the latest librsvg doc .但是在最新的librsvg doc中,有几件事很突出。 While the paragraphs were only added in the v2.42 doc, I think they apply also to older versions, according to what I have observed in Gnome systems.虽然这些段落仅在 v2.42 文档中添加,但根据我在 Gnome 系统中的观察,我认为它们也适用于旧版本。

When processing an SVG, librsvg will only load referenced files if they are in the same directory as the base file, or in a subdirectory of it...This is so that malicious SVG files cannot include files that are in a directory above.在处理 SVG 时,librsvg 只会加载与基本文件位于同一目录中或位于其子目录中的引用文件……这样恶意 SVG 文件就无法包含位于上述目录中的文件。

If you already have SVG data in memory, you can create a memory input stream...Note that in this case, it is important that you specify the base_file for the in-memory SVG data.如果内存中已经有 SVG 数据,则可以创建一个内存输入流...注意,在这种情况下,为内存中的 SVG 数据指定 base_file 很重要。 Librsvg uses the base_file to resolve links to external content, like raster images. Librsvg 使用 base_file 来解析指向外部内容的链接,例如光栅图像。

Depending on the ImageMagick implementation, that leads to two possible solutions:根据 ImageMagick 的实现,这会导致两种可能的解决方案:

  1. Load the SVG directly from a file with直接从文件加载 SVG

     $im->readImage('src.svg');
  2. Use the string, but set the second file argument in使用字符串,但在中设置第二个文件参数

     $im->readImageBlob('<?xml version="1.0"?>' . $svg, 'src.svg')

Only as an aside, $im->setFormat('RSVG') doesn't make any sense, since "RSVG" is only the name of the library and not a file format. $im->setFormat('RSVG')$im->setFormat('RSVG')没有任何意义,因为“RSVG”只是库的名称而不是文件格式。

I could not make it work through IMagick PHP library, but using proc_open leads to exactly same result as when calling from command line:我无法通过 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM