mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +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" {
|
||||
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" {
|
||||
pub fn Gecko_SetImageOrientation(aVisibility: *mut nsStyleVisibility,
|
||||
aRadians: f64, aFlip: bool);
|
||||
|
|
|
@ -1684,14 +1684,59 @@ fn static_assert() {
|
|||
<% 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) {
|
||||
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)]
|
||||
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;
|
||||
// FIXME: Copy alternateValues as well.
|
||||
// self.gecko.mFont.alternateValues = other.gecko.mFont.alternateValues;
|
||||
unsafe {
|
||||
Gecko_CopyAlternateValuesFrom(&mut self.gecko.mFont, &other.gecko.mFont);
|
||||
}
|
||||
}
|
||||
|
||||
${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",
|
||||
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"
|
||||
spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates">
|
||||
use properties::longhands::system_font::SystemFont;
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
use values::CustomIdent;
|
||||
|
||||
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! {
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub flags VariantAlternates: u8 {
|
||||
pub flags ParsingFlags: u8 {
|
||||
const NORMAL = 0,
|
||||
const HISTORICAL_FORMS = 0x01,
|
||||
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 |
|
||||
/// [ stylistic(<feature-value-name>) ||
|
||||
/// historical-forms ||
|
||||
|
@ -1369,35 +1410,63 @@ ${helpers.single_keyword_system("font-kerning",
|
|||
/// annotation(<feature-value-name>) ]
|
||||
pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
|
||||
-> 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() {
|
||||
return Ok(SpecifiedValue::Value(result))
|
||||
return Ok(SpecifiedValue::Value(VariantAlternatesList(alternates.into_boxed_slice())));
|
||||
}
|
||||
|
||||
while let Ok(flag) = input.try(|input| {
|
||||
Ok(match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
|
||||
"stylistic" => STYLISTIC,
|
||||
"historical-forms" => HISTORICAL_FORMS,
|
||||
"styleset" => STYLESET,
|
||||
"character-variant" => CHARACTER_VARIANT,
|
||||
"swash" => SWASH,
|
||||
"ornaments" => ORNAMENTS,
|
||||
"annotation" => ANNOTATION,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}) {
|
||||
if result.intersects(flag) {
|
||||
return Err(StyleParseError::UnspecifiedError.into())
|
||||
let mut parsed_alternates = ParsingFlags::empty();
|
||||
macro_rules! check_if_parsed(
|
||||
($flag:ident) => (
|
||||
if parsed_alternates.contains($flag) {
|
||||
return Err(StyleParseError::UnspecifiedError.into())
|
||||
}
|
||||
parsed_alternates |= $flag;
|
||||
)
|
||||
);
|
||||
while let Ok(_) = input.try(|input| {
|
||||
match input.next()? {
|
||||
Token::Ident(ident) => {
|
||||
if ident == "historical-forms" {
|
||||
check_if_parsed!(HISTORICAL_FORMS);
|
||||
alternates.push(VariantAlternates::HistoricalForms);
|
||||
Ok(())
|
||||
} else {
|
||||
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()),
|
||||
}
|
||||
result.insert(flag);
|
||||
}
|
||||
}) { }
|
||||
|
||||
if !result.is_empty() {
|
||||
Ok(SpecifiedValue::Value(result))
|
||||
} else {
|
||||
Err(StyleParseError::UnspecifiedError.into())
|
||||
if parsed_alternates.is_empty() {
|
||||
return Err(StyleParseError::UnspecifiedError.into());
|
||||
}
|
||||
Ok(SpecifiedValue::Value(VariantAlternatesList(alternates.into_boxed_slice())))
|
||||
}
|
||||
</%helpers:longhand>
|
||||
|
||||
|
@ -2347,9 +2416,8 @@ ${helpers.single_keyword("-moz-math-variant",
|
|||
-moz-info -moz-dialog -moz-button -moz-pull-down-menu
|
||||
-moz-list -moz-field""".split()
|
||||
kw_font_props = """font_style font_variant_caps font_stretch
|
||||
font_kerning font_variant_position font_variant_alternates
|
||||
font_variant_ligatures font_variant_east_asian
|
||||
font_variant_numeric""".split()
|
||||
font_kerning font_variant_position font_variant_ligatures
|
||||
font_variant_east_asian font_variant_numeric""".split()
|
||||
kw_cast = """font_style font_variant_caps font_stretch
|
||||
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
|
||||
::T(system.languageOverride),
|
||||
font_feature_settings: longhands::font_feature_settings::get_initial_value(),
|
||||
font_variant_alternates: longhands::font_variant_alternates::get_initial_value(),
|
||||
system_font: *self,
|
||||
};
|
||||
unsafe { bindings::Gecko_nsFont_Destroy(&mut system); }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue