Closed jermy closed 2 months ago
Should the whole text be included in the bounding box? Should there be quite so much space above/below since the characters don't actually extend into that space? The alignment-baseline will affect the initial position of the text on the canvas, but not the width or height otherwise.
I believe this is intended. I wrote this code, but it's been a while, but I just stumbled upon this comment: https://github.com/RazrFalcon/resvg/blob/5141a830346576edab4c52244185ef95e2a07dc5/crates/usvg/src/text/layout.rs#L395-L400
One of the main reasons that the whole bounding box calculation in usvg exists is to be able to resolve attributes that have the unit objectBoundingBox
, which requires us to know the bounding box of the object a paint is applied to to resolve it. In the case of text, the SVG explicitly mentions that the text bounding box should be derived from the font metrics, and not from the actual shape of the glyphs that are drawn.
As a workaround, you can try using the stroke bounding box
instead, which does give me an exact fit for the text when trying it out:
Yes, it's a bit inconsistent that bounding box
and stroke bounding box
work so differently, but it is what it is.
I guess we should probably add an additional text_bounding_box
property to Text
which contains the bounding box as required by the SVG spec, and then bounding box should just be the actual shape bounding box
Would it be reasonable to add resvg_get_image_stroke_bbox
? (Similarly to how I'd like to add resvg_get_image_transform
for my other open issue)
No idea, I have zero clue about the C++ API. I only worked on the Rust part. But if no API exists, I guess it makes sense to add it.
@RazrFalcon any thoughts on this?
Insert that as the viewbox in the SVG, and also draw a box around that area:
Don't do this. That's not what viewBox
is for. Use width
/height
instead. Or at least set just width/height to viewBox
, aka 0 0 W H
.
Note that the bounding box doesn't include all of the text
Yes, object bbox includes font metrics, not the path bbox. See https://razrfalcon.github.io/notes-on-svg-parsing/text/bbox.html The problem with SVG is that it never works in a way you think it should. It's very unintuitive. Especially when it comes to text.
What you need here is a layer bbox, aka Node::abs_layer_bounding_box
, but I guess we do not expose it in the C API.
And resvg_get_image_bbox
is Node::abs_bounding_box
, which is a very different thing.
Sadly, I haven't wrote a chapter about different types of bounding boxes in SVG in "Notes on SVG parsing" yet. Basically, there are 3 types of bounding boxes: object bbox in object units, object bbox in user/canvas units and "layer" bbox. All 3 are different and SVG uses all 3 depending on the situation.
Try patching resvg_get_image_bbox
to use abs_layer_bounding_box
instead of abs_bounding_box
and see if it solves your issue. If not, than there is probably a bug in text layer bbox caclulation.
And yes, resvg_get_image_size
will always trim/clip your image when you're not providing the SVG size. That's just how SVG works, unfortunately...
SVG size auto-detection is trash and you must not rely on it. It doesn't respect negative positions and always uses object canvas bboxes, which is never what you want. But that's by the spec.
I guess I would have to improve documentation...
abs_layer_bounding_box
is what you're looking for, and yes, it's missing from the C API. And it has nothing to do with viewBox
flattening.
@LaurenzV
Yes, it's a bit inconsistent that bounding box and stroke bounding box work so differently, but it is what it is.
It is inconsistent, but unfortunately we're simply following the spec. It's not a bug. Stroke bbox and layer bbox are not in the spec to begin with. I made them up for my use cases. Only object bbox is in the spec and it doesn't work the way you think.
Try patching resvg_get_image_bbox to use abs_layer_bounding_box instead of abs_bounding_box and see if it solves your issue. If not, than there is probably a bug in text layer bbox caclulation.
That most likely won't work, because abs_layer_bounding_box
is also based on abs_bounding_box
, which has the same issue.
abs_layer_bounding_box
is completely different to abs_bounding_box
, since it prefers stroke bbox to object bbox. And stroke bbox for text is a path bbox, even when we do not have a stroke.
Welcome to SVG...
In this:
tree.0.root().abs_bounding_box().to_non_zero_rect()
)tree.0.root().abs_stroke_bounding_box().to_non_zero_rect()
)tree.0.root().abs_layer_bounding_box()
)Assuming the latter is the one that I should add to the C API, is there a sensible name? The above test is using resvg_get_image_layer_bbox
but I don't think that's clear. Using resvg_get_image_stroke_bbox
would match the API for nodes, and mostly does the same thing, even if it uses the group layer_bounding_box internally?
Insert that as the viewbox in the SVG, and also draw a box around that area:
Don't do this. That's not what
viewBox
is for. Usewidth
/height
instead. Or at least set just width/height toviewBox
, aka0 0 W H
.
But that's precisely what viewBox is for since it needs to handle negative numbers. I'm not interested in the first image in this thread!
I'm interested in creating an image containing text then rendering it with a tight crop around the text and there are two ways to do it:
The former is straightforward, the latter might be impossible (or at least more complicated) if the x/y values involve percentages, and involves parsing the whole XML document.
Yes, object bbox includes font metrics, not the path bbox. See https://razrfalcon.github.io/notes-on-svg-parsing/text/bbox.html The problem with SVG is that it never works in a way you think it should. It's very unintuitive. Especially when it comes to text.
Thanks for that - I'm guessing the cropped 'd' in these images is due to the bounding box being based on the advancement?
Sadly, I haven't wrote a chapter about different types of bounding boxes in SVG in "Notes on SVG parsing" yet. Basically, there are 3 types of bounding boxes: object bbox in object units, object bbox in user/canvas units and "layer" bbox. All 3 are different and SVG uses all 3 depending on the situation.
This relates more to #822 where I claim this is precisely what I mean about different coordinate systems - the difference between and object units and canvas units, that different functions return different types of units, and the fact that since the "viewbox flattening" changes we no longer have a way in the C API to translate these. I can obviously just expose abs_transform()
for the root element in a local fork, but would prefer to not divert too far from what you're doing.
resvg_get_image_bbox
is the right name. We just call the wrong method inside it. Right now it calls abs_bounding_box
, which is almost never what you want.
As for stroke vs layer, they are different and you have to use "layer". Unlike stroke bbox, layer bbox includes filters region as well. Stroke bbox for text is undefined to begin with, afaik. It's just a temporary value. As a user/caller you should care only about layer bbox. I might even hide object and stroke bboxes in the future to reduce confusion.
But that's precisely what viewBox is for since it needs to handle negative numbers.
viewBox
is just a transform. And usvg
flattens it for you. Think of it as a syntax sugar.
If you want to shift an image, either pass the right transform to the rendering API or add a root group with your transform (which is what viewBox
is doing automatically).
Thanks for that - I'm guessing the cropped 'd' in these images is due to the bounding box being based on the advancement?
Yes, glyph bbox based on TrueType metrics and glyph outline bbox do not guarantee to match. And the SVG spec requires us to use the first one during SVG processing.
the difference between and object units and canvas units, that different functions return different types of units
Sort of. But C API always uses absolute/canvas coordinates. Object bbox units are for internal use only and eventually would be removed from Rust API as well. So no, you don't have to worry about it and I have no idea how it could have affected your code.
The only problem with C API is that it returns an image bbox without stroke+filters+text-bbox. That's it.
As noted #822, I've got a workflow which involves using resvg to generate SVGs containing just text, and having issues with the calculated bounding box. I suspect this might be related to the font in question.
The workflow is as follows:
This renders as:
resvg_get_image_bbox
. In this case, I getx=0 y=-13.029999732971191 width=113.95999145507812 height=35.119998931884766
Which renders as:
I'm using (eg)
./resvg --background grey --zoom 4 --use-font-file fonts/Pacifico.ttf /tmp/pacifico-hello-world-smaller.svg /tmp/pacifico-hello-world-smaller.png
to generate all of these, with the resvg version taggedv0.43.0
, although this is the same behaviour as 0.41.Should the whole text be included in the bounding box? Should there be quite so much space above/below since the characters don't actually extend into that space? The alignment-baseline will affect the initial position of the text on the canvas, but not the width or height otherwise.
Here's another SVG with the same process to show the size of the ascenders/descenders: