meyfa / php-svg

Vector graphics (SVG) library for PHP
https://packagist.org/packages/meyfa/php-svg
MIT License
509 stars 92 forks source link

SVGImage cannot be rasterised to PNG or JPG #189

Closed jonwinstanley closed 1 year ago

jonwinstanley commented 1 year ago

This is a FYI for anyone intending to use this library. You cannot currently rasterise embedded Images.

The library is really great for building SVGs from shapes and text, but if you include an embedded image, you can no longer rasterise the SVG to PNG/JPG etc.

meyfa commented 1 year ago

Thanks for reporting! Could you go into a bit more detail about the specific problem you're seeing?

if you include an embedded image, you can no longer rasterise the SVG to PNG/JPG etc.

This sounds a bit worrying. I'd expect rasterization to still work, just maybe not include the embedded image, but the rest should still be rendered, at least.

Can you provide a reproducible example?

psociety commented 1 year ago

I was going to open a thread on the forum thinking it was only me/i was doing something wrong. Here's an svg:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="280px" viewBox="0 0 264 280" width="264px">
  <defs>
    <circle cx="120" cy="120" id="path-1" r="120" />
    <path d="M12,160 C12,226.27417 65.72583,280 132,280 C198.27417,280 252,226.27417 252,160 L264,160 L264,-1.42108547e-14 L-3.19744231e-14,-1.42108547e-14 L-3.19744231e-14,160 L12,160 Z" id="path-2" />
    <path d="M124,144.610951 L124,163 L128,163 L128,163 C167.764502,163 200,195.235498 200,235 L200,244 L0,244 L0,235 C-4.86974701e-15,195.235498 32.235498,163 72,163 L72,163 L76,163 L76,144.610951 C58.7626345,136.422372 46.3722246,119.687011 44.3051388,99.8812385 C38.4803105,99.0577866 34,94.0521096 34,88 L34,74 C34,68.0540074 38.3245733,63.1180731 44,62.1659169 L44,56 L44,56 C44,25.072054 69.072054,5.68137151e-15 100,0 L100,0 L100,0 C130.927946,-5.68137151e-15 156,25.072054 156,56 L156,62.1659169 C161.675427,63.1180731 166,68.0540074 166,74 L166,88 C166,94.0521096 161.51969,99.0577866 155.694861,99.8812385 C153.627775,119.687011 141.237365,136.422372 124,144.610951 Z" id="path-3" />
  </defs>
  <g fill="none" fill-rule="evenodd" stroke="none" stroke-width="1">
    <g transform="translate(-825.000000, -1100.000000)">
      <g transform="translate(825.000000, 1100.000000)">
        <g fill-rule="evenodd" mask="url(#mask-5)" stroke-width="1">
          <g transform="translate(32.000000, 36.000000)">
            <mask fill="#FFFFFF" id="mask-6">
              <use xlink:href="#path-3" />
            </mask>
            <use fill="#D0C6AC" xlink:href="#path-3" />
            <g fill="#AE5D29" mask="url(#mask-6)">
              <g transform="translate(0.000000, 0.000000)">
                <rect height="280" width="264" x="0" y="0" />
              </g>
            </g>
            <path d="M156,79 L156,102 C156,132.927946 130.927946,158 100,158 C69.072054,158 44,132.927946 44,102 L44,79 L44,94 C44,124.927946 69.072054,150 100,150 C130.927946,150 156,124.927946 156,94 L156,79 Z" fill="#000000" fill-opacity="0.100000001" mask="url(#mask-6)" />
          </g>
          <g transform="translate(0.000000, 170.000000)">
            <defs>
              <path d="M133.960472,0.294916112 C170.936473,3.32499816 200,34.2942856 200,72.0517235 L200,81 L0,81 L0,72.0517235 C1.22536245e-14,33.9525631 29.591985,2.76498122 67.0454063,0.219526408 C67.0152598,0.593114549 67,0.969227185 67,1.34762511 C67,13.2107177 81.9984609,22.8276544 100.5,22.8276544 C119.001539,22.8276544 134,13.2107177 134,1.34762511 C134,0.994669088 133.986723,0.64370138 133.960472,0.294916112 Z" id="path-7" />
            </defs>
            <g transform="translate(32.000000, 29.000000)">
              <mask fill="#FFFFFF" id="mask-8">
                <use xlink:href="#path-7" />
              </mask>
              <use fill="#E6E6E6" xlink:href="#path-7" />
              <g fill="#262E33" mask="url(#mask-8)">
                <g transform="translate(-32.000000, -29.000000)">
                  <rect height="110" width="264" x="0" y="0" />
                </g>
              </g>
              <g fill="#000000" fill-opacity="0.16" mask="url(#mask-8)" opacity="0.599999964">
                <g transform="translate(60.000000, -25.000000)">
                  <ellipse cx="40.5" cy="27.8476251" rx="39.6351047" ry="26.9138272" />
                </g>
              </g>
            </g>
            <g transform="translate(32.000000, 28.000000)">
              <path d="M68.784807,1.12222847 C30.512317,2.80409739 -1.89486556e-14,34.3646437 -1.42108547e-14,73.0517235 L0,73.0517235 L0,82 L69.3616767,82 C65.9607412,69.9199941 64,55.7087296 64,40.5 C64,26.1729736 65.7399891,12.7311115 68.784807,1.12222847 Z M131.638323,82 L200,82 L200,73.0517235 C200,34.7067641 170.024954,3.36285166 132.228719,1.17384225 C135.265163,12.7709464 137,26.1942016 137,40.5 C137,55.7087296 135.039259,69.9199941 131.638323,82 Z" fill="#3A4C5A" />
              <path d="M149,58 L158.555853,50.83311 L158.555853,50.83311 C159.998897,49.7508275 161.987779,49.7682725 163.411616,50.8757011 L170,56 L149,58 Z" fill="#E6E6E6" />
              <path d="M69,1.13686838e-13 C65,19.3333333 66.6666667,46.6666667 74,82 L58,82 L44,46 L50,37 L44,31 L63,1 C65.027659,0.369238637 67.027659,0.0359053037 69,1.13686838e-13 Z" fill="#2F4351" />
              <path d="M151,1.13686838e-13 C147,19.3333333 148.666667,46.6666667 156,82 L140,82 L126,46 L132,37 L126,31 L145,1 C147.027659,0.369238637 149.027659,0.0359053037 151,1.13686838e-13 Z" fill="#2F4351" transform="translate(141.000000, 41.000000) scale(-1, 1) translate(-141.000000, -41.000000)" />
            </g>
          </g>
          <g fill="#000000" transform="translate(76.000000, 82.000000)">
            <g fill="#000000" fill-opacity="0.699999988" transform="translate(2.000000, 52.000000)">
              <path d="M40.0582943,16.6539438 C40.7076459,23.6831146 46.7016363,28.3768187 54,28.3768187 C61.3416045,28.3768187 67.3633339,23.627332 67.9526838,16.5287605 C67.9840218,16.1513016 67.0772329,15.8529531 66.6289111,16.077395 C61.0902255,18.8502083 56.8805885,20.2366149 54,20.2366149 C51.1558456,20.2366149 47.0072148,18.8804569 41.5541074,16.168141 C41.0473376,15.9160792 40.0197139,16.2363147 40.0582943,16.6539438 Z" transform="translate(54.005357, 22.188409) scale(1, -1) translate(-54.005357, -22.188409)" />
            </g>
            <g fill-opacity="0.16" transform="translate(28.000000, 40.000000)">
              <path d="M16,8 C16,12.418278 21.372583,16 28,16 L28,16 C34.627417,16 40,12.418278 40,8" />
            </g>
            <g fill-opacity="0.599999964" transform="translate(0.000000, 8.000000)">
              <path d="M16.1601674,32.4473116 C18.006676,28.648508 22.1644225,26 26.9975803,26 C31.8136766,26 35.9591217,28.629842 37.8153518,32.4071242 C38.3667605,33.5291977 37.5821037,34.4474817 36.790607,33.7670228 C34.3395063,31.6597833 30.8587163,30.3437884 26.9975803,30.3437884 C23.2572061,30.3437884 19.8737584,31.5787519 17.4375392,33.5716412 C16.5467928,34.3002944 15.6201012,33.5583844 16.1601674,32.4473116 Z" transform="translate(27.000000, 30.000000) scale(1, -1) translate(-27.000000, -30.000000)" />
              <path d="M74.1601674,32.4473116 C76.006676,28.648508 80.1644225,26 84.9975803,26 C89.8136766,26 93.9591217,28.629842 95.8153518,32.4071242 C96.3667605,33.5291977 95.5821037,34.4474817 94.790607,33.7670228 C92.3395063,31.6597833 88.8587163,30.3437884 84.9975803,30.3437884 C81.2572061,30.3437884 77.8737584,31.5787519 75.4375392,33.5716412 C74.5467928,34.3002944 73.6201012,33.5583844 74.1601674,32.4473116 Z" transform="translate(85.000000, 30.000000) scale(1, -1) translate(-85.000000, -30.000000)" />
            </g>
            <g fill-opacity="0.599999964" fill-rule="nonzero">
              <path d="M15.6114904,15.1845247 C19.8515017,9.41618792 22.4892046,9.70087612 28.9238518,14.5564693 C29.1057771,14.6937504 29.2212592,14.7812575 29.5936891,15.063789 C34.4216439,18.7263562 36.7081807,20 40,20 C41.1045695,20 42,19.1045695 42,18 C42,16.8954305 41.1045695,16 40,16 C37.9337712,16 36.0986396,14.9777974 32.011227,11.8770179 C31.6358269,11.5922331 31.5189458,11.5036659 31.3332441,11.3635351 C27.5737397,8.52660822 25.3739873,7.28738405 22.6379899,6.99208688 C18.9538127,6.59445233 15.5799484,8.47367246 12.3885096,12.8154753 C11.7343147,13.7054768 11.9254737,14.9572954 12.8154753,15.6114904 C13.7054768,16.2656853 14.9572954,16.0745263 15.6114904,15.1845247 Z" />
              <path d="M73.6114904,15.1845247 C77.8515017,9.41618792 80.4892046,9.70087612 86.9238518,14.5564693 C87.1057771,14.6937504 87.2212592,14.7812575 87.5936891,15.063789 C92.4216439,18.7263562 94.7081807,20 98,20 C99.1045695,20 100,19.1045695 100,18 C100,16.8954305 99.1045695,16 98,16 C95.9337712,16 94.0986396,14.9777974 90.011227,11.8770179 C89.6358269,11.5922331 89.5189458,11.5036659 89.3332441,11.3635351 C85.5737397,8.52660822 83.3739873,7.28738405 80.6379899,6.99208688 C76.9538127,6.59445233 73.5799484,8.47367246 70.3885096,12.8154753 C69.7343147,13.7054768 69.9254737,14.9572954 70.8154753,15.6114904 C71.7054768,16.2656853 72.9572954,16.0745263 73.6114904,15.1845247 Z" transform="translate(84.999934, 13.470064) scale(-1, 1) translate(-84.999934, -13.470064)" />
            </g>
          </g>
          <g fill-rule="evenodd" stroke-width="1">
            <defs>
              <rect height="280" id="path-10" width="264" x="0" y="0" />
              <path d="M90.9102919,55.3613196 L175.085702,55.3613196 C193.333279,44.8338001 196.759397,26.1510357 183.849606,9.92600089 C180.635746,5.88682054 175.085702,21.6755614 158.028596,22.6504878 C140.97149,23.6254143 142.608865,16.3498661 124.45759,19.0739248 C106.306316,21.7979835 108.311575,36.37843 96.4671989,39.8768239 C88.5709482,42.2090865 86.7186458,47.370585 90.9102919,55.3613196 Z" id="path-9" />
              <filter filterUnits="objectBoundingBox" height="108.0%" id="filter-13" width="101.5%" x="-0.8%" y="-2.0%">
                <feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1" />
                <feColorMatrix in="shadowOffsetOuter1" result="shadowMatrixOuter1" type="matrix" values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.16 0" />
                <feMerge>
                  <feMergeNode in="shadowMatrixOuter1" />
                  <feMergeNode in="SourceGraphic" />
                </feMerge>
              </filter>
            </defs>
            <mask fill="#FFFFFF" id="mask-12">
              <use xlink:href="#path-10" />
            </mask>
            <g mask="url(#mask-12)">
              <g transform="translate(-1.000000, 0.000000)">
                <g transform="translate(49.000000, 72.000000)">
                  <defs>
                    <path d="M84.0002865,69.2970648 C77.2083681,65.7112456 67.5782013,65.1489138 62.3885276,67.1316942 C56.6144416,69.3374281 51.5052994,75.5829845 42.6388201,72.8283797 C42.2699314,72.7136458 41.9094725,73.0449523 42.0204089,73.408662 C43.3937943,77.9183313 51.0278347,81.0068878 53.6221945,81.1080652 C64.961124,81.549609 74.0949802,72.8302891 84.0002865,72.1614794 C93.9055921,72.8302891 103.03945,81.549609 114.378714,81.1080652 C116.972736,81.0068878 124.607113,77.9183313 125.980498,73.408662 C126.091098,73.0449523 125.730639,72.7136458 125.36175,72.8283797 C116.495271,75.5829845 111.386129,69.3374281 105.612044,67.1316942 C100.422371,65.1489138 90.7922044,65.7112456 84.0002865,69.2970648 Z" id="path-14" />
                  </defs>
                  <mask fill="#FFFFFF" id="mask-15">
                    <use xlink:href="#path-14" />
                  </mask>
                  <use fill="#28354B" fill-rule="evenodd" xlink:href="#path-14" />
                  <g fill="#B58143" mask="url(#mask-15)">
                    <g transform="translate(-32.000000, 0.000000)">
                      <rect height="244" width="264" x="0" y="0" />
                    </g>
                  </g>
                </g>
                <mask fill="#FFFFFF" id="mask-11">
                  <use xlink:href="#path-9" />
                </mask>
                <use fill="#252E32" fill-rule="evenodd" stroke="none" xlink:href="#path-9" />
                <g fill="#E8E1E1" mask="url(#mask-11)">
                  <g transform="translate(0.000000, 0.000000)">
                    <rect height="280" width="264" x="0" y="0" />
                  </g>
                </g>
              </g>
            </g>
          </g>
        </g>
      </g>
    </g>
  </g>
