Changes between Version 3 and Version 4 of Ruby


Ignore:
Timestamp:
Dec 17, 2014 12:07:17 PM (7 years ago)
Author:
mmaxfield@apple.com
Comment:

Pictures!

Legend:

Unmodified
Added
Removed
Modified
  • Ruby

    v3 v4  
    1 '''Ruby'''
     1=== Ruby ===
    22
    33Ruby is marked up as such: “<ruby><rb>Left Below</rb><rp>(</rp><rt>Left Above</rt><rp>)</rp><rb>Right Below</rb><rp>(</rp><rt>Right Above</rt><rp>)</rp></ruby>”
    44
    5 Let’s break that down. We have two pieces which are almost identical to: “<rb>Left Below</rb><rp>(</rp><rt>Left Above</rt><rp>)</rp>”. Each of these is called a “ruby run”. A ruby tag’s contents is a sequence of ruby runs.
     5That ends up looking like this:
     6
     7[[Image(1.png)]]
     8
     9Let’s break that down. We have two pieces which are almost identical to: “<rb>Left Below</rb><rp>(</rp><rt>Left Above</rt><rp>)</rp>”. Each of these is called a “ruby run”. A ruby tag’s contents is a sequence of ruby runs. These are the ruby runs:
     10
     11[[Image(2.png)]]
    612
    713<rp> is (usually) display: none; so this is the same as “<rb>Left Below</rb><rt>Left Above</rt>”. It is used for browsers who don’t recognize the ruby tag (and will instead simply show the contents of unrecognized tags inline). This is so that the ruby text has some sort of context around it that shows that it is not part of the normal stream of text. The tag stands for “ruby parenthesis”.
     
    915The <rb> tag is automatically inserted around whatever content comes before the <rt> tag, so this is the same as simply “Left Below<rt>Left Above</rt>”. “rb” stands for “ruby base” and “rt” stands for “ruby text”.
    1016
    11 There are two different renderer classes which might represent a <ruby> tag, RenderRubyAsInline and RenderRubyAsBlock. They are used for display: inline and display: block, respectively. A ruby run is represented by a RenderRubyRun. Ruby text is represented by a RenderRubyText, and a ruby base is represented by a RenderRubyBase.
     17There are two different renderer classes which might represent a <ruby> tag, RenderRubyAsInline and RenderRubyAsBlock. They are used for display: inline and display: block, respectively. A ruby run is represented by a RenderRubyRun. Ruby text is represented by a RenderRubyText, and a ruby base is represented by a RenderRubyBase. In the following picture, blue is ruby text and green is ruby base:
    1218
    13 RenderRubyRun, RenderRubyText, and RenderRubyBase are all subclasses of RenderBlockFlow. That way, the base and text block layout is reused as any other progression of <div>s. This means that a RenderRubyText is the first child of a RenderRubyRun (if it is present), and a RenderRubyBase is the last child of a RenderRubyRun (if it is present). This also means that a RenderRubyRun has at most two children. In addition, there can be a line break between two ruby runs, but not inside a single ruby run. This also means that if the contents of a ruby text are too wide for a line, the ruby text will line wrap inside the element. The same goes for the ruby base.
     19[[Image(3.png)]]
     20
     21RenderRubyRun, RenderRubyText, and RenderRubyBase are all subclasses of RenderBlockFlow. That way, the base and text block layout is reused as any other progression of <div>s. This means that a RenderRubyText is the first child of a RenderRubyRun (if it is present), and a RenderRubyBase is the last child of a RenderRubyRun (if it is present). This also means that a RenderRubyRun has at most two children. In addition, there can be a line break between two ruby runs, but not inside a single ruby run. This also means that if the contents of a ruby text are too wide for a line, the ruby text will line wrap inside the element. The same goes for the ruby base. In the following diagram, a red arrow is a RenderObject() child relationship and a green arrow is an InlineFlowBox child relationship (however, the relationship between RenderRubyText and RootInlineBox is a has-a relationship, by way of the RenderLineBoxList in RenderBlockFlow) .
     22
     23[[Image(4.png)]]
    1424
    1525The width of a ruby run is simply the maximum width of its two children: the ruby text and the ruby base (which is true of all inline-block elements). However, it’s important to realize that if the ruby text is significantly longer than the ruby base, there is some amount of overhang where the ruby text is allowed to encroach upon the space of the elements which surround it. However, this amount of overhang is capped at a multiple of the font size. This overhang is implemented by setting negative margins on the RenderRubyRun.
     
    2333Then we pop back up to RenderRubyRun::layout() just after it calls RenderBlockFlow::layout(), where we place the ruby text to make it match up with the ruby base.
    2434
    25 Once we’re done layout out the ruby run itself, the line containing the ruby run lays out. RenderRubyRun acts as a replaced inline element (see its constructor), which means that the constructBidiRunsForSegment() call inside layoutRunsAndFloatsInRange() will return a bidi run whose renderer() is the RenderRubyRun. Then, when computeInlineDirectionPositionsForSegment() iterates over the bidi runs, it calls RenderRubyRun::getOverhang(), and applies margins equal to the starting and ending overhang. We can calculate how much overhang there should be by looking at the size of the RootInlineBoxes inside the RenderRubyBase, and comparing that to the size of the RenderRubyRun. The RootInlineBoxes will hug the size of the actual text inside the base, while the RenderRubyRun’s logicalWidth() will grow to encompass the ruby text as well. Note that we look at the ruby base (not the ruby text) when performing this calculation.
     35Once we’re done layout out the ruby run itself, the line containing the ruby run lays out. RenderRubyRun acts as a replaced inline element (see its constructor), which means that the constructBidiRunsForSegment() call inside layoutRunsAndFloatsInRange() will return a bidi run whose renderer() is the RenderRubyRun. Then, when computeInlineDirectionPositionsForSegment() iterates over the bidi runs, it calls RenderRubyRun::getOverhang(), and applies margins equal to the starting and ending overhang. We can calculate how much overhang there should be by looking at the size of the RootInlineBoxes inside the RenderRubyBase, and comparing that to the size of the RenderRubyRun. The RootInlineBoxes will hug the size of the actual text inside the base, while the RenderRubyRun’s logicalWidth() will grow to encompass the ruby text as well. Note that we look at the ruby base (not the ruby text) when performing this calculation. In the following diagram of the ruby's containing line, the green arrows are the InlineFlowBox child relationships, and the purple arrows are InlineBox::renderer() relationships.
     36
     37[[Image(5.png)]]
    2638
    2739If you have a ruby run on a justified line, our search for expansion opportunities will descend into ruby bases inside RenderBlockFlow::computeInlineDirectionPositionsForSegment(). Then, when it comes time to grow the InlineBoxes by expanding the expansion opportunities, we also descend into ruby runs in RenderBlockFlow::updateRubyForJustifiedText. In that function, we calculate the size that the expanded base should have, then tell the RenderRubyBase to set its “initial offset” to a value that would center its contents. We then override the width of the RenderRubyRun and re-lay it out. This time, however, RenderRubyBase::adjustInlineDirectionLineBounds remembers the initial offset that we set earlier, and adjust the bounds of its inner line accordingly. Then, because ruby bases are justified (see above), the ruby base will expand to take up the entire space of the line so the expansions will be re-calculated and will happen to end up being the same as the expansions for the line that contains the ruby.