ImageMagick的SVG输出出现格式错误

3

我有一个PHP脚本,使用ImageMagick生成图标,并以多种格式返回。位图格式(如JPG、PNG甚至ICO)效果很好,但SVG输出会出现一些奇怪的问题。以下是一个最简脚本来说明这个情况:

<?php

$format = 'jpg';

$im = new Imagick;
$im->newImage(300, 300, new ImagickPixel('#ffffff'));
$im->setImageFormat($format);

$draw = new ImagickDraw();
$draw->setStrokeWidth(0);
$draw->setFillColor(new ImagickPixel('#ff0000'));
$draw->setFillOpacity(.5);
$draw->rectangle(50, 50, 249, 249);

$im->drawImage($draw);

header('Content-Type: ' . $im->getImageMimeType());

echo $im->getImageBlob();

样例输出:

白色画布上的粉色正方形。

如果我将$format的值更改为pngico,图像仍然能够正常工作,并且会发送正确的MIME类型头。当我将格式切换为svg时,会发送正确的MIME类型头,并返回某种类型的SVG,但似乎存在格式错误。这是上面示例脚本的输出:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
  "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="300" height="300">
stroke-width:0;fill:#FFFF00000000;fill-opacity:0.5;  <rect x="50" y="50" width="199" height="199"/>
</svg>

值得注意的是,svg元素上的xmlns属性缺失了。更奇怪的是,矩形属性实际上并没有应用到矩形上,而是被转储为某种内联CSS(但没有正确的上下文来使其作为CSS起作用)。
符合预期并且可行的输出结果应该是:
<?xml version="1.0" standalone="no"?>
<svg width="300" height="300" xmlns="http://www.w3.org/2000/svg">
    <rect x="50" y="50" width="199" height="199" stroke-width="0" fill="#FF0000" fill-opacity="0.5"/>
</svg>

此外,一个具有正确尺寸的viewBox属性会更好,但这并不是必须的。

我是否漏掉了什么?也许在以矢量格式呈现之前需要应用某些操作?也许这是个错误?我找不到任何相关信息,希望能给出指导方向。

更新2019-11-25

情节反转。 我之前没有注意到,但当我省略我的示例中的drawImage步骤时,输出完全不同:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="300px" height="300px" viewBox="0 0 300 300" enable-background="new 0 0 300 300" xml:space="preserve">  <image id="image0" width="300" height="300" x="0" y="0"
    xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsAQAAAABRBrPYAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAd2KE6QAAAA+SURB
VGje7coxAQAACAOg9U9rA1dBf7jJXkw0TdM0TdM0TdM0TdM0TdM0TdM0TdM0TdM0TdM0TdM0TdO0
fytHkUx5BCDW7AAAAABJRU5ErkJggg==" />
</svg>

现在,它创建了一个PNG背景图像,为某种原因内联解析,但也引入了以前缺失的方面,特别是xmlnsviewBox

整个标记看起来与Inkscape生成的东西相同。这可能是有道理的,因为我在某个地方读到这是ImageMagick可以用于呈现SVG的库之一。

就好像drawImage方法破坏了Inkscape解析器一样,然后它退回到某些劣质的引擎或其他东西。希望这能缩小解决方案的范围。


1
你安装了 libmagickcore6-extra 吗?我认为应该已经安装了,因为它可以运行到这一步,但也有可能是旧版本并且支持较少的功能。 - Jeremy Harris
1
我的理解是该库添加了SVG支持。也许可以制作一个Docker容器,并使用libmagickcore-6.q16-6-extra软件包运行Linux? - Jeremy Harris
是的,抱歉我不能提供更多帮助。 - Jeremy Harris
这个问题的悬赏应该更高。由于SVG在技术上是一种标记语言,因此解决这个问题需要一个更好的从光栅图像到矢量图像的转换算法。50分似乎有点...低? - GetSet
@GetSet,我并不是要求别人为我编写一个更好的库。我只是想知道我当前的方法是否有所遗漏。 - ACJ
显示剩余3条评论
1个回答

1
根据这个ImageMagic板块的说法,将图像导出为SVG格式相当棘手,因为该库旨在处理光栅图像而非矢量图形。据我所知,SVG输出始终是一个带有Base64编码二进制数据的图像标签。解决您需求的方法可能是这样的:
$im = new Imagick;
$im->newImage(300, 300, new ImagickPixel('#ffffff'));
$im->setImageFormat('svg');

$im1 = new Imagick();
$im1->newImage(300, 300, new ImagickPixel('#ffffff'));
$im1->setImageFormat('png');
$fillColor = new ImagickPixel('#ff0000');
$draw = new ImagickDraw();
$draw->setFillColor($fillColor);
$draw->setStrokeWidth(0);
$draw->setFillOpacity(.5);
$draw->rectangle(50, 50, 249, 249);
$im1->drawImage($draw);

$im->compositeImage($im1->getimage(), Imagick::COMPOSITE_COPY, 0, 0);

header('Content-Type: image/svg+xml');

echo $im->getImageBlob();

谢谢。我一直以为_ImageMagick_可以输出SVG。虽然从技术上讲这是正确的,但它的输出方式并不特别有用。我想我会回到我的软件中使用一个旧开关,根据请求的MIME类型触发完全不同的逻辑,就像我一直在做的那样。这不是我所希望的,但你的答案很清晰,赏金归你了。 - ACJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接