</svg>

When doing:

header('Content-type: image/png');
imagepng($image->toRasterImage(200, 200));
exit;

This is the result: Screenshot from 2023-03-22 14-59-55

Expected result: Screenshot from 2023-03-22 14-59-45

Hope it helps debugging it. It throws no error :s

meyfa commented 1 year ago

@psociety Your report doesn't seem related to this issue, since this issue is about embedded images (<image> tag), which your SVG doesn't contain. The problem in your case is the extensive use of masks, as well as the <use> tag - both of which aren't implemented in PHP-SVG currently. Issues for that already exist, see #49 and #50. If you'd like to help out, feel free to discuss ideas on the linked issues (not here since it's not related) and/or provide a pull request with an implementation, if you can! Thank you!

redbeardcreator commented 1 year ago

Rasterizing an embedded image appears to fail when the x and y attributes are omitted (see the first attached image) with the TypeError SVG\Rasterization\Transform\Transform::map(): Argument #1 ($x) must be of type float, null given, called in vendor/meyfa/php-svg/src/Rasterization/Renderers/ImageRenderer.php on line 30.

The same SVG with the embedded image with the x and y attributes set (second attached image) appears to rasterize fine. Note that this SVG has the image scaled down slightly, as well. I initially thought that was the problem.

The x and y attributes should both default to 0.

php-svg-189-fails php-svg-189-works

meyfa commented 1 year ago

Thanks for the reproduction case, @redbeardcreator! I made a fix at #206, and released v0.14.4 with it.

I hope this fix solved the original issue as well. If it didn't, feel free to open a new issue.