memononen / nanosvg

Simple stupid SVG parser
zlib License
1.69k stars 357 forks source link

Support <defs> after elements that reference them #202

Closed keiwando closed 1 year ago

keiwando commented 2 years ago

Currently the parser expects the <defs> to be placed before any element in the SVG that references them using url(…). Whenever it tries to resolve a url for a gradient that hasn't been parsed yet, the nsvg__createGradient call just returns NULL and the fillType of the shape is set to NSVG_PAINT_NONE.

Some applications (e.g. Figma) apparently write SVGs that contain the <defs> section at the end. I took a quick look at the SVG spec but couldn't find any definitive requirement that <defs> have to preceed elements that reference them. It just makes certain recommendations to allow for more efficient parsing such as:

To provide some SVG user agents with an opportunity to implement efficient implementations in streaming environments, creators of SVG content are encouraged to place all elements which are targets of local IRI references within a ‘defs’ element which is a direct child of one of the ancestors of the referencing element.

I've attached two SVGs here with which the issue can be reproduced. The first one ("svg_gradients.svg") is the Figma export with the <defs> section after the shape definitions that use the gradients. In the second one ("svg_gradients_defs_at_the_top.svg") I've manually moved the <defs> to the beginning and this causes the SVG to be parsed correctly.

svg_gradients.svg svg_gradients

svg_gradients_defs_at_the_top.svg svg_gradients_defs_at_the_top

This is probably going to require an extra pass over all shapes at the end in order to resolve potentially unresolved gradients. We will make sure to open a PR if we end up fixing this ourselves. Either way, thanks for the library!

mgrudzinska commented 2 years ago

@keiwando since the nanoSVG project doesn't seem to be developed very vigorously, let me suggest you to consider another library: ThorVG best :)