servo/components/fonts/tests/font.rs
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

105 lines
3.4 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use app_units::Au;
use euclid::num::Zero;
use fonts::platform::font::PlatformFont;
use fonts::{
Font, FontData, FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef,
PlatformFontMethods, ShapingFlags, ShapingOptions,
};
use servo_url::ServoUrl;
use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps;
use style::values::computed::{FontStretch, FontStyle, FontWeight};
use unicode_script::Script;
fn make_font(path: PathBuf) -> Font {
let mut bytes = Vec::new();
File::open(path.clone())
.expect("Couldn't open font file!")
.read_to_end(&mut bytes)
.unwrap();
let data = FontData::from_bytes(&bytes);
let identifier = FontIdentifier::Web(ServoUrl::from_file_path(path).unwrap());
let platform_font = PlatformFont::new_from_data(identifier.clone(), &data, None, &[]).unwrap();
let template = FontTemplate {
identifier,
descriptor: platform_font.descriptor(),
stylesheet: None,
};
let descriptor = FontDescriptor {
weight: FontWeight::normal(),
stretch: FontStretch::hundred(),
style: FontStyle::normal(),
variant: FontVariantCaps::Normal,
pt_size: Au::from_px(24),
variation_settings: vec![],
};
Font::new(FontTemplateRef::new(template), descriptor, Some(data), None).unwrap()
}
#[test]
fn test_font_can_do_fast_shaping() {
let dejavu_sans = make_font(
[
env!("CARGO_MANIFEST_DIR"),
"tests",
"support",
"dejavu-fonts-ttf-2.37",
"ttf",
"DejaVuSans.ttf",
]
.iter()
.collect(),
);
let dejavu_sans_fast_shapeable = make_font(
[
env!("CARGO_MANIFEST_DIR"),
"tests",
"support",
"dejavu-fonts-ttf-2.37",
"ttf",
"DejaVuSansNoGSUBNoGPOS.ttf",
]
.iter()
.collect(),
);
// Fast shaping requires a font with a kern table and no GPOS or GSUB tables.
let shaping_options = ShapingOptions {
letter_spacing: None,
word_spacing: Au::zero(),
script: Script::Latin,
flags: ShapingFlags::empty(),
};
assert!(!dejavu_sans.can_do_fast_shaping("WAVE", &shaping_options));
assert!(dejavu_sans_fast_shapeable.can_do_fast_shaping("WAVE", &shaping_options));
// Non-Latin script should never have fast shaping.
let shaping_options = ShapingOptions {
letter_spacing: None,
word_spacing: Au::zero(),
script: Script::Cherokee,
flags: ShapingFlags::empty(),
};
assert!(!dejavu_sans.can_do_fast_shaping("WAVE", &shaping_options));
assert!(!dejavu_sans_fast_shapeable.can_do_fast_shaping("WAVE", &shaping_options));
// Right-to-left text should never use fast shaping.
let shaping_options = ShapingOptions {
letter_spacing: None,
word_spacing: Au::zero(),
script: Script::Latin,
flags: ShapingFlags::RTL_FLAG,
};
assert!(!dejavu_sans.can_do_fast_shaping("WAVE", &shaping_options));
assert!(!dejavu_sans_fast_shapeable.can_do_fast_shaping("WAVE", &shaping_options));
}