PHP Imagick 在转换为 PNG 时破坏了 SVG

PHP Imagick breaks SVG on conversion to PNG

我正在更新一个超级旧版应用程序,我正在尝试从 PHP 5.3 迁移到 PHP 5.6。除了 Imagick 之外,一切都很顺利。

在旧服务器上我们使用 Imagick 3.0.0,现在在新服务器上使用 PHP 5.6 我们尝试使用 Imagick 3.6 和 3.7,但出现了同样的问题。在尝试将 SVG 图形转换为 PNG 时,它会变形。

用于测试的代码非常简单:

$image = new Imagick();
$svg = 'GENERATED SVG';
$image->readImageBlob('<?xml version="1.0" encoding="UTF-8" standalone="no"?>'.$svg);
$image->setImageFormat('png24');
$image->writeImage('test.png');

我非常肯定 SVG 本身很好,因为我无法在 docker 机器 运行 类似配置(PHP 5.6 和 Imagick 3.7)上复制这个结果。 Docker 机器设法将 SVG 转换为 PNG 没问题。 Chrome 和其他在线 SVG 预览工具可以正确显示 SVG。

我的 SVG 文件:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="2704" height="1330" viewBox="0 0 2704 1330"><rect fill="#FFFFFF" x="0" y="0" width="2704" height="1330"></rect><text transform="rotate(270 35.357142857143,1086.742)" text-anchor="end" x="35.357142857143" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">1</text><text transform="rotate(270 80.357142857143,1086.742)" text-anchor="end" x="80.357142857143" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">10</text><text transform="rotate(270 125.35714285714,1086.742)" text-anchor="end" x="125.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">11</text><text transform="rotate(270 170.35714285714,1086.742)" text-anchor="end" x="170.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">12</text><text transform="rotate(270 215.35714285714,1086.742)" text-anchor="end" x="215.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">13</text><text transform="rotate(270 260.35714285714,1086.742)" text-anchor="end" x="260.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">14</text><text transform="rotate(270 305.35714285714,1086.742)" text-anchor="end" x="305.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">15</text><text transform="rotate(270 350.35714285714,1086.742)" text-anchor="end" x="350.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">16</text><text transform="rotate(270 395.35714285714,1086.742)" text-anchor="end" x="395.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">17</text><text transform="rotate(270 440.35714285714,1086.742)" text-anchor="end" x="440.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">18</text><text transform="rotate(270 485.35714285714,1086.742)" text-anchor="end" x="485.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">2</text><text transform="rotate(270 530.35714285714,1086.742)" text-anchor="end" x="530.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">3</text><text transform="rotate(270 575.35714285714,1086.742)" text-anchor="end" x="575.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">3</text><text transform="rotate(270 620.35714285714,1086.742)" text-anchor="end" x="620.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">4</text><text transform="rotate(270 665.35714285714,1086.742)" text-anchor="end" x="665.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">6</text><text transform="rotate(270 710.35714285714,1086.742)" text-anchor="end" x="710.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">7</text><text transform="rotate(270 755.35714285714,1086.742)" text-anchor="end" x="755.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">8</text><text transform="rotate(270 800.35714285714,1086.742)" text-anchor="end" x="800.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">9</text><text transform="rotate(270 845.35714285714,1086.742)" text-anchor="end" x="845.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">SEB</text><text transform="rotate(270 890.35714285714,1086.742)" text-anchor="end" x="890.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">Swedbank</text><text text-anchor="middle" x="442" y="1326" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">Savaitė</text><text transform="rotate(270 935.35714285714,1086.742)" text-anchor="end" x="935.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">1</text><text transform="rotate(270 980.35714285714,1086.742)" text-anchor="end" x="980.35714285714" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">10</text><text transform="rotate(270 1025.3571428571,1086.742)" text-anchor="end" x="1025.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">11</text><text transform="rotate(270 1070.3571428571,1086.742)" text-anchor="end" x="1070.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">12</text><text transform="rotate(270 1115.3571428571,1086.742)" text-anchor="end" x="1115.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">13</text><text transform="rotate(270 1160.3571428571,1086.742)" text-anchor="end" x="1160.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">14</text><text transform="rotate(270 1205.3571428571,1086.742)" text-anchor="end" x="1205.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">15</text><text transform="rotate(270 1250.3571428571,1086.742)" text-anchor="end" x="1250.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">16</text><text transform="rotate(270 1295.3571428571,1086.742)" text-anchor="end" x="1295.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">17</text><text transform="rotate(270 1340.3571428571,1086.742)" text-anchor="end" x="1340.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">18</text><text transform="rotate(270 1385.3571428571,1086.742)" text-anchor="end" x="1385.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">2</text><text transform="rotate(270 1430.3571428571,1086.742)" text-anchor="end" x="1430.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">3</text><text transform="rotate(270 1475.3571428571,1086.742)" text-anchor="end" x="1475.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">3</text><text transform="rotate(270 1520.3571428571,1086.742)" text-anchor="end" x="1520.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">4</text><text transform="rotate(270 1565.3571428571,1086.742)" text-anchor="end" x="1565.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">6</text><text transform="rotate(270 1610.3571428571,1086.742)" text-anchor="end" x="1610.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">7</text><text transform="rotate(270 1655.3571428571,1086.742)" text-anchor="end" x="1655.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">8</text><text transform="rotate(270 1700.3571428571,1086.742)" text-anchor="end" x="1700.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">9</text><text transform="rotate(270 1745.3571428571,1086.742)" text-anchor="end" x="1745.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">SEB</text><text transform="rotate(270 1790.3571428571,1086.742)" text-anchor="end" x="1790.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">Swedbank</text><text text-anchor="middle" x="1342" y="1326" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">Delfi.lt</text><rect fill="#f7911e" x="1805" y="46" width="40" height="1027.742"></rect><text text-anchor="middle" x="1825" y="33" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">1</text><text transform="rotate(270 1835.3571428571,1086.742)" text-anchor="end" x="1835.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">Swedbank</text><text transform="rotate(270 1880.3571428571,1086.742)" text-anchor="end" x="1880.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">1</text><text transform="rotate(270 1925.3571428571,1086.742)" text-anchor="end" x="1925.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">10</text><text transform="rotate(270 1970.3571428571,1086.742)" text-anchor="end" x="1970.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">11</text><text transform="rotate(270 2015.3571428571,1086.742)" text-anchor="end" x="2015.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">12</text><text transform="rotate(270 2060.3571428571,1086.742)" text-anchor="end" x="2060.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">13</text><text transform="rotate(270 2105.3571428571,1086.742)" text-anchor="end" x="2105.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">14</text><text transform="rotate(270 2150.3571428571,1086.742)" text-anchor="end" x="2150.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">15</text><text transform="rotate(270 2195.3571428571,1086.742)" text-anchor="end" x="2195.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">16</text><text transform="rotate(270 2240.3571428571,1086.742)" text-anchor="end" x="2240.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">17</text><text transform="rotate(270 2285.3571428571,1086.742)" text-anchor="end" x="2285.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">18</text><text transform="rotate(270 2330.3571428571,1086.742)" text-anchor="end" x="2330.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">2</text><text transform="rotate(270 2375.3571428571,1086.742)" text-anchor="end" x="2375.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">3</text><text transform="rotate(270 2420.3571428571,1086.742)" text-anchor="end" x="2420.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">3</text><text transform="rotate(270 2465.3571428571,1086.742)" text-anchor="end" x="2465.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">4</text><text transform="rotate(270 2510.3571428571,1086.742)" text-anchor="end" x="2510.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">6</text><text transform="rotate(270 2555.3571428571,1086.742)" text-anchor="end" x="2555.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">7</text><text transform="rotate(270 2600.3571428571,1086.742)" text-anchor="end" x="2600.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">8</text><text transform="rotate(270 2645.3571428571,1086.742)" text-anchor="end" x="2645.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">9</text><text transform="rotate(270 2690.3571428571,1086.742)" text-anchor="end" x="2690.3571428571" y="1086.742" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">SEB</text><text text-anchor="middle" x="2242" y="1326" style="font-family:Verdana;font-size:29px;font-weight:bold;color:#000000;">15min.lt</text><line x1="2" y1="1073.742" x2="2702" y2="1073.742" style="stroke:#333333;stroke-width:1"></line><line x1="2" y1="1073.742" x2="2" y2="1313.5" style="stroke:#333333;stroke-width:1"></line><line x1="49" y1="1073.742" x2="49" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="94" y1="1073.742" x2="94" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="139" y1="1073.742" x2="139" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="184" y1="1073.742" x2="184" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="229" y1="1073.742" x2="229" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="274" y1="1073.742" x2="274" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="319" y1="1073.742" x2="319" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="364" y1="1073.742" x2="364" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="409" y1="1073.742" x2="409" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="454" y1="1073.742" x2="454" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="499" y1="1073.742" x2="499" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="544" y1="1073.742" x2="544" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="589" y1="1073.742" x2="589" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="634" y1="1073.742" x2="634" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="679" y1="1073.742" x2="679" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="724" y1="1073.742" x2="724" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="769" y1="1073.742" x2="769" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="814" y1="1073.742" x2="814" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="859" y1="1073.742" x2="859" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="902" y1="1073.742" x2="902" y2="1313.5" style="stroke:#333333;stroke-width:1"></line><line x1="949" y1="1073.742" x2="949" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="994" y1="1073.742" x2="994" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1039" y1="1073.742" x2="1039" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1084" y1="1073.742" x2="1084" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1129" y1="1073.742" x2="1129" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1174" y1="1073.742" x2="1174" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1219" y1="1073.742" x2="1219" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1264" y1="1073.742" x2="1264" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1309" y1="1073.742" x2="1309" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1354" y1="1073.742" x2="1354" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1399" y1="1073.742" x2="1399" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1444" y1="1073.742" x2="1444" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1489" y1="1073.742" x2="1489" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1534" y1="1073.742" x2="1534" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1579" y1="1073.742" x2="1579" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1624" y1="1073.742" x2="1624" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1669" y1="1073.742" x2="1669" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1714" y1="1073.742" x2="1714" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1759" y1="1073.742" x2="1759" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1802" y1="1073.742" x2="1802" y2="1313.5" style="stroke:#333333;stroke-width:1"></line><line x1="1849" y1="1073.742" x2="1849" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1894" y1="1073.742" x2="1894" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1939" y1="1073.742" x2="1939" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="1984" y1="1073.742" x2="1984" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2029" y1="1073.742" x2="2029" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2074" y1="1073.742" x2="2074" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2119" y1="1073.742" x2="2119" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2164" y1="1073.742" x2="2164" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2209" y1="1073.742" x2="2209" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2254" y1="1073.742" x2="2254" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2299" y1="1073.742" x2="2299" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2344" y1="1073.742" x2="2344" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2389" y1="1073.742" x2="2389" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2434" y1="1073.742" x2="2434" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2479" y1="1073.742" x2="2479" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2524" y1="1073.742" x2="2524" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2569" y1="1073.742" x2="2569" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2614" y1="1073.742" x2="2614" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2659" y1="1073.742" x2="2659" y2="1284" style="stroke:#333333;stroke-width:1"></line><line x1="2704" y1="1073.742" x2="2704" y2="1313.5" style="stroke:#333333;stroke-width:1"></line></svg>

