Commit Graph

24 Commits

Author SHA1 Message Date
Martin Robinson
b8ef264268 layout: Integrate more details into FontAndScriptInfo (#43974)
This will be needed for ensuring that shaped text is valid during
relayout. It duplicates some information, but the hope is that when all
of these change are done, there will be many fewer individual shaped
segments of text.

Testing: This should not really change observable behavior so is covered
by existing tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-04-08 21:37:44 +00:00
Martin Robinson
82c2f1434e fonts: Clean up application of word-spacing (#43899)
Let `word_spacing` be optional in `ShapingOptions` like
`letter_spacing`. In addition, send `None` for Canvas rendering instead
of the width of the space character. It is supposed to represent *extra*
space added between words. Finally, remove duplicated code that applied
word and letter spacing for the fast shaper. This can just reuse the
code from the Harfbuzz shaper.

Testing: This causes some canvas tests to start passing.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-04-06 09:20:34 +00:00
Martin Robinson
322dfd0115 layout: Combine some common text segment data into FontAndScriptInfo (#43653)
This starts splitting out commonly shared data into a new data structure
that will make it easier to compare old shaping results to new ones in
the future. This eliminates some memory usage during inline layout, but
adds 8 bytes per segment (usually one per inline box). We hope to
recover this memory by storing one `GlyphStore` per `TextRunSegment`,
which
should recover 16 bytes.

Testing: This should not change behavior so is covered by existing
tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Luke Warlow <lwarlow@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2026-03-30 13:15:23 +00:00
rtjkro
afa4b524ab font: Consider lang attribute when shaping (#43447)
Fonts: Added `language` field in the struct `ShapingOptions` so Harfbuzz
can also consider language when shaping glyphs.

Testing: Existing WPT. Most recent try run:
[link](https://github.com/RichardTjokroutomo/servo/actions/runs/23334049878)

2 new passes, 5 new fails.

Failures:
- `/css/css-text-decor/text-emphasis-punctuation-2.html` should be a
false positive since `text-emphasis` shorthand hasn't been supported on
stylo yet.
- Not quite sure about this, but
`/css/css-text/text-spacing-trim/text-spacing-trim-quote-001.html?class=halt,htb&amp;lang=ja`
tests `text-spacing-trim` default behavior on `JA` texts. Since this
property is not defined in Stylo, I believe that this property's
behavior (including default) hasn't been considered in Servo. So Servo
previously passing the test should be a false positive. As a side note,
this test also fails on Firefox.
- `/html/canvas/element/manual/text/canvas.2d.lang.dynamic.html`,
`/html/canvas/element/manual/text/canvas.2d.lang.html`,
`/html/canvas/element/manual/text/canvas.2d.lang.inherit.disconnected.canvas.html`
fail because canvas' experimental [`lang`
attribute](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lang)
hasn't been supported yet.

credits to @mrobinson for figuring out the reason for last 3 failing WPT
tests!

Fixes: #41825

---------

Signed-off-by: Richard Tjokroutomo <richard.tjokro2@gmail.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2026-03-23 08:39:53 +00:00
Euclid Ye
cae0752676 cargo: Rename workspace-local library starting with b to servo_* (#43552)
Follow up of #43526. This addresses Nico's comment:
https://github.com/servo/servo/pull/43526#issuecomment-4104953308

- `bluetooth_traits` -> `servo_bluetooth_traits`
- `base` -> `servo_base`
- `bluetooth` -> `servo_bluetooth`
- `background_hang_monitor` -> `servo_background_hang_monitor`

Testing: This should not change any behaviour.

---------

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
2026-03-23 08:26:49 +00:00
Freya Arbjerg
4ec46aaadc layout: Support text carets on empty lines in <textarea> (#42218)
This PR modifies the text layout in such a way that text carets can
render on empty lines (other than the first if the textarea is empty).
Zero-length segments are added to support caret rendering. The last
line, if it is without content, does not currently render a caret
correctly

This corresponds to cases 0, 1, and 3 in #41338

Testing: WPT subset `tests/wpt/tests/css/css-text/` ran as expected, one
internal WPT test was added
Fixes: Partially addresses https://github.com/servo/servo/issues/41338

---------

Signed-off-by: Freya Arbjerg <git@arbjerg.dev>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2026-03-09 07:06:27 +00:00
Martin Robinson
150f767b19 layout: Store a fallback cache in the FontGroup (#42466)
Store a list of recently used fallbacks in the `FontGroups`, keyed by
`lang` property and the detected `UnicodeBlock` and `Script` of the
character in question. This should mean that less work is done when
switching between falling back and not falling back. In addition, this
will allow better caching of `FontRef`s that come directly from the
platform font backend -- something necessary for the next generation
font fallback code.

Testing: This should not change rendering output so is covered by
existing
tests. It might have a minor affect on performance.
Fixes: This is part of #41426.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-02-13 17:01:41 +00:00
Martin Robinson
edbce7e854 layout: Properly count characters when segmenting IFC text (#42399)
There was a bug where characters were not properly counted when
segmeting IFC text. Immediately incrementing the
`current_character_index` meant that the count was always one character
off. This character count is mainly used for drawing selections and
really matters when multiple text segments are in a single IFC. This
change fixes that by counting characters in the same way we were
counting byte indices for the text.

Testing: This change adds a Servo-specific WPT-style test. As it is
quite
difficult to reproduce the correct display in a different way, a
mismatch test
is used. Since this is mainly about appearance and is very specific to
our
implementation the test is Servo-specific.
Fixes: #42354.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-02-06 13:37:19 +00:00
Martin Robinson
c639bb1a7b layout: Do not enable letter-spacing for cursive scripts (#42165)
This is specified in CSS Text 3.

Testing: This fixes a WPT test.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-01-26 21:34:39 +00:00
Martin Robinson
2a7d2780b3 fonts: Store shaping output per-character rather than per-code point (#42105)
This change is a reworking of the shaping code and simplification of the
`GlyphRun` data structure.

The shaper was written between 2012 and 2014 against an early version of
Rust. It was originally written to have a single glyph entry per UTF-8
code point. This is useful when you always need to iterate through
glyphs based on UTF-8 code points. Unfortunately, this required a
tri-level data structure to hold detailed glyphs and meant that CJK
characters took over 3x the memory usage of ASCII characters. In
addition, iterating through glyphs (the most common and basic operation
on shaped text) required doing a lookup involving a binary search for
detailed glyphs (ones that had large advances or mapped to more or less
than a single UTF-8 code point).

The new design of the `GlyphStore` is instead based on `chars` in the
input string. These are tracked during layout so that the resulting
glyph output can be interpreted relatively to its original character
offset in the containing IFC. We are already dealing with IFC text on a
per-character basis for a variety of reasons (such as text
transformation and whitespace collapse). In addition, we will now able
to
implement mapping between the character offsets before and after layout
transformations of the original DOM string.

Now the penalty of more complex glyph iteration is only paid when
transforming glyph offsets to character offsets. Currently this is only
done for selections and clicking in text boxes, both of which are much
less common than layout.

This change does not properly handle selections in RTL text, though
rendering and basic selection and visual movement works (though buggy).

It does not seem like this affects the performance of shaping based on
measurement using the text shaping performance counters. This likely
means that the performance of shaping is dominated on our machines by
HarfBuzz. We noticed no performance degradation in Speedometer when run
on a M3 Mac.

Followup work:
 - Properly handle selection in RTL text.
 - Support mapping from original DOM character offsets to offsets in
   layout after text transformation and whitespace collapse. This is now
   possible.

Testing: This causes some tests to pass and a few to fail. This is
likely
due to the fact that we are handling glyphs more consistently while
shaping. Of the new failures:
- `letter-spacing-bengali-yaphala-001.html`,
`letter-spacing-cursive-001.html`, `font-feature-settings-tibetan.html`
where passing before probably because we were not applying letter
spacing to detailed glyphs. These scripts should not have letter spacing
applied to them, because they are cursive -- which we never implemented
properly. It will be handled in a a followup.
- `shaping-arabic-diacritics-001.html`: This was a false pass. The tests
verifies that Arabic diacritics are applied to NBSP. This wasn't
happening before nor after this change, but the results matched anyway.
Now they don't, but before and after are equally broken.
 - 
Fixes: #216
Part of #35540.

---------

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2026-01-26 12:38:04 +00:00
Martin Robinson
4a9c95ac8e layout: Handle selection during display list construction (#41963)
Instead of handling selection in text input during box tree / fragment
tree construction, fully handle it during display list construction.
This means that when the selection changes in script, it updates
automatically in the `FragmentTree` and only a new display list is
necessary. This avoids a layout while changing the selection in text
fields.

Testing: This fixes a few rendering issues, but these are very hard
to isolate and test for. It causes one test to start failing, but this
is
because a cursor that wasn't rendered properly now starts showing
up.
Fixes: #41920.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2026-01-21 08:04:15 +00:00
Martin Robinson
90f568f962 fonts: Store ByteIndex as a usize (#42012)
This value is supposed to represent a UTF-8 code point offset or
length. It should never be negative and we often need to convert it to a
`usize`, so we can store it as a `usize`.

Testing: This should not change behavior so existing tests should
suffice.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-01-19 18:30:04 +00:00
Oriol Brufau
e58bdc03f9 layout: Track parentage in the box tree (#41884)
This patch adds a weak reference to the parent box in `LayoutBoxBase`,
so that boxes can refer to their parent. This will help during future
changes for incremental layout.

Testing: Not needed, no behavior change

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2026-01-14 14:40:12 +00:00
Martin Robinson
d5dcdd6e06 script: Add support for setting the edit point with the mouse in <input> and <textarea> (#41906)
This change updates and implements the old `query_text_index` layout
query to properly look for the glyph index of a point within a node's
`Fragment`s. This should work properly with the shadow DOM of both
`<input>` and `<textarea>` elements. In particular, multiple lines are
supported.

Caveats:
 - `<input>` and `<textarea>` that are transformed are currently not
   supported. This will happen in a followup.
 - For multi-line inputs, we should be finding the text offset of the
   nearest line that is within the block range of the click. This will
   happen in a followup.

Testing: This change adds two Servo-specific WPT-style tests. These are
Servo-specific because the behavior of clicking in text fields isn't
fully specified.

Fixes: #35432
Fixes: #10083

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2026-01-14 13:23:43 +00:00
Martin Robinson
30fa32714e Remove Servo's custom Range implementation (#41880)
Servo `Range` type probably predates the `Range` type in the standard
library. The fact that there are two `Range` types in Servo is often
very confusing. The internal type is only used for byte indices in glyph
runs. This change removes the internal generic `Range` in favor of a
small wrapper around a native Rust `Range`.

It's likely that there are more improvements that could be done here,
such as reusing `Utf8CodePointIndex`, but this code is going to change a
lot soon and these ranges might soon be replaced with actual glyph
ranges. We are going to be looking to remove `TextByteRange` entirely.
This is just an intermediate step.

Testing: This should not change behavior and is thus covered by existing
tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-01-13 15:31:47 +00:00
Martin Robinson
55c4f579c9 fonts: Refactor the font fallback code a little bit and remove RwLock around FontGroup (#41449)
This change reworks the logic for finding font fallbacks to make it
simpler. I was involved with writing the code for `FontGroupFamily` and
`FontGroupFamilyMember` and I still struggle a bit with understanding
it, so I've chosen to do this. In addition, the change is in preparation
for more flexible font fallback (#41426).

The main changes here are:
 1. Move the logic for creating a new descriptor with variations into
    the Font constructor.
 2. Add some more general methods to `FontGroupFamily` such as a
    template iterator.
 3. Use `OnceLock` to avoid a convoluted code structure because of
    mutability and also having boolean "loaded" members. This is what
    `OnceLock` and `OnceCell` are for!
 4. Rename `FontGroupFamilyMember` to `FontGroupFamilyTemplate` to
    stress that it is one template of a particular `FontGroupFamily`.

Testing: This should not change behavior so is covered by existing WPT
tests.

---------

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-12-22 15:06:25 +00:00
minghuaw
7e665f6bb9 layout: Remove centralized FontKeyAndMetrics list from the InlineFormattingContext (#41208)
As previously described in #41150 ,
`InlineFormattingContext::font_metrics` contains info that are easily
accessible via `FontRef`, and the `FontInstanceKey` is already cached in
the `FontContext`. Instead of keeping an index and obtaining these info
using the index, one should be able to obtain the same info via a
`FontRef`.

Testing: This should not change the expected behavior and the current
unit tests and WPT testcases should be sufficient.
Fixes: #41150

---------

Signed-off-by: minghuaw <michael.wu1107@gmail.com>
2025-12-15 16:52:43 +00:00
Freya Arbjerg
adc16980c4 Fix duplicate carets in <textarea> (#40837)
This fixes a bug where <textarea> would render with many carets. Reports
vary, but in my case it would would be at the start and end of every
"word", as long as those cursors came before the actual cursor.

The actual cursor would render normally, except that it does not render
immediately after a line break. This appears to be a separate issue.

The issue occurs because `push_glyph_store_to_unbreakable_segment` gets
passed too long of a range. This bypasses the check here:
bbbfe6d299/components/layout/flow/inline/mod.rs (L1438-L1440)

Testing: Tested with `./mach test-unit`. No new tests were added.
Perhaps someone has an idea of how one could test this. Also tested
manually in multiple scenarios, including with a text input. I was not
able to get Servo to soft wrap in a textbox, maybe Servo does not
support that currently. Ultimately I am not super confident about this
solution as it interacts with line wrapping, and I would like to hear
some feedback
Fixes: https://github.com/servo/servo/issues/40075

Signed-off-by: Freya Arbjerg <git@arbjerg.dev>
2025-11-30 18:58:10 +00:00
Mukilan Thiyagarajan
07cf9a9175 compositor: Rename RenderingGroupId to PainterId (#40307)
And let the Compositor create the `PainterId` when it creates a new
`Painter`. Also make inline formatting code take `LayoutContext` rather
than threading it via the `InlineFormattingContextBuilder`.


Testing: Should preserve existing behavior, so covered by existing
tests.

Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2025-10-31 11:49:24 +00:00
araya
6880af31f6 fonts: Do not fall back to non-Japanese fonts for Han in Japanese language documents (#39608)
## Motivations

This PR improves fallback font algorithm for Japanese documents on macos
and Linux.

For Japanese documents, we generally use a same font for
Hiragana/Katakana/Kanji(Han).
Since their is a fallback algorithm to render serif fonts for
`Script::Han`in current implementation, it gives unnatural reading
experience for Japanese users.

## Changes
To achieve above, 
- added `lang` field to `FallbackFontSelectionOptions` struct
- pass `_x_lang` from text_run in layout component

## Screenshots
rendering https://ja.wikipedia.org/wiki/Servo

<table>
<thead>
<tr>
<th>before fixed</th>
<th>after fixed</th>

</tr>
</thead>
<tbody>
<tr>
<td>
<!-- before fixed -->
<img width="545" height="168" alt="CleanShot 2025-10-01 at 23 08 24"
src="https://github.com/user-attachments/assets/9ad7ccb0-729a-49ad-b8ce-88b1d78a2705"
/>



</td>
<td>
<!-- after fixed -->

<img width="474" height="139" alt="CleanShot 2025-10-01 at 23 07 16"
src="https://github.com/user-attachments/assets/010e8052-5a10-48e1-8367-ff29a5632d0f"
/>


</td>

</tr>
</tbody>
</table>

---------

Signed-off-by: araya <araya@araya.dev>
2025-10-11 09:07:14 +00:00
Narfinger
e64f021550 Allow WebViews and fonts to have a RenderingGroupId. (#39140)
Motivation: The font cache currently has to store a cache of Keys which
need to be given by the webrender instance.
Having a cache for every WebViewId in the future when we have every
webview have the different webrender::DocumentId might be too wasteful
to store this key cache per DocumentId. This proposes to include in the
WebViewId another id, the RenderingGroupId. This id can be easily
changed
to be equivalent to the DocumentId when we support multiple DocumentIds
for a unique Webrender instance.
Additionally this will keep it easier to integrate the currently out of
tree patches for multiple rendering contexts with different webrenders.


Change:
We introduce the RenderingGroupId in the WebViewId and allow a method to
extract it. The font key cache uses this cache
and forwards it to the Compositor when requesting new changes. The
compositor currently ignores this id.
Additionally, the WebView can return the RenderingGroupId. The WebViewId
also has an appropiate constructor for specifying a RenderingGroupId.
Because there currently will be only one RenderingGroupId the
performance will be minimal.


Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>

Testing: This should be covered by WPT tests and normal browsing
behavior works fine.

---------

Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
2025-09-29 10:01:56 +00:00
Simon Wülker
7471ad7730 fonts: Implement CSS font-variation-settings property for FreeType platforms (#38642)
This change adds support for variable fonts via the
[`font-variation-settings`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings)
property.

There are three areas where we need to set the variation values:
* Webrender (`compositor.rs`), for drawing the glyphs
* Harfbuzz (`shaper.rs`), for most shaping tasks
* PlatformFont (`fonts/platform/`), for horizontal advances and kerning

For now, freetype is the only platform shaper that supports variable
fonts. I can't easily test the fonts with non-freetype shapers. Thats
why variable fonts are behind the `layout_variable_fonts_enabled` pref,
which is disabled by default.

<img width="1250" height="710" alt="image"
src="https://github.com/user-attachments/assets/1aee1407-f3a2-42f6-a106-af0443fcd588"
/>

<details><summary>HTML test file</summary>

```html
<style>
@font-face {
  font-family: "Amstelvar VF";
  src: url("https://mdn.github.io/shared-assets/fonts/variable-fonts/AmstelvarAlpha-VF.woff2")
    format("woff2-variations");
  font-weight: 300 900;
  font-stretch: 35% 100%;
  font-style: normal;
  font-display: swap;
}

p {
  font:
    1.2em "Amstelvar VF",
    Georgia,
    serif;
  font-size: 4rem;
  margin: 1rem;
  display: inline-block;
}

.p1 {
  font-variation-settings: "wght" 300;
}

.p2 {
  font-variation-settings: "wght" 625;
}

.p3 {
  font-variation-settings: "wght" 900;
}

</style>
<div>
  <p class="p1">Weight</p>
  <span>(font-variation-settings: "wght" 300)</span>
</div>
<div>
  <p class="p2">Weight</p>
  <span>(font-variation-settings: "wght" 625)</span>
</div>
<div>
  <p class="p3">Weight</p>
  <span>(font-variation-settings: "wght" 900)</span>
</div>
</div>
```
</details>



https://github.com/user-attachments/assets/9e21101a-796a-49fe-b82c-8999d8fa9ee1


Testing: Needs decision on whether we want to enable the pref in CI
Works towards https://github.com/servo/servo/issues/37236

Depends on https://github.com/servo/stylo/pull/230

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-08-18 16:30:14 +00:00
Martin Robinson
a0dd2c1beb layout: Share styles to inline box children via SharedInlineStyles (#36896)
`TextRun`s use their parent style to render. Previously, these styles
were cloned and stored directly in the box tree `TextRun` and resulting
`TextFragment`s. This presents a problem for incremental layout.
Wrapping the style in another layer of shared ownership and mutability
will allow updating all `TextFragment`s during repaint-only incremental
layout by simply updating the box tree styles of the original text
parents.

This adds a new set of borrows when accessing text styles, but also
makes it so that during box tree block construction
`InlineFormattingContext`s are created lazily and now
`InlineFormattingContextBuilder::finish` consumes the builder, making
the API make a bit more sense. This should also improve performance of
box tree block construction slightly.

Testing: This should not change observable behavior and thus is covered
by existing WPT tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-05-12 09:38:50 +00:00
Martin Robinson
7787cab521 layout: Combine layout_2020 and layout_thread_2020 into a crate called layout (#36613)
Now that legacy layout has been removed, the name `layout_2020` doesn't
make much sense any longer, also it's 2025 now for better or worse. The
split between the "layout thread" and "layout" also doesn't make as much
sense since layout doesn't run on it's own thread. There's a possibility
that it will in the future, but that should be something that the user
of the crate controls rather than layout iself.

This is part of the larger layout interface cleanup and optimization
that
@Looriool and I are doing.

Testing: Covered by existing tests as this is just code movement.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-04-19 10:17:03 +00:00