Hi Alex,
On Tue, Apr 2, 2019, at 1:47 AM, Alex Henrie wrote:
> Hi, I am trying to fix Bug 1529182,[1] but debugging the SVG <text>
> code is particularly difficult for me because it uses at least six
> coordinate spaces, and I can't figure out how they relate to each
> other:
>
> - Run space
> - Run user space
> - Frame user space
> - (Regular?) user space
> - GFX space
> - App space
>
> Can anyone here explain them to me?
Using this as an example:
<!DOCTYPE html>
<svg width="400" height="300">
<text x="100 200" y="100" style="font: 20px monospace">ab<tspan
style="font-weight: bold">cd</tspan></text>
</svg>
the frame tree dump might look like this (with some unnecessary information
elided):
SVGOuterSVG(svg)(0) {0,0,24000,18000}
SVGOuterSVGAnonChild(svg)(0) {0,0,0,0}
SVGText(text)(1) {5985,4875,8190,1410}
Block(text)(1) {0,0,2880,1440}
line 7f2768ba20a8: count=2 {0,0,2880,1440}
Text(0)"ab" {0,30,1440,1380}
Inline(tspan)(1) {1440,30,1440,1380}
Text(0)"cd" {0,0,1440,1380}
The curly braces are the mRect values for each frame, which are in app units,
which are 1/60 of a CSS pixel.
SVGTextFrame works by leveraging the existing support for CSS rendering of
blocks and inlines by building an nsBlockFrame for the <text> contents,
nsInlineFrame for any <tspan>, and nsTextFrame for text nodes. This lets us
avoid re-implementing text layout in the SVG code. Anything SVG specific, like
text positioning attributes or <textPath>, are handled as a post-processing
step when determining where to render each bit of the resulting CSS-positioned
nsTextFrames.
Generally these mRects are relative to their parent, but the nsBlockFrame child
of the SVGTextFrame is used just to lay out the text and is not rendered
directly, so its (0, 0) position doesn't mean much. Its width and height (2880
x 1440 app units) are the result of reflowing it without any constraints on its
size. The layout of the block frame and its descendants is done independent of
any SVG text positioning attributes and <textPath>.
Because we leverage standard HTML/CSS rendering for SVG text, and because SVG
text positioning attributes and <textPath>s can affect the positions (and
rotations) of individual glyphs, we produce TextRenderedRun objects to
represent each contiguous sequence of characters within an nsTextFrame that we
can ask the text frame to render in one go. In this example, we end up
producing three TextRenderedRuns:
* One for "a", at SVG text position (100, 100). This covers {0,0,720,1380} of
the first nsTextFrame.
* One for "b", at SVG text position (200, 200). This covers {720,0,720,1380}
of the first nsTextFrame.
* One for "cd", at SVG text position (200 + advance_in_user_units("b"), 200).
This corresponds to the entire {0,0,1440,1380} of the second nsTextFrame.
Those rectangles might be in what you call "run space". I don't think they're
really named in the code.
In SVGTextFrame::PaintSVG, as we iterate over each TextRenderedRun, we need to
set a transform on the gfxContext so that when asking an nsTextFrame to render
some of its characters (the substring corresponding to the TextRenderedRun), it
will appear in the right place. Most of the complication in SVGTextFrame.cpp
is doing this.
As for the coordinate systems you mention:
User space is the local SVG coordinate system for an SVG element. In this
example, the user space of the <text> element is the coordinate system the
<svg> establishes, with (0, 0) at the top level of the <svg> element on the
page, and (400, 300) at the bottom right.
"Run user space" is a term I made up to mean a coordinate space that is the
same as the <text>'s user space except translated so that the origin is at the
top left of the rectangle that covers a given TextRenderedRun, and rotated by
the TextRenderedRun's rotation. This example doesn't have any rotate=""
attributes or <textPath>s, so there's no rotation here. So in this coordinate
system, (0, 0) is at the top left of the "b" glyph cell, and (12, 23) is at the
bottom right of the "b" glyph cell. TextRenderedRun::GetRunUserSpaceRect
returns {0,0,12,23}, and TextRenderedRun::GetUserSpaceRect returns something
like {200,86,12,23}, depending on the ascent/descent of the "b" (since the SVG
text positioning aligns its alphabetic baseline at y=100).
"Frame user space" is something that doesn't really correspond to what you see
on the page. It's similar to "run user space": it's a coordinate system of the
same scale as the <text>'s user space, but with its origin placed at the top
left corner of an nsTextFrame's rectangle. For "b",
TextRenderedRun::GetFrameUserSpaceRect returns {12,0,12,23} -- it's the same as
GetRunUserSpaceRect but translated to where in its corresponding nsTextFrame is.
I'm not sure about "GFX space" and "app space" as terms. There are many
different units the coordinate values could be in -- see layout/base/Units.h.
In SVG PaintSVG methods (e.g. SVGGeometryFrame::PaintSVG), setting `aTransform
* aContext.CurrentMatrixDouble()` as the current matrix on the context will
allow you to paint things in the current SVG element's user space.
SVGTextFrame::PaintSVG doesn't use exactly that because it calls into
nsTextFrame::PaintText, which doesn't expect to be in an SVG coordinate space.
Please let me know if I can clarify anything else about the SVGTextFrame code.
Thanks,
Cameron
_______________________________________________
dev-platform mailing list
[email protected]
https://lists.mozilla.org/listinfo/dev-platform