Search code examples
iossvgsafari

SVG textPath with linear gradient is damaged in Safari


The textPath is damaged when I fill linear gradient. How to fix it? Thanks.

[[the broken image]]: https://i.sstatic.net/200t3.png

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" width="1000" height="1000">
  <g font-size="42.5" font-weight="800">
<path d="M 400, 400
      m -100, 0
      a 100, 100 0 1,0 200, 0
      a 100, 100 0 1,0 -200, 0" fill="transparent" stroke="black" id="SvgjsPath1000"></path>
<circle r="4" cx="400" cy="400" fill="#000000"></circle>
<text stroke-width="10" svgjs:data="{&quot;leading&quot;:&quot;1.3&quot;}">
  <textPath xlink:href="#SvgjsPath1000" fill="transparent" stroke="#ffffff" svgjs:data="{&quot;leading&quot;:&quot;1.3&quot;}">
    <tspan alignment-baseline="middle">RRRRRRRRRRRRRRRRRRRRR</tspan>
  </textPath>
</text>
<text svgjs:data="{&quot;leading&quot;:&quot;1.3&quot;}">
  <textPath xlink:href="#SvgjsPath1000" fill="url(&quot;#SvgjsLinearGradient1001&quot;)" svgjs:data="{&quot;leading&quot;:&quot;1.3&quot;}">
    <tspan alignment-baseline="middle">RRRRRRRRRRRRRRRRRRRRR</tspan>
  </textPath>
</text>
  </g>
  <defs>
<linearGradient x1="0%" y1="0%" x2="100%" y2="0%" id="SvgjsLinearGradient1001">
  <stop offset="0" style="stop-color:#007cf0"></stop>
  <stop offset="1" style="stop-color:#ff0080"></stop>
</linearGradient>
  </defs>
</svg>


Solution

  • Apparently, a safari specific rendering bug when applying gradients to <textPath> elements.

    I could also reproduce this issue with a simplified example (testing in iOS safari):

    <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <linearGradient id="myGradient" gradientTransform="rotate(90)">
          <stop offset="5%" stop-color="gold" />
          <stop offset="95%" stop-color="red" />
        </linearGradient>  
      </defs>
      <path
        id="MyPath"
        fill="none"
        stroke="red"
        d="M10,90 Q90,90 90,45 Q90,10 50,10 Q10,10 10,40 Q10,70 45,70 Q70,70 75,50" />
      <text>
        <textPath fill="url('#myGradient')" href="#MyPath">Quick brown fox jumps over the lazy dog.</textPath>
      </text>
    </svg>

    Workaround: create a <mask> containing the text path:

    body {
      background: #ccc
    }
    
    svg {
      width: 50%;
      border: 1px solid #000
    }
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" viewBox="250 250 300 300">
      <defs>
        <linearGradient x1="0%" y1="0%" x2="100%" y2="0%" id="SvgjsLinearGradient1001">
          <stop offset="0" style="stop-color:#007cf0"></stop>
          <stop offset="1" style="stop-color:#ff0080"></stop>
        </linearGradient>
        <mask id="mask" x="0" y="0" width="200%" height="200%">
          <rect fill="#000" x="0" y="0" width="200%" height="200%" />
          <text>
            <textPath class="textPath" font-size="42.5" font-weight="800" href="#SvgjsPath1000" dominant-baseline="middle" fill="#fff">
              RRRRRRRRRRRRRRRRRRRRR
            </textPath>
          </text>
        </mask>
      </defs>
      <g>
        <path d="M 300 400 a 100 100 0 1 0 200 0 a 100 100 0 1 0 -200 0 z" fill="transparent" stroke="black" id="SvgjsPath1000" />
        <circle r="4" cx="400" cy="400" fill="#000000" />
        <text>
          <textPath class="textPath" font-size="42.5" font-weight="800" href="#SvgjsPath1000" dominant-baseline="middle" fill="transparent" stroke="#fff" stroke-width="10">
            RRRRRRRRRRRRRRRRRRRRR
          </textPath>
        </text>
        <rect mask="url(#mask)" fill="url(#SvgjsLinearGradient1001)" x="0" y="0" width="200%" height="200%" />
      </g>
    </svg>