Bug 1367275 - Part 3: stylo: Support font-variation-settings; r?xidorn draft
authorManish Goregaokar <manishearth@gmail.com>
Tue, 23 May 2017 18:15:47 -0700
changeset 585455 b97135950bea8bf6cd6c71324459f414e2dfd0e5
parent 585454 b6443187ef2692aa99b1098151c4a8aafa26708c
child 585456 78fc31992ef3c597ce92e8daf05d282c39e85f0a
push id61109
push userbmo:manishearth@gmail.com
push dateFri, 26 May 2017 21:40:57 +0000
reviewersxidorn
bugs1367275
milestone55.0a1
Bug 1367275 - Part 3: stylo: Support font-variation-settings; r?xidorn MozReview-Commit-ID: 41xVlR8GuOr
layout/style/ServoBindings.toml
servo/components/style/gecko/generated/structs_debug.rs
servo/components/style/gecko/generated/structs_release.rs
servo/components/style/gecko/rules.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/values/generics/mod.rs
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -123,16 +123,17 @@ whitelist-types = [
     "mozilla::HalfCorner",
     "mozilla::PropertyStyleAnimationValuePair",
     "mozilla::TraversalRestyleBehavior",
     "mozilla::TraversalRootBehavior",
     "mozilla::StyleShapeRadius",
     "mozilla::StyleGrid.*",
     "mozilla::UpdateAnimationsTasks",
     "mozilla::LookAndFeel",
+    "mozilla::gfx::FontVariation",
     ".*ThreadSafe.*Holder",
     "AnonymousContent",
     "AudioContext",
     "CapturingContentInfo",
     "DefaultDelete",
     "DOMIntersectionObserverEntry",
     "Element",
     "FontFamilyList",
@@ -252,17 +253,18 @@ whitelist-types = [
     "mozilla::InheritTarget",
     "mozilla::StyleRuleInclusion",
 ]
 opaque-types = [
     "std::pair__PCCP",
     "std::namespace::atomic___base", "std::atomic__My_base",
     "std::atomic",
     "std::atomic___base",
-    "mozilla::gfx::.*",
+    # We want everything but FontVariation to be opaque but we don't have negative regexes ;_;
+    "mozilla::gfx::(.{0,12}|.{14,}|([^F][^o][^n][^t][^V][^a][^r][^i][^a][^t][^i][^o][^n]))",
     "FallibleTArray",
     "mozilla::dom::Sequence",
     "mozilla::dom::Optional",
     "mozilla::dom::Nullable",
     "RefPtr_Proxy",
     "RefPtr_Proxy_member_function",
     "nsAutoPtr_Proxy",
     "nsAutoPtr_Proxy_member_function",
--- a/servo/components/style/gecko/generated/structs_debug.rs
+++ b/servo/components/style/gecko/generated/structs_debug.rs
@@ -2383,27 +2383,39 @@ pub mod root {
             }
             pub type IntRectTyped_Super = u8;
             pub type IntRectTyped_Self = u8;
             pub type IntRectTyped_ToInt = u32;
             pub type IntRect = [u32; 4usize];
             #[repr(C)]
             #[derive(Debug, Copy)]
             pub struct FontVariation {
-                pub _bindgen_opaque_blob: [u32; 2usize],
+                pub mTag: u32,
+                pub mValue: f32,
             }
             #[test]
             fn bindgen_test_layout_FontVariation() {
                 assert_eq!(::std::mem::size_of::<FontVariation>() , 8usize ,
                            concat ! (
                            "Size of: " , stringify ! ( FontVariation ) ));
                 assert_eq! (::std::mem::align_of::<FontVariation>() , 4usize ,
                             concat ! (
                             "Alignment of " , stringify ! ( FontVariation )
                             ));
+                assert_eq! (unsafe {
+                            & ( * ( 0 as * const FontVariation ) ) . mTag as *
+                            const _ as usize } , 0usize , concat ! (
+                            "Alignment of field: " , stringify ! (
+                            FontVariation ) , "::" , stringify ! ( mTag ) ));
+                assert_eq! (unsafe {
+                            & ( * ( 0 as * const FontVariation ) ) . mValue as
+                            * const _ as usize } , 4usize , concat ! (
+                            "Alignment of field: " , stringify ! (
+                            FontVariation ) , "::" , stringify ! ( mValue )
+                            ));
             }
             impl Clone for FontVariation {
                 fn clone(&self) -> Self { *self }
             }
             pub type Matrix4x4 = [u32; 16usize];
             #[repr(C)]
             #[derive(Debug, Copy, Clone)]
             pub struct ScaleFactor {
--- a/servo/components/style/gecko/generated/structs_release.rs
+++ b/servo/components/style/gecko/generated/structs_release.rs
@@ -2293,27 +2293,39 @@ pub mod root {
             }
             pub type IntRectTyped_Super = u8;
             pub type IntRectTyped_Self = u8;
             pub type IntRectTyped_ToInt = u32;
             pub type IntRect = [u32; 4usize];
             #[repr(C)]
             #[derive(Debug, Copy)]
             pub struct FontVariation {
-                pub _bindgen_opaque_blob: [u32; 2usize],
+                pub mTag: u32,
+                pub mValue: f32,
             }
             #[test]
             fn bindgen_test_layout_FontVariation() {
                 assert_eq!(::std::mem::size_of::<FontVariation>() , 8usize ,
                            concat ! (
                            "Size of: " , stringify ! ( FontVariation ) ));
                 assert_eq! (::std::mem::align_of::<FontVariation>() , 4usize ,
                             concat ! (
                             "Alignment of " , stringify ! ( FontVariation )
                             ));
+                assert_eq! (unsafe {
+                            & ( * ( 0 as * const FontVariation ) ) . mTag as *
+                            const _ as usize } , 0usize , concat ! (
+                            "Alignment of field: " , stringify ! (
+                            FontVariation ) , "::" , stringify ! ( mTag ) ));
+                assert_eq! (unsafe {
+                            & ( * ( 0 as * const FontVariation ) ) . mValue as
+                            * const _ as usize } , 4usize , concat ! (
+                            "Alignment of field: " , stringify ! (
+                            FontVariation ) , "::" , stringify ! ( mValue )
+                            ));
             }
             impl Clone for FontVariation {
                 fn clone(&self) -> Self { *self }
             }
             pub type Matrix4x4 = [u32; 16usize];
             #[repr(C)]
             #[derive(Debug, Copy, Clone)]
             pub struct ScaleFactor {
--- a/servo/components/style/gecko/rules.rs
+++ b/servo/components/style/gecko/rules.rs
@@ -11,16 +11,17 @@ use counter_style;
 use cssparser::UnicodeRange;
 use font_face::{FontFaceRuleData, Source, FontDisplay};
 use gecko_bindings::bindings;
 use gecko_bindings::structs::{self, nsCSSFontFaceRule, nsCSSValue};
 use gecko_bindings::structs::{nsCSSCounterDesc, nsCSSCounterStyleRule};
 use gecko_bindings::sugar::ns_css_value::ToNsCssValue;
 use gecko_bindings::sugar::refptr::{RefPtr, UniqueRefPtr};
 use shared_lock::{ToCssWithGuard, SharedRwLockReadGuard};
+use values::generics::FontSettings;
 use std::{fmt, str};
 
 /// A @font-face rule
 pub type FontFaceRule = RefPtr<nsCSSFontFaceRule>;
 
 impl ToNsCssValue for FamilyName {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         nscssvalue.set_string_from_atom(&self.name)
@@ -31,26 +32,26 @@ impl ToNsCssValue for font_weight::T {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         nscssvalue.set_integer(self as i32)
     }
 }
 
 impl ToNsCssValue for font_feature_settings::T {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         match self {
-            font_feature_settings::T::Normal => nscssvalue.set_normal(),
-            font_feature_settings::T::Tag(tags) => {
+            FontSettings::Normal => nscssvalue.set_normal(),
+            FontSettings::Tag(tags) => {
                 nscssvalue.set_pair_list(tags.into_iter().map(|entry| {
                     let mut feature = nsCSSValue::null();
                     let mut raw = [0u8; 4];
                     (&mut raw[..]).write_u32::<BigEndian>(entry.tag).unwrap();
                     feature.set_string(str::from_utf8(&raw).unwrap());
 
                     let mut index = nsCSSValue::null();
-                    index.set_integer(entry.value as i32);
+                    index.set_integer(entry.value.0 as i32);
 
                     (feature, index)
                 }))
             }
         }
     }
 }
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1501,37 +1501,37 @@ fn static_assert() {
     }
 </%self:impl_trait>
 
 <%
     skip_font_longhands = """font-family font-size font-size-adjust font-weight
                              font-synthesis -x-lang font-variant-alternates
                              font-variant-east-asian font-variant-ligatures
                              font-variant-numeric font-language-override
-                             font-feature-settings"""
+                             font-feature-settings font-variation-settings"""
 %>
 <%self:impl_trait style_struct_name="Font"
     skip_longhands="${skip_font_longhands}"
     skip_additionals="*">
 
     pub fn set_font_feature_settings(&mut self, v: longhands::font_feature_settings::computed_value::T) {
-        use properties::longhands::font_feature_settings::computed_value::T;
+        use values::generics::FontSettings;
 
         let current_settings = &mut self.gecko.mFont.fontFeatureSettings;
         current_settings.clear_pod();
 
         match v {
-            T::Normal => unsafe { current_settings.set_len_pod(0) },
-
-            T::Tag(feature_settings) => {
+            FontSettings::Normal => (), // do nothing, length is already 0
+
+            FontSettings::Tag(feature_settings) => {
                 unsafe { current_settings.set_len_pod(feature_settings.len() as u32) };
 
                 for (current, feature) in current_settings.iter_mut().zip(feature_settings) {
                     current.mTag = feature.tag;
-                    current.mValue = feature.value;
+                    current.mValue = feature.value.0;
                 }
             }
         };
     }
 
     pub fn copy_font_feature_settings_from(&mut self, other: &Self ) {
         let current_settings = &mut self.gecko.mFont.fontFeatureSettings;
         let feature_settings = &other.gecko.mFont.fontFeatureSettings;
@@ -1541,16 +1541,50 @@ fn static_assert() {
         unsafe { current_settings.set_len_pod(settings_length) };
 
         for (current, feature) in current_settings.iter_mut().zip(feature_settings.iter()) {
             current.mTag = feature.mTag;
             current.mValue = feature.mValue;
         }
     }
 
+    pub fn set_font_variation_settings(&mut self, v: longhands::font_variation_settings::computed_value::T) {
+        use values::generics::FontSettings;
+
+        let current_settings = &mut self.gecko.mFont.fontVariationSettings;
+        current_settings.clear_pod();
+
+        match v {
+            FontSettings::Normal => (), // do nothing, length is already 0
+
+            FontSettings::Tag(feature_settings) => {
+                unsafe { current_settings.set_len_pod(feature_settings.len() as u32) };
+
+                for (current, feature) in current_settings.iter_mut().zip(feature_settings) {
+                    current.mTag = feature.tag;
+                    current.mValue = feature.value.0;
+                }
+            }
+        };
+    }
+
+    pub fn copy_font_variation_settings_from(&mut self, other: &Self ) {
+        let current_settings = &mut self.gecko.mFont.fontVariationSettings;
+        let feature_settings = &other.gecko.mFont.fontVariationSettings;
+        let settings_length = feature_settings.len() as u32;
+
+        current_settings.clear_pod();
+        unsafe { current_settings.set_len_pod(settings_length) };
+
+        for (current, feature) in current_settings.iter_mut().zip(feature_settings.iter()) {
+            current.mTag = feature.mTag;
+            current.mValue = feature.mValue;
+        }
+    }
+
     pub fn fixup_none_generic(&mut self, device: &Device) {
         unsafe {
             bindings::Gecko_nsStyleFont_FixupNoneGeneric(&mut self.gecko, &*device.pres_context)
         }
     }
 
     pub fn set_font_family(&mut self, v: longhands::font_family::computed_value::T) {
         use properties::longhands::font_family::computed_value::FontFamily;
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -1769,149 +1769,77 @@ macro_rules! exclusive_value {
                                 animation_value_type="none")}
 
 <%helpers:longhand name="font-feature-settings" products="gecko" animation_value_type="none"
                    extra_prefixes="moz" boxed="True"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-feature-settings">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
+    use values::generics::FontSettings;
 
     #[derive(Debug, Clone, PartialEq)]
     pub enum SpecifiedValue {
         Value(computed_value::T),
         System(SystemFont)
     }
     no_viewport_percentage!(SpecifiedValue);
 
     <%self:simple_system_boilerplate name="font_feature_settings"></%self:simple_system_boilerplate>
 
     pub mod computed_value {
-        use cssparser::Parser;
-        use parser::{Parse, ParserContext};
-        use std::fmt;
-        use style_traits::ToCss;
-
-        #[derive(Clone, Debug, Eq, PartialEq)]
-        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub enum T {
-            Normal,
-            Tag(Vec<FeatureTagValue>)
-        }
-
-        #[derive(Clone, Debug, Eq, PartialEq)]
-        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub struct FeatureTagValue {
-            pub tag: u32,
-            pub value: u32
-        }
-
-        impl ToCss for T {
-            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-                match *self {
-                    T::Normal => dest.write_str("normal"),
-                    T::Tag(ref ftvs) => {
-                        let mut iter = ftvs.iter();
-                        // handle head element
-                        try!(iter.next().unwrap().to_css(dest));
-                        // handle tail, precede each with a delimiter
-                        for ftv in iter {
-                            try!(dest.write_str(", "));
-                            try!(ftv.to_css(dest));
-                        }
-                        Ok(())
-                    }
-                }
-            }
-        }
-
-        impl Parse for T {
-            /// https://www.w3.org/TR/css-fonts-3/#propdef-font-feature-settings
-            fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-                if input.try(|i| i.expect_ident_matching("normal")).is_ok() {
-                    return Ok(T::Normal);
-                }
-                input.parse_comma_separated(|i| FeatureTagValue::parse(context, i)).map(T::Tag)
-            }
-        }
-
-        impl ToCss for FeatureTagValue {
-            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-                use std::str;
-                use byteorder::{WriteBytesExt, BigEndian};
-                use cssparser::serialize_string;
-
-                let mut raw: Vec<u8> = vec!();
-                raw.write_u32::<BigEndian>(self.tag).unwrap();
-                serialize_string(str::from_utf8(&raw).unwrap_or_default(), dest)?;
-
-                match self.value {
-                    1 => Ok(()),
-                    0 => dest.write_str(" off"),
-                    x => write!(dest, " {}", x)
-                }
-            }
-        }
-
-        impl Parse for FeatureTagValue {
-            /// https://www.w3.org/TR/css-fonts-3/#propdef-font-feature-settings
-            /// <string> [ on | off | <integer> ]
-            fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-                use std::io::Cursor;
-                use byteorder::{ReadBytesExt, BigEndian};
-
-                let tag = try!(input.expect_string());
-
-                // allowed strings of length 4 containing chars: <U+20, U+7E>
-                if tag.len() != 4 ||
-                   tag.chars().any(|c| c < ' ' || c > '~')
-                {
-                    return Err(())
-                }
-
-                let mut raw = Cursor::new(tag.as_bytes());
-                let u_tag = raw.read_u32::<BigEndian>().unwrap();
-
-                if let Ok(value) = input.try(|input| input.expect_integer()) {
-                    // handle integer, throw if it is negative
-                    if value >= 0 {
-                        Ok(FeatureTagValue { tag: u_tag, value: value as u32 })
-                    } else {
-                        Err(())
-                    }
-                } else if let Ok(_) = input.try(|input| input.expect_ident_matching("on")) {
-                    // on is an alias for '1'
-                    Ok(FeatureTagValue { tag: u_tag, value: 1 })
-                } else if let Ok(_) = input.try(|input| input.expect_ident_matching("off")) {
-                    // off is an alias for '0'
-                    Ok(FeatureTagValue { tag: u_tag, value: 0 })
-                } else {
-                    // empty value is an alias for '1'
-                    Ok(FeatureTagValue { tag: u_tag, value: 1 })
-                }
-            }
-        }
+        use values::generics::{FontSettings, FontSettingTagInt};
+        pub type T = FontSettings<FontSettingTagInt>;
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
-        computed_value::T::Normal
+        FontSettings::Normal
     }
 
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue::Value(computed_value::T::Normal)
+        SpecifiedValue::Value(FontSettings::Normal)
     }
 
     /// normal | <feature-tag-value>#
     pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         computed_value::T::parse(context, input).map(SpecifiedValue::Value)
     }
 </%helpers:longhand>
 
+<%helpers:longhand name="font-variation-settings" products="gecko" animation_value_type="none"
+                   boxed="True"
+                   spec="https://drafts.csswg.org/css-fonts-4/#low-level-font-variation-settings-control-the-font-variation-settings-property">
+    use values::computed::ComputedValueAsSpecified;
+    use values::generics::FontSettings;
+
+    impl ComputedValueAsSpecified for SpecifiedValue {}
+
+    pub type SpecifiedValue = computed_value::T;
+
+    no_viewport_percentage!(SpecifiedValue);
+
+
+    pub mod computed_value {
+        use values::generics::{FontSettings, FontSettingTagFloat};
+        pub type T = FontSettings<FontSettingTagFloat>;
+    }
+
+    #[inline]
+    pub fn get_initial_value() -> computed_value::T {
+        FontSettings::Normal
+    }
+
+    /// normal | <feature-tag-value>#
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+        computed_value::T::parse(context, input)
+    }
+</%helpers:longhand>
+
 <%helpers:longhand name="font-language-override" products="gecko" animation_value_type="none"
                    extra_prefixes="moz" boxed="True"
                    spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
     use byteorder::{BigEndian, ByteOrder};
     no_viewport_percentage!(SpecifiedValue);
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -5,17 +5,17 @@
 //! Generic types that share their serialization implementations
 //! for both specified and computed values.
 
 use counter_style::parse_counter_style_name;
 use cssparser::Parser;
 use euclid::size::Size2D;
 use parser::{Parse, ParserContext};
 use std::fmt;
-use style_traits::{HasViewportPercentage, ToCss};
+use style_traits::{HasViewportPercentage, OneOrMoreCommaSeparated, ToCss};
 use super::CustomIdent;
 
 pub use self::basic_shape::serialize_radius_values;
 
 pub mod basic_shape;
 pub mod border;
 pub mod grid;
 pub mod image;
@@ -103,8 +103,154 @@ impl ToCss for CounterStyleOrNone {
     #[inline]
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match self {
             &CounterStyleOrNone::None_ => dest.write_str("none"),
             &CounterStyleOrNone::Name(ref name) => name.to_css(dest),
         }
     }
 }
+
+/// A settings tag, defined by a four-character tag and a setting value
+///
+/// For font-feature-settings, this is a tag and an integer,
+/// for font-variation-settings this is a tag and a float
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct FontSettingTag<T> {
+    /// A four-character tag, packed into a u32 (one byte per character)
+    pub tag: u32,
+    /// The value
+    pub value: T,
+}
+
+impl<T> OneOrMoreCommaSeparated for FontSettingTag<T> {}
+
+impl<T: ToCss> ToCss for FontSettingTag<T> {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        use std::str;
+        use byteorder::{WriteBytesExt, BigEndian};
+        use cssparser::serialize_string;
+
+        let mut raw: Vec<u8> = vec!();
+        raw.write_u32::<BigEndian>(self.tag).unwrap();
+        serialize_string(str::from_utf8(&raw).unwrap_or_default(), dest)?;
+
+        self.value.to_css(dest)
+    }
+}
+
+impl<T: Parse> Parse for FontSettingTag<T> {
+    /// https://www.w3.org/TR/css-fonts-3/#propdef-font-feature-settings
+    /// https://drafts.csswg.org/css-fonts-4/#low-level-font-variation-settings-control-the-font-variation-settings-property
+    /// <string> [ on | off | <integer> ]
+    /// <string> <number>
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        use std::io::Cursor;
+        use byteorder::{ReadBytesExt, BigEndian};
+
+        let tag = try!(input.expect_string());
+
+        // allowed strings of length 4 containing chars: <U+20, U+7E>
+        if tag.len() != 4 ||
+           tag.chars().any(|c| c < ' ' || c > '~')
+        {
+            return Err(())
+        }
+
+        let mut raw = Cursor::new(tag.as_bytes());
+        let u_tag = raw.read_u32::<BigEndian>().unwrap();
+
+        Ok(FontSettingTag { tag: u_tag, value: T::parse(context, input)? })
+    }
+}
+
+
+/// A font settings value for font-variation-settings or font-feature-settings
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum FontSettings<T> {
+    /// No settings (default)
+    Normal,
+    /// Set of settings
+    Tag(Vec<FontSettingTag<T>>)
+}
+
+impl<T: ToCss> ToCss for FontSettings<T> {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        match *self {
+            FontSettings::Normal => dest.write_str("normal"),
+            FontSettings::Tag(ref ftvs) => ftvs.to_css(dest)
+        }
+    }
+}
+
+impl<T: Parse> Parse for FontSettings<T> {
+    /// https://www.w3.org/TR/css-fonts-3/#propdef-font-feature-settings
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        if input.try(|i| i.expect_ident_matching("normal")).is_ok() {
+            return Ok(FontSettings::Normal);
+        }
+        Vec::parse(context, input).map(FontSettings::Tag)
+    }
+}
+
+/// An integer that can also parse "on" and "off",
+/// for font-feature-settings
+///
+/// Do not use this type anywhere except within FontSettings
+/// because it serializes with the preceding space
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct FontSettingTagInt(pub u32);
+/// A number value to be used for font-variation-settings
+///
+/// Do not use this type anywhere except within FontSettings
+/// because it serializes with the preceding space
+#[derive(Clone, Debug, PartialEq)]
+pub struct FontSettingTagFloat(pub f32);
+
+impl ToCss for FontSettingTagInt {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        match self.0 {
+            1 => Ok(()),
+            0 => dest.write_str(" off"),
+            x => write!(dest, " {}", x)
+        }
+    }
+}
+
+impl Parse for FontSettingTagInt {
+    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        if let Ok(value) = input.try(|input| input.expect_integer()) {
+            // handle integer, throw if it is negative
+            if value >= 0 {
+                Ok(FontSettingTagInt(value as u32))
+            } else {
+                Err(())
+            }
+        } else if let Ok(_) = input.try(|input| input.expect_ident_matching("on")) {
+            // on is an alias for '1'
+            Ok(FontSettingTagInt(1))
+        } else if let Ok(_) = input.try(|input| input.expect_ident_matching("off")) {
+            // off is an alias for '0'
+            Ok(FontSettingTagInt(0))
+        } else {
+            // empty value is an alias for '1'
+            Ok(FontSettingTagInt(1))
+        }
+    }
+}
+
+
+impl Parse for FontSettingTagFloat {
+    fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        input.expect_number().map(FontSettingTagFloat)
+    }
+}
+
+impl ToCss for FontSettingTagFloat {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        dest.write_str(" ")?;
+        self.0.to_css(dest)
+    }
+}
+
+