resvg icon indicating copy to clipboard operation
resvg copied to clipboard

Failed to rendering a text in nested SVG

Open yanorei32 opened this issue 10 months ago • 7 comments

I wrote are two SVGs and I nested those and I got a blank image unexpectedly.

Is this a bug...? (Sorry. I can't classify that problem as bug, unimplemented feature, undefined behaviour or something else, because I'm not an expert of SVG specification.)

Version: 0.41.0 OS: Windows 11 23H2 22631.3447 (x86_64) and Arch Linux.

I got a same results in --use-fonts-dir option.

Text containing SVG (child.svg)

Works expectedly if this file only. This file just needs to contain a text element. But I wrote some elements and attributes for visiblity.

Note: I choose a Noto Sans but this problem is occured too in any system fonts.

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" width="100px" height="100px">
	<rect x="25" y="25" width="25" height="25" fill="red" />
	<text
		x="50" y="50" dominant-baseline="middle" text-anchor="middle"
		font-size="25" font-family="Noto Sans"
		fill="black"
	>
		Hello
	</text>
</svg>

Rendered Image (child.svg):

resvg child.svg child.png

child

Parent SVG (parent.svg)

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" width="100px" height="100px">
	<image x="0" y="0" width="100" height="100" href="child.svg" />
</svg>

Rendered Image (parent.svg):

resvg parent.svg parent.png

Actually behaviour (parent.svg)

parent

and I got warning:

Warning (in usvg::text::layout:1115): No match for '"Noto Sans"' font-family.

Expected behaviour (parent.svg)

Completly same as child.svg in this case.

yanorei32 avatar Apr 15 '24 18:04 yanorei32

Yes, we're loading fonts only when the main SVG has text nodes. We do not check referenced SVGs. Will fix.

For now, simply add a single empty text element to the main SVG and it will fix the issue.

RazrFalcon avatar Apr 15 '24 18:04 RazrFalcon

Amazing! Thank you for very fast reply! I confirmed that solution is works and that completely avoid this problem. I hope this problem will be fixed in future.

yanorei32 avatar Apr 15 '24 18:04 yanorei32

Yes, we're loading fonts only when the main SVG has text nodes. We do not check referenced SVGs.

Didn't I fix this in #675? Apparently I didn't...

LaurenzV avatar Apr 15 '24 19:04 LaurenzV

Oh... It's because resvg has it's own logic for checking if there are text nodes... So it works in tests but not in the CLI:

https://github.com/RazrFalcon/resvg/blob/ec1627fb73ecf4f3d91f5b2283cf8b89ec6c83c3/crates/resvg/src/main.rs#L84-L86

That's gonna be tricky to change though if we want to do it inside the resvg CLI...

Maybe it's best to just always initialize the fontdb?

LaurenzV avatar Apr 15 '24 19:04 LaurenzV

Maybe it's best to just always initialize the fontdb?

That's pretty slow. Even on a macbook with a hot cache it's like 10-20ms. It could easily take 100-500ms on an average hardware.

The issue here is that before, text flattening was done after parsing, not during. So before we were checking for usvg::Text nodes, not text XML elements.

This is partially related to #710 , as we have to add a callback to text flattening. I haven't thought about it much yet.

The only "solution" so far is to have some public, intermediate tree representation. One between usvg::parser::svgtree and usvg::Tree. This way we wouldn't have to do everything in one go, which has it's own limitations (paint servers resolving) and complicates code quite a bit (text flattening). But it would have a small performance hit. Basically, instead of modifying usvg::Tree and passing it to resvg, we would allow modifying of this new tree, which then be converted into usvg::Tree. And we already do roxmltree -> svgtree -> usvg::Tree ...

RazrFalcon avatar Apr 16 '24 09:04 RazrFalcon

That will be a lot of trees then. :p But I've also had thoughts about whether there should be some additional intermediate representation...

LaurenzV avatar Apr 16 '24 09:04 LaurenzV

While intermediate representations do reduce performance a bit, they do simplify the code and logic quite a lot. The hard part is to find the right boundaries for each representation.

My goal is to keep usvg::Tree read-only with all of the SVG complexity removed and all metadata calculated (bboxes, etc). For the caller, SVG should look like a simple list of layers. And as you already know, it's ridiculously hard to do.

RazrFalcon avatar Apr 16 '24 10:04 RazrFalcon