All places where text shaping happens, the callback is used to simply
append a glyph into the end of glyphs vector. This change removes the
callback parameter and makes the text shaping function return a glyph
run.
Append text chunks to either the start or end of the text fragment,
depending on the text direction. The direction is determined by what
script its code points are from.
The ChunkIterator now limits a chunk to using only one font (before, it
was possible to have a chunk with >1 font, when `unicode-range` CSS
property is used).
This change allows us to reduce some complexity in the text shaping and
painting code and makes us compatible with the APIs in Skia and
HarfBuzz.
According to the CSS font matching algorithm specification, it is
supposed to be executed for each glyph instead of each text run, as is
currently done. This change partially implements this by having the
font matching algorithm produce a list of fonts against which each
glyph will be tested to find its suitable font.
Now, it becomes possible to have per-glyph fallback fonts: if the
needed glyph is not present in a font, we can check the subsequent
fonts in the list.
Previously, we determined the positions of glyphs for each text run at
the time of painting, which constituted a significant portion of the
painting process according to profiles. However, since we already go
through each glyph to figure out the width of each fragment during
layout, we can simultaneously gather data about the position of each
glyph in the layout phase and utilize this information in the painting
phase.
I had to update expectations for a couple of reference tests. These
updates are due to the fact that we now measure glyph positions during
layout using a 1x font, and then linearly scale each glyph's position
to device pixels during painting. This approach should be acceptable,
considering we measure a fragment's width and height with an unscaled
font during layout.
When calculating the width of text using a bitmap font, a glyph spacing
is added at the end of each fragment, including the last one. This meant
that everything was 1 pixel too long. This bug did not affect vector
fonts.
This is intended to annotate conversions from unknown floating-point
values to CSSPixels, and make it more obvious the fp value will be
rounded to the nearest fixed-point value.
In general it is not safe to convert any arbitrary floating-point value
to CSSPixels. CSSPixels has a resolution of 0.015625, which for small
values (e.g. scale factors between 0 and 1), can produce bad results
if converted to CSSPixels then scaled back up. In the worst case values
can underflow to zero and produce incorrect results.
Check the width of the next token after white space to decide line
breaks. The next width can also be the total width of multiple tokens.
This better follows the CSS Text specification and matches behavior of
other browsers.
Fixes#20388.
This reverts commit b062a0fb7c.
This made a calculation of pseudo-elements' height incorrect when they
had `height` set to `auto` and used other techniques (like setting
`padding-top`) to set height, as it was now also adding an empty line.
Additionally, the case didn't work for content containing whitespace
characters, so a pseudo-element with `content: " "` didn't have *this*
particular problem.
As it turns out, Layout::TreeBuilder never managed to wrap text within
table boxes in anonymous wrapper boxes, since it relied on checking
text_for_rendering(), and that was never initialized during that early
stage of tree building.
This patch fixes the issue by making text_for_rendering() compute the
(potentially collapsed) text lazily when called.
Note that the test included with this patch is still totally wrong,
but that is now a TFC problem rather than a TreeBuilder problem. :^)
This ensures that we create a line box for content:"", which would
otherwise get pruned by the empty line cleanup in IFC.
The empty line box is important in this case, since it gives us a
reference point for measuring the automatic height of the IFC's
containing block. By having an empty line, we can now correctly measure
the impact of vertical margins on a generated box with content:""
and allow them to contribute to the block height.
This is a more correct check than !is_inline_block(), as it now enters
all elements that have inline behavior on the outside and flow behavior
on the inside.
This remained undetected for a long time as HeaderCheck is disabled by
default. This commit makes the following file compile again:
// file: compile_me.cpp
#include <LibWeb/CSS/GridTrackSize.h>
// That's it, this was enough to cause a compilation error.
This fixes an issue where whitespace inside embedded <svg> elements
would create unexpected whitespace text content on the page.
When combined with something like `white-space: pre-wrap`, it ended
up generating a lot of surprising vertical offsets.
We were mistakenly treating inline replaced elements as if they are the
start of a regular display:inline element. This meant that we collected
the horizontal start and end metrics from the box model, and then added
those to the inline-level item produced by InlineLevelIterator.
This effectively meant that <img>, <svg> and other replaced elements got
double-sized values for margin/border/padding on the left and right
sides. (Which manifested as a mysterious margin around the element.)
Previously, the whitespace collapsing code had a parameter telling it
whether the previous text node ended in whitespace. This was not
actually necessary, so let's get rid of it.
Instead of emitting a Text item with the "should_force_break" flag set
to true, newlines in newline-preserving text content now timply turn
into ForcedBreak items. This makes the <pre> element work again.
CSS floats are now emitted by the InlineLevelIterator. When this
happens, IFC coordinates with the parent BFC to float the box to the
side, using the current LineBuilder state for vertical placement.
This makes the "instructions" text on Acid3 render as a single
contiguous flow of inline content.
Note that we don't put absolutely positioned items on a line! This is
just so that IFC can discover boxes and pass them along to BFC.
This fixes an issue where only direct children of the IFC containing
block were considered for absolute positioning. Now we pick up
absolutely positioned children of nested inline nodes as well.
The purpose of "entering" a box is to collect box model metrics that
apply to content fragments within the box. However, inline-blocks are
special, in that their inner content does not directly participate in
the inline formatting context outside it.
We were neglecting to pop nodes from the box model stack. The metrics
were already being zeroed out when used, but let's not grow the stack
needlessly.
This patch adds a map of Layout::Node to FormattingState::NodeState.
Instead of updating layout nodes incrementally as layout progresses
through the formatting contexts, all updates are now written to the
corresponding NodeState instead.
At the end of layout, FormattingState::commit() is called, which
transfers all the values from the NodeState objects to the Node.
This will soon allow us to perform completely non-destructive layouts
which don't affect the tree.
Note that there are many imperfections here, and still many places
where we assign to the NodeState, but later read directly from the Node
instead. I'm just committing at this stage to make subsequent diffs
easier to understand.