我想不出我们的服务器无法将此 SVG 转换为 PNG 的任何原因。

惹恼 ImageMagic 的是 transform/rotate。如果将 <text> 包装成 <g> 然后 transform/translate <g><text> 的 x,y 位置并删除 x,y 参数形式旋转它的行为。

我没有对此进行测试,但您应该将样式属性替换为 <text> 上相应的 SVG 属性。

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="2704" height="1330" viewBox="0 0 2704 1330">
  <rect fill="#FFF" x="0" y="0" width="2704" height="1330"></rect>
  <g transform="translate(35.357142857143 1086.742)">
    <text transform="rotate(270)" text-anchor="end" font-family="Verdana" font-size="29" font-weight="bold" fill="#000000">1</text>
  </g>
  <g transform="translate(80.357142857143 1086.742)">
    <text transform="rotate(270)" text-anchor="end" font-family="Verdana" font-size="29" font-weight="bold" fill="#000000">10</text>
  </g>
</svg>

问题中描述的问题已被@chrwahl接受的答案成功解决。

但深入挖掘后,我发现 Imagick SVG 处理一般存在更多问题,例如完全忽略自定义字体、使文本和其他线条比应有的粗体很多、为图形添加不必要的边框以及其他一些情况一些文本忽略了它的定位。

所有这些描述的问题以及问题中的问题都是由 Imagick 使用 MSVG 有缺陷的 SVG 引擎引起的,问题已通过安装 Inkscape.

解决