flutter_svg icon indicating copy to clipboard operation
flutter_svg copied to clipboard

Out of order defs/references

Open dnfield opened this issue 6 years ago • 29 comments

Right now, an error is reported if you try to use a file that does something like the following:

<svg>
  <path fill="url(#MyGradient)" d="..." />
  <linearGradient id="MyGradient" ...>
    ...
  </linearGradient>
</svg>

This is done to make sure we don't have to maintain a full DOM or traverse the XML more than once.

However, it should be possible to keep an in memory structure of defs (probably a Map of some sort) we expect to see but haven't seen yet, and go back and back-fill them (and then remove them from the structure after we found the def they're looking for) with only a little bit of memory overhead.

See also #68 and #97

dnfield avatar Jan 14 '19 23:01 dnfield

Would really appreciate if this would be implemented. We are having way to many/dynamic SVGs to change them all :)

IchordeDionysos avatar Aug 28 '19 20:08 IchordeDionysos

Would be useful. Affinity Designer produces SVGs with defs at the bottom, for example.

NotThatBowser avatar Sep 02 '19 13:09 NotThatBowser

I thought I had a way to do this, but all the ways I've been able to come up with are pretty inefficient.

If we defer resolution until drawing time (e.g. a lot of things that are now static properties become functions/getters that may not work during the parse phase), we can avoid reparsing the document but may get into strange situations where we have lots of indirection going on via functions. For example, getting a Drawable's fill might actually be a function that calls a function that calls a function that returns the data by calling a few other functions to resolve its properties.

If, on the other hand, we always parse defs in any order, we have to go to a DOM based model of parsing and do a lot of jumping around the document/tree, which will be very inefficient on particularly large SVGs.

This should be something that could be preprocessed away and avoid these complexities/inefficiencies.

dnfield avatar Oct 21 '19 18:10 dnfield

I wonder if you there's a way to add build time pre-processing to avoid inefficiencies.

Something like how built_value.dart (https://github.com/google/built_value.dart) generates Dart files, could you have flutter_svg generating a compressed SVG that matches the format required by the package?

hash404 avatar Oct 22 '19 00:10 hash404

That's definitely doable, and something I've considered exploring but haven't had time to prioritize - or evidence that it would really bump performance enough to be worth it. And unfortunately it wouldn't help with network based images

I suppose a middle road might be having something like that and then allowing for a less efficient parsing algorithm - which would make precompiled images a bit faster but dynamic images even slower.

dnfield avatar Oct 22 '19 00:10 dnfield

I thought I had updated this again, but I guess not.

Supporting this introduces a lot of complexity. In particular, references can have other references, which can have other references, etc.

To fully support this would require some substantial reworking - the simplest cases of only one level of nesting aren't too bad, but when those nested references have other nested references, it becomes a big ugly list of closures everywhere to resolve things, and will be much less efficient both time and space wise, not to mention getitng a lot harder to reason about.

I'm really hesitant to support this. But I also understand it's frustrating when you have an SVG you don't control that just comes this way.

dnfield avatar Jan 28 '20 08:01 dnfield

Why not just provisionally add support for single level nesting? Once done we can get a better idea if we need multi-level nesting support. Maybe multi-level nesting is very rare compared to single-level nesting.

ngaurav avatar Jan 29 '20 07:01 ngaurav

On second thought, If a file only has single-level nesting then it can programatically preprocessed to rearrange out-of-order declarations.

ngaurav avatar Jan 29 '20 12:01 ngaurav

And if you can't? @ngaurav Stored on another server/too many of them.

IchordeDionysos avatar Jan 29 '20 17:01 IchordeDionysos

@IchordeDionysos - you could preprocess the SVG from the server before you pass it to this library.

In fact, maybe that's the right way to handle this - write a preprocessing package to rearrange defs. That way people who want to pay the performance and complexity costs (or have no choice) can, without forcing them on people with more sane SVGs.

dnfield avatar Jan 29 '20 17:01 dnfield

What's the status on this? I have my defs up top right after <svg>, but am still getting this error.

willhaslett avatar May 19 '21 16:05 willhaslett

FYI, figma exports definitions after so SVGs are not rendered. Figma has a larger user base than affinity and i think we'll hear more complains on this.

cihadturhan avatar Jun 05 '21 17:06 cihadturhan

is there a solution please? :)

agueroveraalvaro avatar Jun 07 '21 21:06 agueroveraalvaro

