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
dev-platform@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-platform

Reply via email to