software-mansion / react-native-svg

SVG library for React Native, React Native Web, and plain React web projects.
MIT License
7.43k stars 1.12k forks source link

LinearGradient crash with xlink:href #919

Open gosuperninja opened 5 years ago

gosuperninja commented 5 years ago

xlink:href are not supported, the use of it causes a crash. linearGradient that use xlink:href have no children elements which causes a call on undefined.

Example

<svg id="b809eb86-ec6b-4213-aa3e-83f4128e49a5" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink" width="1082" height="725" viewBox="0 0 1082 725">
    <defs>
        <linearGradient id="b7709055-0dd4-48d6-ac10-5a758f15d1a1" x1="443.56" y1="792.13" x2="443.56" y2="251.5"
                        gradientUnits="userSpaceOnUse">
            <stop offset="0" stop-color="gray" stop-opacity="0.25"/>
            <stop offset="0.54" stop-color="gray" stop-opacity="0.12"/>
            <stop offset="1" stop-color="gray" stop-opacity="0.1"/>
        </linearGradient>
        <linearGradient id="f59ba046-7ebe-407e-be3d-c62856fc1078" x1="712" y1="788.54" x2="712" y2="248.5"
                        xlink:href="#b7709055-0dd4-48d6-ac10-5a758f15d1a1">
        </linearGradient>
    </defs>
</svg>
msand commented 5 years ago

That doesn't render anything in any browser or other svg viewer either. You need to refer to the gradient in a fill or stroke attribute using e.g. fill="url(#idOfGradient)" in some other element which causes some geometry to render. Check e.g. https://github.com/react-native-community/react-native-svg#lineargradient

<Svg
    height="150"
    width="300"
>
    <Defs>
        <LinearGradient id="grad" x1="0" y1="0" x2="170" y2="0">
            <Stop offset="0" stopColor="rgb(255,255,0)" stopOpacity="0" />
            <Stop offset="1" stopColor="red" stopOpacity="1" />
        </LinearGradient>
    </Defs>
    <Ellipse cx="150" cy="75" rx="85" ry="55" fill="url(#grad)" />
</Svg>
msand commented 5 years ago

Hmm, apparently the spec allow inheritance here: https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementHrefAttribute https://svgwg.org/svg2-draft/pservers.html#LinearGradientElementHrefAttribute

msand commented 5 years ago

https://www.w3.org/TR/SVG/pservers.html#PaintServerTemplates https://svgwg.org/svg2-draft/pservers.html#PaintServerTemplates

MillerGregor commented 5 years ago

in case anyone else runs into this..

IF you have access to the source AND you're using Illustrator

save as settings: SVG 1.1 type: SVG subsetting: none Image location: (whatever) css properties: Presentation Attributes

I then process via gulp

gulpfile.js:

var gulp = require('gulp');
var svgmin = require('gulp-svgmin');
var replace = require('gulp-replace');

gulp.task('svg', function() {
  return gulp
    .src('./svg/in/*.svg')
    .pipe(
      svgmin({
        plugins: [
          { removeXMLNS: true },
          {
            inlineStyles: { onlyMatchedOnce: false, removeMatchedSelectors: false }
          },
          { convertStyleToAttrs: true }
        ]
      })
    )
    .pipe(
      svgmin({
        plugins: [{ removeStyleElement: true }, { removeEmptyContainers: true }]
      })
    )
    .pipe(
      svgmin({
        plugins: [
          { removeEmptyContainers: true },
          {
            removeAttrs: {
              attrs: '(class|data-name)'
            }
          },
          {
            convertShapeToPath: {
              convertArcs: true
            }
          }
        ]
      })
    )
    .pipe(
      svgmin({
        plugins: [
          {
            mergePaths: {
              collapseRepeated: true,
              leadingZero: true,
              negativeExtraSpace: true
            }
          }
        ]
      })
    )
    .pipe(
      replace(/xlink:href=/g, (match, p1, offset, string) => {
        return `href=`;
      })
    )
    .pipe(
      replace(/stop-color=/g, (match, p1, offset, string) => {
        return `stopColor=`;
      })
    )
    .pipe(
      replace(/clip-path=/g, (match, p1, offset, string) => {
        return `clipPath=`;
      })
    )
    .pipe(
      replace(/stroke-linecap=/g, (match, p1, offset, string) => {
        return `strokeLinecap=`;
      })
    )
    .pipe(
      replace(/stroke-linejoin=/g, (match, p1, offset, string) => {
        return `strokeLinejoin=`;
      })
    )
    .pipe(
      replace(/stroke-width=/g, (match, p1, offset, string) => {
        return `strokeWidth=`;
      })
    )
    .pipe(
      replace(/stroke-miterlimit=/g, (match, p1, offset, string) => {
        return `strokeMiterlimit=`;
      })
    )
    .pipe(
      replace(/="(-?[0-9]*\.[0-9]*)"/g, (match, p1, offset, string) => {
        return `={${p1}}`;
      })
    )
    .pipe(
      replace(/<(\w)/g, (match, p1, offset, string) => {
        return '<Svg.' + p1.toUpperCase();
      })
    )
    .pipe(
      replace(/<\/(\w)/g, (match, p1, offset, string) => {
        return '</Svg.' + p1.toUpperCase();
      })
    )
    .pipe(gulp.dest('./svg/out'));
});
stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. You may also mark this issue as a "discussion" and I will leave this open.

stale[bot] commented 4 years ago

Closing this issue after a prolonged period of inactivity. Fell free to reopen this issue, if this still affecting you.