i facing with this issue and figma design tool :( anyone can help me?

cesc1802 avatar Jun 22 '21 10:06 cesc1802

@willhaslett did you find a solution?

hpbl avatar Jul 06 '21 12:07 hpbl

The workaround for me was to move the <defs> up, as the first children in the <svg> element. E.g.:

Before:

<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" viewBox="0 0 20 20">
    <path fill="url(#paint0_linear)" d="M10 20c5.523 0 10-4.477 10-10S15.523 0 10 0 0 4.477 0 10s4.477 10 10 10z"/>
    <path fill="#fff" fill-rule="evenodd" d="M4.527 9.894c2.915-1.27 4.859-2.107 5.832-2.512 2.777-1.155 3.354-1.356 3.73-1.362.083-.002.268.019.387.116.102.082.13.193.143.27.013.079.03.256.016.394-.15 1.582-.801 5.419-1.133 7.19-.14.75-.416 1-.683 1.025-.58.053-1.022-.384-1.584-.752-.88-.577-1.377-.937-2.232-1.5-.987-.65-.347-1.008.216-1.592.147-.153 2.706-2.48 2.755-2.691.006-.027.012-.125-.046-.177-.059-.052-.145-.034-.208-.02-.088.02-1.494.949-4.218 2.788-.399.274-.76.407-1.084.4-.357-.008-1.044-.202-1.554-.368-.627-.203-1.124-.31-1.081-.656.022-.18.27-.365.744-.553z" clip-rule="evenodd"/>
    <defs>
        <linearGradient id="paint0_linear" x1="10" x2="10" y1="0" y2="19.852" gradientUnits="userSpaceOnUse">
            <stop stop-color="#2AABEE"/>
            <stop offset="1" stop-color="#229ED9"/>
        </linearGradient>
    </defs>
</svg>

After:

<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" viewBox="0 0 20 20">
    <defs>
        <linearGradient id="paint0_linear" x1="10" x2="10" y1="0" y2="19.852" gradientUnits="userSpaceOnUse">
            <stop stop-color="#2AABEE"/>
            <stop offset="1" stop-color="#229ED9"/>
        </linearGradient>
    </defs>
    <path fill="url(#paint0_linear)" d="M10 20c5.523 0 10-4.477 10-10S15.523 0 10 0 0 4.477 0 10s4.477 10 10 10z"/>
    <path fill="#fff" fill-rule="evenodd" d="M4.527 9.894c2.915-1.27 4.859-2.107 5.832-2.512 2.777-1.155 3.354-1.356 3.73-1.362.083-.002.268.019.387.116.102.082.13.193.143.27.013.079.03.256.016.394-.15 1.582-.801 5.419-1.133 7.19-.14.75-.416 1-.683 1.025-.58.053-1.022-.384-1.584-.752-.88-.577-1.377-.937-2.232-1.5-.987-.65-.347-1.008.216-1.592.147-.153 2.706-2.48 2.755-2.691.006-.027.012-.125-.046-.177-.059-.052-.145-.034-.208-.02-.088.02-1.494.949-4.218 2.788-.399.274-.76.407-1.084.4-.357-.008-1.044-.202-1.554-.368-.627-.203-1.124-.31-1.081-.656.022-.18.27-.365.744-.553z" clip-rule="evenodd"/>
</svg>

OlegNovosad avatar Jul 25 '21 13:07 OlegNovosad

Any solution? Still not working. tmdb.svg

ebwood avatar Aug 30 '21 20:08 ebwood

I make a quick program for reordering <defs> to the first child, hope It help someone. By the way, I still can't use the svg from designer exported from figma, even If I reorder the svgs.

use flutter_svg_opt as dev dependency, and run flutter pub run flutter_svg_opt:main (will change all svgs under assets by default).

Tokenyet avatar Oct 29 '21 12:10 Tokenyet

The workaround for me was to move the <defs> up, as the first children in the <svg> element. E.g.:

Before:

<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" viewBox="0 0 20 20">
    <path fill="url(#paint0_linear)" d="M10 20c5.523 0 10-4.477 10-10S15.523 0 10 0 0 4.477 0 10s4.477 10 10 10z"/>
    <path fill="#fff" fill-rule="evenodd" d="M4.527 9.894c2.915-1.27 4.859-2.107 5.832-2.512 2.777-1.155 3.354-1.356 3.73-1.362.083-.002.268.019.387.116.102.082.13.193.143.27.013.079.03.256.016.394-.15 1.582-.801 5.419-1.133 7.19-.14.75-.416 1-.683 1.025-.58.053-1.022-.384-1.584-.752-.88-.577-1.377-.937-2.232-1.5-.987-.65-.347-1.008.216-1.592.147-.153 2.706-2.48 2.755-2.691.006-.027.012-.125-.046-.177-.059-.052-.145-.034-.208-.02-.088.02-1.494.949-4.218 2.788-.399.274-.76.407-1.084.4-.357-.008-1.044-.202-1.554-.368-.627-.203-1.124-.31-1.081-.656.022-.18.27-.365.744-.553z" clip-rule="evenodd"/>
    <defs>
        <linearGradient id="paint0_linear" x1="10" x2="10" y1="0" y2="19.852" gradientUnits="userSpaceOnUse">
            <stop stop-color="#2AABEE"/>
            <stop offset="1" stop-color="#229ED9"/>
        </linearGradient>
    </defs>
</svg>

After:

<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" viewBox="0 0 20 20">
    <defs>
        <linearGradient id="paint0_linear" x1="10" x2="10" y1="0" y2="19.852" gradientUnits="userSpaceOnUse">
            <stop stop-color="#2AABEE"/>
            <stop offset="1" stop-color="#229ED9"/>
        </linearGradient>
    </defs>
    <path fill="url(#paint0_linear)" d="M10 20c5.523 0 10-4.477 10-10S15.523 0 10 0 0 4.477 0 10s4.477 10 10 10z"/>
    <path fill="#fff" fill-rule="evenodd" d="M4.527 9.894c2.915-1.27 4.859-2.107 5.832-2.512 2.777-1.155 3.354-1.356 3.73-1.362.083-.002.268.019.387.116.102.082.13.193.143.27.013.079.03.256.016.394-.15 1.582-.801 5.419-1.133 7.19-.14.75-.416 1-.683 1.025-.58.053-1.022-.384-1.584-.752-.88-.577-1.377-.937-2.232-1.5-.987-.65-.347-1.008.216-1.592.147-.153 2.706-2.48 2.755-2.691.006-.027.012-.125-.046-.177-.059-.052-.145-.034-.208-.02-.088.02-1.494.949-4.218 2.788-.399.274-.76.407-1.084.4-.357-.008-1.044-.202-1.554-.368-.627-.203-1.124-.31-1.081-.656.022-.18.27-.365.744-.553z" clip-rule="evenodd"/>
</svg>

Yaap, this actually works...

Vedsaga avatar Dec 25 '21 12:12 Vedsaga

Is this error solved? The rearrangement of tags didn't solve the issue

LearningLeopard avatar Jan 23 '22 10:01 LearningLeopard

Is this error solved? The rearrangement of tags didn't solve the issue

Ohh for me it did solved... If you put your <path > tag at the bottom

Vedsaga avatar Jan 23 '22 12:01 Vedsaga

My svg doesn't have a <path> tag but it has <rect> tag

LearningLeopard avatar Jan 23 '22 13:01 LearningLeopard

My svg doesn't have a <path> tag but it has <rect> tag

Ohh I haven't worked with <rect> 😅 my be try finding svg which have path in that... Or if possible you can share one of your svg code here I will try see if I can convert it into path like format

Vedsaga avatar Jan 23 '22 13:01 Vedsaga

headphone_on_book

Here is the svg file. You can download it and open it in a code editor since pasting the code of svg seems pretty clumsy here

LearningLeopard avatar Jan 23 '22 14:01 LearningLeopard

headphone_on_book

this does seems to be svg... this seems to be png image I am not sure if this can be scan be supported.. so, I would suggest to simply use png instead of converting png to svg

Vedsaga avatar Jan 23 '22 14:01 Vedsaga

any updates on this issue ?

sahharYoucef avatar May 31 '22 16:05 sahharYoucef

I'm working on a new package that supports out of order defs. However, as I expected, doing so resulted in some noticable performance regressions. I'm not sure it'll make sense to ever support in this package, which is already slow enough atparsing the SVGs.

dnfield avatar May 31 '22 17:05 dnfield

Thank you for your work. From my point of view it would be great if the library was able to render more SVGs, support for the really expensive features could be optional.

To work around this, we preprocess user-supplied SVGs where we move the defs to the beginning and we do a topological sort of the defs which solves the problem when they reference each other but were defined in the wrong order.

matejsnoha avatar May 31 '22 18:05 matejsnoha

I make a quick program for reordering <defs> to the first child, hope It help someone. By the way, I still can't use the svg from designer exported from figma, even If I reorder the svgs.

use flutter_svg_opt as dev dependency, and run flutter pub run flutter_svg_opt:main (will change all svgs under assets by default).

@Tokenyet would you please consider updating your package to use xml v6 ? Thanks.

MagnusJohansson avatar Sep 21 '22 11:09 MagnusJohansson

@all

  flutter_svg:
    git:
      url: https://github.com/binSaed/flutter_svg.git
      ref: fix_defs_order

binSaed avatar Dec 15 '22 01:12 binSaed