mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
stylo: Fix font-variant-alternates property
This commit is contained in:
parent
9a13cf6bda
commit
5d0f7f10e0
3 changed files with 220 additions and 95 deletions
|
@ -873,6 +873,17 @@ extern "C" {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_nsFont_Destroy(dst: *mut nsFont);
|
pub fn Gecko_nsFont_Destroy(dst: *mut nsFont);
|
||||||
}
|
}
|
||||||
|
extern "C" {
|
||||||
|
pub fn Gecko_ClearAlternateValues(font: *mut nsFont, length: usize);
|
||||||
|
}
|
||||||
|
extern "C" {
|
||||||
|
pub fn Gecko_AppendAlternateValues(font: *mut nsFont, alternate_name: u32,
|
||||||
|
atom: *mut nsIAtom);
|
||||||
|
}
|
||||||
|
extern "C" {
|
||||||
|
pub fn Gecko_CopyAlternateValuesFrom(dest: *mut nsFont,
|
||||||
|
src: *const nsFont);
|
||||||
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_SetImageOrientation(aVisibility: *mut nsStyleVisibility,
|
pub fn Gecko_SetImageOrientation(aVisibility: *mut nsStyleVisibility,
|
||||||
aRadians: f64, aFlip: bool);
|
aRadians: f64, aFlip: bool);
|
||||||
|
|
|
@ -1684,14 +1684,59 @@ fn static_assert() {
|
||||||
<% impl_simple_type_with_conversion("font_language_override", "mFont.languageOverride") %>
|
<% impl_simple_type_with_conversion("font_language_override", "mFont.languageOverride") %>
|
||||||
|
|
||||||
pub fn set_font_variant_alternates(&mut self, v: longhands::font_variant_alternates::computed_value::T) {
|
pub fn set_font_variant_alternates(&mut self, v: longhands::font_variant_alternates::computed_value::T) {
|
||||||
self.gecko.mFont.variantAlternates = v.to_gecko_keyword()
|
use gecko_bindings::bindings::{Gecko_ClearAlternateValues, Gecko_AppendAlternateValues};
|
||||||
|
% for value in "normal swash stylistic ornaments annotation styleset character_variant historical".split():
|
||||||
|
use gecko_bindings::structs::NS_FONT_VARIANT_ALTERNATES_${value.upper()};
|
||||||
|
% endfor
|
||||||
|
use self::longhands::font_variant_alternates::VariantAlternates;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
Gecko_ClearAlternateValues(&mut self.gecko.mFont, v.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.0.is_empty() {
|
||||||
|
self.gecko.mFont.variantAlternates = NS_FONT_VARIANT_ALTERNATES_NORMAL as u16;
|
||||||
|
}
|
||||||
|
|
||||||
|
for val in v.0.iter() {
|
||||||
|
match *val {
|
||||||
|
% for value in "Swash Stylistic Ornaments Annotation".split():
|
||||||
|
VariantAlternates::${value}(ref ident) => {
|
||||||
|
self.gecko.mFont.variantAlternates |= NS_FONT_VARIANT_ALTERNATES_${value.upper()} as u16;
|
||||||
|
unsafe {
|
||||||
|
Gecko_AppendAlternateValues(&mut self.gecko.mFont,
|
||||||
|
NS_FONT_VARIANT_ALTERNATES_${value.upper()},
|
||||||
|
ident.0.as_ptr());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
% endfor
|
||||||
|
% for value in "styleset character_variant".split():
|
||||||
|
VariantAlternates::${to_camel_case(value)}(ref slice) => {
|
||||||
|
self.gecko.mFont.variantAlternates |= NS_FONT_VARIANT_ALTERNATES_${value.upper()} as u16;
|
||||||
|
for ident in slice.iter() {
|
||||||
|
unsafe {
|
||||||
|
Gecko_AppendAlternateValues(&mut self.gecko.mFont,
|
||||||
|
NS_FONT_VARIANT_ALTERNATES_${value.upper()},
|
||||||
|
ident.0.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
% endfor
|
||||||
|
VariantAlternates::HistoricalForms => {
|
||||||
|
self.gecko.mFont.variantAlternates |= NS_FONT_VARIANT_ALTERNATES_HISTORICAL as u16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn copy_font_variant_alternates_from(&mut self, other: &Self) {
|
pub fn copy_font_variant_alternates_from(&mut self, other: &Self) {
|
||||||
|
use gecko_bindings::bindings::Gecko_CopyAlternateValuesFrom;
|
||||||
|
|
||||||
self.gecko.mFont.variantAlternates = other.gecko.mFont.variantAlternates;
|
self.gecko.mFont.variantAlternates = other.gecko.mFont.variantAlternates;
|
||||||
// FIXME: Copy alternateValues as well.
|
unsafe {
|
||||||
// self.gecko.mFont.alternateValues = other.gecko.mFont.alternateValues;
|
Gecko_CopyAlternateValuesFrom(&mut self.gecko.mFont, &other.gecko.mFont);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
${impl_simple_type_with_conversion("font_variant_ligatures", "mFont.variantLigatures")}
|
${impl_simple_type_with_conversion("font_variant_ligatures", "mFont.variantLigatures")}
|
||||||
|
|
|
@ -1272,19 +1272,123 @@ ${helpers.single_keyword_system("font-kerning",
|
||||||
spec="https://drafts.csswg.org/css-fonts/#propdef-font-kerning",
|
spec="https://drafts.csswg.org/css-fonts/#propdef-font-kerning",
|
||||||
animation_value_type="discrete")}
|
animation_value_type="discrete")}
|
||||||
|
|
||||||
/// FIXME: Implement proper handling of each values.
|
|
||||||
/// https://github.com/servo/servo/issues/15957
|
|
||||||
<%helpers:longhand name="font-variant-alternates" products="gecko" animation_value_type="none"
|
<%helpers:longhand name="font-variant-alternates" products="gecko" animation_value_type="none"
|
||||||
spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates">
|
spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates">
|
||||||
use properties::longhands::system_font::SystemFont;
|
use properties::longhands::system_font::SystemFont;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
|
use values::CustomIdent;
|
||||||
|
|
||||||
no_viewport_percentage!(SpecifiedValue);
|
no_viewport_percentage!(SpecifiedValue);
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub enum VariantAlternates {
|
||||||
|
Stylistic(CustomIdent),
|
||||||
|
Styleset(Box<[CustomIdent]>),
|
||||||
|
CharacterVariant(Box<[CustomIdent]>),
|
||||||
|
Swash(CustomIdent),
|
||||||
|
Ornaments(CustomIdent),
|
||||||
|
Annotation(CustomIdent),
|
||||||
|
HistoricalForms,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub struct VariantAlternatesList(pub Box<[VariantAlternates]>);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub enum SpecifiedValue {
|
||||||
|
Value(VariantAlternatesList),
|
||||||
|
System(SystemFont)
|
||||||
|
}
|
||||||
|
|
||||||
|
<%self:simple_system_boilerplate name="font_variant_alternates"></%self:simple_system_boilerplate>
|
||||||
|
|
||||||
|
impl ToCss for VariantAlternates {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
match *self {
|
||||||
|
% for value in "swash stylistic ornaments annotation".split():
|
||||||
|
VariantAlternates::${to_camel_case(value)}(ref atom) => {
|
||||||
|
dest.write_str("${value}")?;
|
||||||
|
dest.write_str("(")?;
|
||||||
|
atom.to_css(dest)?;
|
||||||
|
dest.write_str(")")
|
||||||
|
},
|
||||||
|
% endfor
|
||||||
|
% for value in "styleset character-variant".split():
|
||||||
|
VariantAlternates::${to_camel_case(value)}(ref vec) => {
|
||||||
|
dest.write_str("${value}")?;
|
||||||
|
dest.write_str("(")?;
|
||||||
|
let mut iter = vec.iter();
|
||||||
|
iter.next().unwrap().to_css(dest)?;
|
||||||
|
for c in iter {
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
c.to_css(dest)?;
|
||||||
|
}
|
||||||
|
dest.write_str(")")
|
||||||
|
},
|
||||||
|
% endfor
|
||||||
|
VariantAlternates::HistoricalForms => {
|
||||||
|
dest.write_str("historical-forms")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for VariantAlternatesList {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
if self.0.is_empty() {
|
||||||
|
return dest.write_str("normal");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut iter = self.0.iter();
|
||||||
|
iter.next().unwrap().to_css(dest)?;
|
||||||
|
for alternate in iter {
|
||||||
|
dest.write_str(" ")?;
|
||||||
|
alternate.to_css(dest)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VariantAlternatesList {
|
||||||
|
/// Returns the length of all variant alternates.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.iter().fold(0, |acc, alternate| {
|
||||||
|
match *alternate {
|
||||||
|
% for value in "Swash Stylistic Ornaments Annotation".split():
|
||||||
|
VariantAlternates::${value}(_) => {
|
||||||
|
acc + 1
|
||||||
|
},
|
||||||
|
% endfor
|
||||||
|
% for value in "Styleset CharacterVariant".split():
|
||||||
|
VariantAlternates::${value}(ref slice) => {
|
||||||
|
acc + slice.len()
|
||||||
|
}
|
||||||
|
% endfor
|
||||||
|
_ => acc,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod computed_value {
|
||||||
|
pub type T = super::VariantAlternatesList;
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn get_initial_value() -> computed_value::T {
|
||||||
|
VariantAlternatesList(vec![].into_boxed_slice())
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn get_initial_specified_value() -> SpecifiedValue {
|
||||||
|
SpecifiedValue::Value(VariantAlternatesList(vec![].into_boxed_slice()))
|
||||||
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub flags VariantAlternates: u8 {
|
pub flags ParsingFlags: u8 {
|
||||||
const NORMAL = 0,
|
const NORMAL = 0,
|
||||||
const HISTORICAL_FORMS = 0x01,
|
const HISTORICAL_FORMS = 0x01,
|
||||||
const STYLISTIC = 0x02,
|
const STYLISTIC = 0x02,
|
||||||
|
@ -1296,69 +1400,6 @@ ${helpers.single_keyword_system("font-kerning",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum SpecifiedValue {
|
|
||||||
Value(VariantAlternates),
|
|
||||||
System(SystemFont)
|
|
||||||
}
|
|
||||||
|
|
||||||
<%self:simple_system_boilerplate name="font_variant_alternates"></%self:simple_system_boilerplate>
|
|
||||||
|
|
||||||
<% font_variant_alternates_map = { "HISTORICAL_FORMS": "HISTORICAL",
|
|
||||||
"STYLISTIC": "STYLISTIC",
|
|
||||||
"STYLESET": "STYLESET",
|
|
||||||
"CHARACTER_VARIANT": "CHARACTER_VARIANT",
|
|
||||||
"SWASH": "SWASH",
|
|
||||||
"ORNAMENTS": "ORNAMENTS",
|
|
||||||
"ANNOTATION": "ANNOTATION" } %>
|
|
||||||
${helpers.gecko_bitflags_conversion(font_variant_alternates_map, 'NS_FONT_VARIANT_ALTERNATES_',
|
|
||||||
'VariantAlternates', kw_type='u16')}
|
|
||||||
|
|
||||||
impl ToCss for VariantAlternates {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
if self.is_empty() {
|
|
||||||
return dest.write_str("normal")
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut has_any = false;
|
|
||||||
|
|
||||||
macro_rules! write_value {
|
|
||||||
($ident:ident => $str:expr) => {
|
|
||||||
if self.intersects($ident) {
|
|
||||||
if has_any {
|
|
||||||
dest.write_str(" ")?;
|
|
||||||
}
|
|
||||||
has_any = true;
|
|
||||||
dest.write_str($str)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write_value!(HISTORICAL_FORMS => "historical-forms");
|
|
||||||
write_value!(STYLISTIC => "stylistic");
|
|
||||||
write_value!(STYLESET => "styleset");
|
|
||||||
write_value!(CHARACTER_VARIANT => "character-variant");
|
|
||||||
write_value!(SWASH => "swash");
|
|
||||||
write_value!(ORNAMENTS => "ornaments");
|
|
||||||
write_value!(ANNOTATION => "annotation");
|
|
||||||
|
|
||||||
debug_assert!(has_any);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod computed_value {
|
|
||||||
pub type T = super::VariantAlternates;
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_initial_value() -> computed_value::T {
|
|
||||||
computed_value::T::empty()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_initial_specified_value() -> SpecifiedValue {
|
|
||||||
SpecifiedValue::Value(VariantAlternates::empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// normal |
|
/// normal |
|
||||||
/// [ stylistic(<feature-value-name>) ||
|
/// [ stylistic(<feature-value-name>) ||
|
||||||
/// historical-forms ||
|
/// historical-forms ||
|
||||||
|
@ -1369,35 +1410,63 @@ ${helpers.single_keyword_system("font-kerning",
|
||||||
/// annotation(<feature-value-name>) ]
|
/// annotation(<feature-value-name>) ]
|
||||||
pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
|
pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
|
||||||
-> Result<SpecifiedValue, ParseError<'i>> {
|
-> Result<SpecifiedValue, ParseError<'i>> {
|
||||||
let mut result = VariantAlternates::empty();
|
let mut alternates = Vec::new();
|
||||||
|
|
||||||
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
|
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
|
||||||
return Ok(SpecifiedValue::Value(result))
|
return Ok(SpecifiedValue::Value(VariantAlternatesList(alternates.into_boxed_slice())));
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Ok(flag) = input.try(|input| {
|
let mut parsed_alternates = ParsingFlags::empty();
|
||||||
Ok(match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
|
macro_rules! check_if_parsed(
|
||||||
"stylistic" => STYLISTIC,
|
($flag:ident) => (
|
||||||
"historical-forms" => HISTORICAL_FORMS,
|
if parsed_alternates.contains($flag) {
|
||||||
"styleset" => STYLESET,
|
|
||||||
"character-variant" => CHARACTER_VARIANT,
|
|
||||||
"swash" => SWASH,
|
|
||||||
"ornaments" => ORNAMENTS,
|
|
||||||
"annotation" => ANNOTATION,
|
|
||||||
_ => return Err(()),
|
|
||||||
})
|
|
||||||
}) {
|
|
||||||
if result.intersects(flag) {
|
|
||||||
return Err(StyleParseError::UnspecifiedError.into())
|
return Err(StyleParseError::UnspecifiedError.into())
|
||||||
}
|
}
|
||||||
result.insert(flag);
|
parsed_alternates |= $flag;
|
||||||
}
|
)
|
||||||
|
);
|
||||||
if !result.is_empty() {
|
while let Ok(_) = input.try(|input| {
|
||||||
Ok(SpecifiedValue::Value(result))
|
match input.next()? {
|
||||||
|
Token::Ident(ident) => {
|
||||||
|
if ident == "historical-forms" {
|
||||||
|
check_if_parsed!(HISTORICAL_FORMS);
|
||||||
|
alternates.push(VariantAlternates::HistoricalForms);
|
||||||
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(StyleParseError::UnspecifiedError.into())
|
return Err(StyleParseError::UnspecifiedError.into());
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Token::Function(name) => {
|
||||||
|
input.parse_nested_block(|i| {
|
||||||
|
match_ignore_ascii_case! { &name,
|
||||||
|
% for value in "swash stylistic ornaments annotation".split():
|
||||||
|
"${value}" => {
|
||||||
|
check_if_parsed!(${value.upper()});
|
||||||
|
let ident = CustomIdent::from_ident(i.expect_ident()?, &[])?;
|
||||||
|
alternates.push(VariantAlternates::${to_camel_case(value)}(ident));
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
% endfor
|
||||||
|
% for value in "styleset character-variant".split():
|
||||||
|
"${value}" => {
|
||||||
|
check_if_parsed!(${to_rust_ident(value).upper()});
|
||||||
|
let idents = i.parse_comma_separated(|i|
|
||||||
|
CustomIdent::from_ident(i.expect_ident()?, &[]))?;
|
||||||
|
alternates.push(VariantAlternates::${to_camel_case(value)}(idents.into_boxed_slice()));
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
% endfor
|
||||||
|
_ => return Err(StyleParseError::UnspecifiedError.into()),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_ => Err(StyleParseError::UnspecifiedError.into()),
|
||||||
|
}
|
||||||
|
}) { }
|
||||||
|
|
||||||
|
if parsed_alternates.is_empty() {
|
||||||
|
return Err(StyleParseError::UnspecifiedError.into());
|
||||||
|
}
|
||||||
|
Ok(SpecifiedValue::Value(VariantAlternatesList(alternates.into_boxed_slice())))
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
|
@ -2347,9 +2416,8 @@ ${helpers.single_keyword("-moz-math-variant",
|
||||||
-moz-info -moz-dialog -moz-button -moz-pull-down-menu
|
-moz-info -moz-dialog -moz-button -moz-pull-down-menu
|
||||||
-moz-list -moz-field""".split()
|
-moz-list -moz-field""".split()
|
||||||
kw_font_props = """font_style font_variant_caps font_stretch
|
kw_font_props = """font_style font_variant_caps font_stretch
|
||||||
font_kerning font_variant_position font_variant_alternates
|
font_kerning font_variant_position font_variant_ligatures
|
||||||
font_variant_ligatures font_variant_east_asian
|
font_variant_east_asian font_variant_numeric""".split()
|
||||||
font_variant_numeric""".split()
|
|
||||||
kw_cast = """font_style font_variant_caps font_stretch
|
kw_cast = """font_style font_variant_caps font_stretch
|
||||||
font_kerning font_variant_position""".split()
|
font_kerning font_variant_position""".split()
|
||||||
%>
|
%>
|
||||||
|
@ -2428,6 +2496,7 @@ ${helpers.single_keyword("-moz-math-variant",
|
||||||
font_language_override: longhands::font_language_override::computed_value
|
font_language_override: longhands::font_language_override::computed_value
|
||||||
::T(system.languageOverride),
|
::T(system.languageOverride),
|
||||||
font_feature_settings: longhands::font_feature_settings::get_initial_value(),
|
font_feature_settings: longhands::font_feature_settings::get_initial_value(),
|
||||||
|
font_variant_alternates: longhands::font_variant_alternates::get_initial_value(),
|
||||||
system_font: *self,
|
system_font: *self,
|
||||||
};
|
};
|
||||||
unsafe { bindings::Gecko_nsFont_Destroy(&mut system); }
|
unsafe { bindings::Gecko_nsFont_Destroy(&mut system); }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue