/* 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/. */ //! Computed values for font properties #[cfg(feature = "gecko")] use crate::gecko_bindings::sugar::refptr::RefPtr; #[cfg(feature = "gecko")] use crate::gecko_bindings::{bindings, structs}; use crate::values::animated::{ToAnimatedValue, ToAnimatedZero}; use crate::values::computed::{ Angle, Context, Integer, Length, NonNegativeLength, NonNegativePercentage, }; use crate::values::computed::{Number, Percentage, ToComputedValue}; use crate::values::generics::font::{FeatureTagValue, FontSettings, VariationValue}; use crate::values::generics::{font as generics, NonNegative}; use crate::values::specified::font::{ self as specified, KeywordInfo, MAX_FONT_WEIGHT, MIN_FONT_WEIGHT, }; use crate::values::specified::length::{FontBaseSize, NoCalcLength}; use crate::values::CSSFloat; use crate::Atom; use cssparser::{serialize_identifier, CssStringWriter, Parser}; #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use std::fmt::{self, Write}; use std::hash::{Hash, Hasher}; #[cfg(feature = "gecko")] use std::mem::{self, ManuallyDrop}; #[cfg(feature = "servo")] use std::slice; use style_traits::{CssWriter, ParseError, ToCss}; #[cfg(feature = "gecko")] use to_shmem::{self, SharedMemoryBuilder, ToShmem}; pub use crate::values::computed::Length as MozScriptMinSize; pub use crate::values::specified::font::{FontSynthesis, MozScriptSizeMultiplier}; pub use crate::values::specified::font::{XLang, XTextZoom}; pub use crate::values::specified::Integer as SpecifiedInteger; /// A value for the font-weight property per: /// /// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight /// /// This is effectively just a `Number`. #[derive( Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, )] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] pub struct FontWeight(pub Number); impl Hash for FontWeight { fn hash(&self, hasher: &mut H) { hasher.write_u64((self.0 * 10000.).trunc() as u64); } } impl ToAnimatedValue for FontWeight { type AnimatedValue = Number; #[inline] fn to_animated_value(self) -> Self::AnimatedValue { self.0 } #[inline] fn from_animated_value(animated: Self::AnimatedValue) -> Self { FontWeight(animated.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT)) } } #[derive( Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedZero, ToCss, ToResolvedValue, )] #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))] /// The computed value of font-size pub struct FontSize { /// The size. pub size: NonNegativeLength, /// If derived from a keyword, the keyword and additional transformations applied to it #[css(skip)] pub keyword_info: KeywordInfo, } impl FontWeight { /// Value for normal pub fn normal() -> Self { FontWeight(400.) } /// Value for bold pub fn bold() -> Self { FontWeight(700.) } /// Convert from an Gecko weight #[cfg(feature = "gecko")] pub fn from_gecko_weight(weight: structs::FontWeight) -> Self { // we allow a wider range of weights than is parseable // because system fonts may provide custom values let weight = unsafe { bindings::Gecko_FontWeight_ToFloat(weight) }; FontWeight(weight) } /// Weither this weight is bold pub fn is_bold(&self) -> bool { self.0 > 500. } /// Return the bolder weight. /// /// See the table in: /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values pub fn bolder(self) -> Self { if self.0 < 350. { FontWeight(400.) } else if self.0 < 550. { FontWeight(700.) } else { FontWeight(self.0.max(900.)) } } /// Return the lighter weight. /// /// See the table in: /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values pub fn lighter(self) -> Self { if self.0 < 550. { FontWeight(self.0.min(100.)) } else if self.0 < 750. { FontWeight(400.) } else { FontWeight(700.) } } } impl FontSize { /// The actual computed font size. #[inline] pub fn size(&self) -> Length { self.size.0 } #[inline] /// Get default value of font size. pub fn medium() -> Self { Self { size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)), keyword_info: KeywordInfo::medium(), } } } impl ToAnimatedValue for FontSize { type AnimatedValue = Length; #[inline] fn to_animated_value(self) -> Self::AnimatedValue { self.size.0 } #[inline] fn from_animated_value(animated: Self::AnimatedValue) -> Self { FontSize { size: NonNegative(animated.clamp_to_non_negative()), keyword_info: KeywordInfo::none(), } } } #[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)] #[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf, Serialize, Deserialize))] /// Specifies a prioritized list of font family names or generic family names. pub struct FontFamily { /// The actual list of family names. pub families: FontFamilyList, /// Whether this font-family came from a specified system-font. pub is_system_font: bool, } impl FontFamily { #[inline] /// Get default font family as `serif` which is a generic font-family pub fn serif() -> Self { FontFamily { families: FontFamilyList::new(Box::new([SingleFontFamily::Generic( GenericFontFamily::Serif, )])), is_system_font: false, } } } #[cfg(feature = "gecko")] impl MallocSizeOf for FontFamily { fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { // SharedFontList objects are generally shared from the pointer // stored in the specified value. So only count this if the // SharedFontList is unshared. let shared_font_list = self.families.shared_font_list().get(); unsafe { bindings::Gecko_SharedFontList_SizeOfIncludingThisIfUnshared(shared_font_list) } } } impl ToCss for FontFamily { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write, { let mut iter = self.families.iter(); iter.next().unwrap().to_css(dest)?; for family in iter { dest.write_str(", ")?; family.to_css(dest)?; } Ok(()) } } #[derive( Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, )] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] /// The name of a font family of choice pub struct FamilyName { /// Name of the font family pub name: Atom, /// Syntax of the font family pub syntax: FontFamilyNameSyntax, } impl ToCss for FamilyName { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write, { match self.syntax { FontFamilyNameSyntax::Quoted => { dest.write_char('"')?; write!(CssStringWriter::new(dest), "{}", self.name)?; dest.write_char('"') }, FontFamilyNameSyntax::Identifiers => { let mut first = true; for ident in self.name.to_string().split(' ') { if first { first = false; } else { dest.write_char(' ')?; } debug_assert!( !ident.is_empty(), "Family name with leading, \ trailing, or consecutive white spaces should \ have been marked quoted by the parser" ); serialize_identifier(ident, dest)?; } Ok(()) }, } } } #[derive( Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, )] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] /// Font family names must either be given quoted as strings, /// or unquoted as a sequence of one or more identifiers. #[repr(u8)] pub enum FontFamilyNameSyntax { /// The family name was specified in a quoted form, e.g. "Font Name" /// or 'Font Name'. Quoted, /// The family name was specified in an unquoted form as a sequence of /// identifiers. Identifiers, } #[derive( Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem, )] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))] /// A set of faces that vary in weight, width or slope. pub enum SingleFontFamily { /// The name of a font family of choice. FamilyName(FamilyName), /// Generic family name. Generic(GenericFontFamily), } /// A generic font-family name. /// /// The order here is important, if you change it make sure that /// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s /// sSingleGenerics are updated as well. #[derive( Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, Parse, ToCss, ToComputedValue, ToResolvedValue, ToShmem, )] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[repr(u8)] #[allow(missing_docs)] pub enum GenericFontFamily { /// No generic family specified, only for internal usage. /// /// NOTE(emilio): Gecko code relies on this variant being zero. #[css(skip)] None = 0, Serif, SansSerif, #[parse(aliases = "-moz-fixed")] Monospace, Cursive, Fantasy, /// An internal value for emoji font selection. #[css(skip)] #[cfg(feature = "gecko")] MozEmoji, } impl SingleFontFamily { /// Parse a font-family value. pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) { return Ok(SingleFontFamily::FamilyName(FamilyName { name: Atom::from(&*value), syntax: FontFamilyNameSyntax::Quoted, })); } let first_ident = input.expect_ident_cloned()?; if let Ok(generic) = GenericFontFamily::from_ident(&first_ident) { return Ok(SingleFontFamily::Generic(generic)); } let reserved = match_ignore_ascii_case! { &first_ident, // https://drafts.csswg.org/css-fonts/#propdef-font-family // "Font family names that happen to be the same as a keyword value // (`inherit`, `serif`, `sans-serif`, `monospace`, `fantasy`, and `cursive`) // must be quoted to prevent confusion with the keywords with the same names. // The keywords ‘initial’ and ‘default’ are reserved for future use // and must also be quoted when used as font names. // UAs must not consider these keywords as matching the type." "inherit" | "initial" | "unset" | "revert" | "default" => true, _ => false, }; let mut value = first_ident.as_ref().to_owned(); let mut serialize_quoted = value.contains(' '); // These keywords are not allowed by themselves. // The only way this value can be valid with with another keyword. if reserved { let ident = input.expect_ident()?; serialize_quoted = serialize_quoted || ident.contains(' '); value.push(' '); value.push_str(&ident); } while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) { serialize_quoted = serialize_quoted || ident.contains(' '); value.push(' '); value.push_str(&ident); } let syntax = if serialize_quoted { // For font family names which contains special white spaces, e.g. // `font-family: \ a\ \ b\ \ c\ ;`, it is tricky to serialize them // as identifiers correctly. Just mark them quoted so we don't need // to worry about them in serialization code. FontFamilyNameSyntax::Quoted } else { FontFamilyNameSyntax::Identifiers }; Ok(SingleFontFamily::FamilyName(FamilyName { name: Atom::from(value), syntax, })) } #[cfg(feature = "servo")] /// Get the corresponding font-family with Atom pub fn from_atom(input: Atom) -> SingleFontFamily { match input { atom!("serif") => return SingleFontFamily::Generic(GenericFontFamily::Serif), atom!("sans-serif") => return SingleFontFamily::Generic(GenericFontFamily::SansSerif), atom!("cursive") => return SingleFontFamily::Generic(GenericFontFamily::Cursive), atom!("fantasy") => return SingleFontFamily::Generic(GenericFontFamily::Fantasy), atom!("monospace") => return SingleFontFamily::Generic(GenericFontFamily::Monospace), _ => {}, } match_ignore_ascii_case! { &input, "serif" => return SingleFontFamily::Generic(GenericFontFamily::Serif), "sans-serif" => return SingleFontFamily::Generic(GenericFontFamily::SansSerif), "cursive" => return SingleFontFamily::Generic(GenericFontFamily::Cursive), "fantasy" => return SingleFontFamily::Generic(GenericFontFamily::Fantasy), "monospace" => return SingleFontFamily::Generic(GenericFontFamily::Monospace), _ => {} } // We don't know if it's quoted or not. So we set it to // quoted by default. SingleFontFamily::FamilyName(FamilyName { name: input, syntax: FontFamilyNameSyntax::Quoted, }) } #[cfg(feature = "gecko")] /// Get the corresponding font-family with family name fn from_font_family_name(family: &structs::FontFamilyName) -> SingleFontFamily { if family.mName.mRawPtr.is_null() { debug_assert_ne!(family.mGeneric, GenericFontFamily::None); return SingleFontFamily::Generic(family.mGeneric); } let name = unsafe { Atom::from_raw(family.mName.mRawPtr) }; SingleFontFamily::FamilyName(FamilyName { name, syntax: family.mSyntax, }) } } #[cfg(feature = "servo")] #[derive( Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, ToComputedValue, ToResolvedValue, ToShmem, )] /// A list of SingleFontFamily pub struct FontFamilyList(Box<[SingleFontFamily]>); #[cfg(feature = "gecko")] #[derive(Clone, Debug, ToComputedValue, ToResolvedValue)] /// A list of SingleFontFamily pub enum FontFamilyList { /// A strong reference to a Gecko SharedFontList object. SharedFontList( #[compute(no_field_bound)] #[resolve(no_field_bound)] RefPtr, ), /// A font-family generic ID. Generic(GenericFontFamily), } #[cfg(feature = "gecko")] impl ToShmem for FontFamilyList { fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result { // In practice, the only SharedFontList objects we create from shared // style sheets are ones with a single generic entry. Ok(ManuallyDrop::new(match *self { FontFamilyList::SharedFontList(ref r) => { if !(r.mNames.len() == 1 && r.mNames[0].mName.mRawPtr.is_null()) { return Err(String::from( "ToShmem failed for FontFamilyList: cannot handle non-generic families", )); } FontFamilyList::Generic(r.mNames[0].mGeneric) }, FontFamilyList::Generic(t) => FontFamilyList::Generic(t), })) } } #[cfg(feature = "gecko")] impl PartialEq for FontFamilyList { fn eq(&self, other: &FontFamilyList) -> bool { let self_list = self.shared_font_list(); let other_list = other.shared_font_list(); if self_list.mNames.len() != other_list.mNames.len() { return false; } for (a, b) in self_list.mNames.iter().zip(other_list.mNames.iter()) { if a.mSyntax != b.mSyntax || a.mName.mRawPtr != b.mName.mRawPtr || a.mGeneric != b.mGeneric { return false; } } true } } #[cfg(feature = "gecko")] impl Eq for FontFamilyList {} impl FontFamilyList { /// Return FontFamilyList with a vector of SingleFontFamily #[cfg(feature = "servo")] pub fn new(families: Box<[SingleFontFamily]>) -> FontFamilyList { FontFamilyList(families) } /// Return FontFamilyList with a vector of SingleFontFamily #[cfg(feature = "gecko")] pub fn new(families: Box<[SingleFontFamily]>) -> FontFamilyList { let fontlist; let names; unsafe { fontlist = bindings::Gecko_SharedFontList_Create(); names = &mut (*fontlist).mNames; names.ensure_capacity(families.len()); }; for family in families.iter() { match *family { SingleFontFamily::FamilyName(ref f) => unsafe { bindings::Gecko_nsTArray_FontFamilyName_AppendNamed( names, f.name.as_ptr(), f.syntax, ); }, SingleFontFamily::Generic(family) => unsafe { bindings::Gecko_nsTArray_FontFamilyName_AppendGeneric(names, family); }, } } FontFamilyList::SharedFontList(unsafe { RefPtr::from_addrefed(fontlist) }) } /// Return iterator of SingleFontFamily #[cfg(feature = "servo")] pub fn iter(&self) -> slice::Iter { self.0.iter() } /// Return iterator of SingleFontFamily #[cfg(feature = "gecko")] pub fn iter(&self) -> FontFamilyNameIter { FontFamilyNameIter { names: &self.shared_font_list().mNames, cur: 0, } } /// Return the generic ID if it is a single generic font pub fn single_generic(&self) -> Option { let mut iter = self.iter(); if let Some(SingleFontFamily::Generic(f)) = iter.next() { if iter.next().is_none() { return Some(f.clone()); } } None } /// Return a reference to the Gecko SharedFontList. #[cfg(feature = "gecko")] pub fn shared_font_list(&self) -> &RefPtr { match *self { FontFamilyList::SharedFontList(ref r) => r, FontFamilyList::Generic(t) => { unsafe { // TODO(heycam): Should really add StaticRefPtr sugar. let index = t as usize; mem::transmute::< &structs::StaticRefPtr, &RefPtr, >(&structs::SharedFontList_sSingleGenerics[index]) } }, } } } /// Iterator of FontFamily #[cfg(feature = "gecko")] pub struct FontFamilyNameIter<'a> { names: &'a structs::nsTArray, cur: usize, } #[cfg(feature = "gecko")] impl<'a> Iterator for FontFamilyNameIter<'a> { type Item = SingleFontFamily; fn next(&mut self) -> Option { if self.cur < self.names.len() { let item = SingleFontFamily::from_font_family_name(&self.names[self.cur]); self.cur += 1; Some(item) } else { None } } } /// Preserve the readability of text when font fallback occurs #[derive( Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, )] pub enum FontSizeAdjust { #[animation(error)] /// None variant None, /// Number variant Number(CSSFloat), } impl FontSizeAdjust { #[inline] /// Default value of font-size-adjust pub fn none() -> Self { FontSizeAdjust::None } /// Get font-size-adjust with float number pub fn from_gecko_adjust(gecko: f32) -> Self { if gecko == -1.0 { FontSizeAdjust::None } else { FontSizeAdjust::Number(gecko) } } } impl ToAnimatedZero for FontSizeAdjust { #[inline] // FIXME(emilio): why? fn to_animated_zero(&self) -> Result { Err(()) } } impl ToAnimatedValue for FontSizeAdjust { type AnimatedValue = Self; #[inline] fn to_animated_value(self) -> Self { self } #[inline] fn from_animated_value(animated: Self::AnimatedValue) -> Self { match animated { FontSizeAdjust::Number(number) => FontSizeAdjust::Number(number.max(0.)), _ => animated, } } } /// Use VariantAlternatesList as computed type of FontVariantAlternates pub type FontVariantAlternates = specified::VariantAlternatesList; impl FontVariantAlternates { /// Get initial value with VariantAlternatesList #[inline] pub fn get_initial_value() -> Self { Self::default() } } /// Use VariantEastAsian as computed type of FontVariantEastAsian pub type FontVariantEastAsian = specified::VariantEastAsian; /// Use VariantLigatures as computed type of FontVariantLigatures pub type FontVariantLigatures = specified::VariantLigatures; /// Use VariantNumeric as computed type of FontVariantNumeric pub type FontVariantNumeric = specified::VariantNumeric; /// Use FontSettings as computed type of FontFeatureSettings. pub type FontFeatureSettings = FontSettings>; /// The computed value for font-variation-settings. pub type FontVariationSettings = FontSettings>; /// font-language-override can only have a single three-letter /// OpenType "language system" tag, so we should be able to compute /// it and store it as a 32-bit integer /// (see http://www.microsoft.com/typography/otspec/languagetags.htm). #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToResolvedValue)] #[repr(C)] pub struct FontLanguageOverride(pub u32); impl FontLanguageOverride { #[inline] /// Get computed default value of `font-language-override` with 0 pub fn zero() -> FontLanguageOverride { FontLanguageOverride(0) } /// Returns this value as a `&str`, backed by `storage`. #[inline] pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str { *storage = u32::to_be_bytes(self.0); // Safe because we ensure it's ASCII during computing let slice = if cfg!(debug_assertions) { std::str::from_utf8(&storage[..]).unwrap() } else { unsafe { std::str::from_utf8_unchecked(&storage[..]) } }; slice.trim_end() } /// Parses a str, return `Self::zero()` if the input isn't a valid OpenType /// "language system" tag. #[inline] pub fn from_str(lang: &str) -> Self { if lang.is_empty() || lang.len() > 4 { return Self::zero(); } let mut bytes = [b' '; 4]; for (byte, lang_byte) in bytes.iter_mut().zip(lang.as_bytes()) { if !lang_byte.is_ascii() { return Self::zero(); } *byte = *lang_byte; } Self(u32::from_be_bytes(bytes)) } /// Unsafe because `Self::to_str` requires the value to represent a UTF-8 /// string. #[inline] pub unsafe fn from_u32(value: u32) -> Self { Self(value) } } impl ToCss for FontLanguageOverride { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write, { if self.0 == 0 { return dest.write_str("normal"); } self.to_str(&mut [0; 4]).to_css(dest) } } // FIXME(emilio): Make Gecko use the cbindgen'd fontLanguageOverride, then // remove this. #[cfg(feature = "gecko")] impl From for FontLanguageOverride { fn from(v: u32) -> Self { unsafe { Self::from_u32(v) } } } #[cfg(feature = "gecko")] impl From for u32 { fn from(v: FontLanguageOverride) -> u32 { v.0 } } impl ToComputedValue for specified::MozScriptMinSize { type ComputedValue = MozScriptMinSize; fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize { // this value is used in the computation of font-size, so // we use the parent size let base_size = FontBaseSize::InheritedStyle; match self.0 { NoCalcLength::FontRelative(value) => value.to_computed_value(cx, base_size), NoCalcLength::ServoCharacterWidth(value) => { value.to_computed_value(base_size.resolve(cx)) }, ref l => l.to_computed_value(cx), } } fn from_computed_value(other: &MozScriptMinSize) -> Self { specified::MozScriptMinSize(ToComputedValue::from_computed_value(other)) } } /// The computed value of the math-depth property. pub type MathDepth = i8; #[cfg(feature = "gecko")] impl ToComputedValue for specified::MathDepth { type ComputedValue = MathDepth; fn to_computed_value(&self, cx: &Context) -> i8 { use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue; use std::{cmp, i8}; let int = match *self { specified::MathDepth::AutoAdd => { let parent = cx.builder.get_parent_font().clone_math_depth() as i32; let style = cx.builder.get_parent_font().clone_math_style(); if style == MathStyleValue::Compact { parent + 1 } else { parent } }, specified::MathDepth::Add(rel) => { let parent = cx.builder.get_parent_font().clone_math_depth(); parent as i32 + rel.to_computed_value(cx) }, specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx), }; cmp::min(int, i8::MAX as i32) as i8 } fn from_computed_value(other: &i8) -> Self { let computed_value = *other as i32; specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value)) } } /// A wrapper over an `Angle`, that handles clamping to the appropriate range /// for `font-style` animation. #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] pub struct FontStyleAngle(pub Angle); impl ToAnimatedValue for FontStyleAngle { type AnimatedValue = Angle; #[inline] fn to_animated_value(self) -> Self::AnimatedValue { self.0 } #[inline] fn from_animated_value(animated: Self::AnimatedValue) -> Self { FontStyleAngle(Angle::from_degrees( animated .degrees() .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES) .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES), )) } } impl Hash for FontStyleAngle { fn hash(&self, hasher: &mut H) { hasher.write_u64((self.0.degrees() * 10000.).trunc() as u64); } } /// The computed value of `font-style`. /// /// FIXME(emilio): Angle should be a custom type to handle clamping during /// animation. pub type FontStyle = generics::FontStyle; impl FontStyle { /// The `normal` value. #[inline] pub fn normal() -> Self { generics::FontStyle::Normal } /// The default angle for font-style: oblique. This is 20deg per spec: /// /// https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle #[inline] pub fn default_angle() -> FontStyleAngle { FontStyleAngle(Angle::from_degrees( specified::DEFAULT_FONT_STYLE_OBLIQUE_ANGLE_DEGREES, )) } /// Get the font style from Gecko's nsFont struct. #[cfg(feature = "gecko")] pub fn from_gecko(style: structs::FontSlantStyle) -> Self { let mut angle = 0.; let mut italic = false; let mut normal = false; unsafe { bindings::Gecko_FontSlantStyle_Get(style, &mut normal, &mut italic, &mut angle); } if normal { return generics::FontStyle::Normal; } if italic { return generics::FontStyle::Italic; } generics::FontStyle::Oblique(FontStyleAngle(Angle::from_degrees(angle))) } } impl ToCss for FontStyle { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write, { match *self { generics::FontStyle::Normal => dest.write_str("normal"), generics::FontStyle::Italic => dest.write_str("italic"), generics::FontStyle::Oblique(ref angle) => { dest.write_str("oblique")?; // Use `degrees` instead of just comparing Angle because // `degrees` can return slightly different values due to // floating point conversions. if angle.0.degrees() != Self::default_angle().0.degrees() { dest.write_char(' ')?; angle.to_css(dest)?; } Ok(()) }, } } } /// A value for the font-stretch property per: /// /// https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch #[derive( Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, )] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] pub struct FontStretch(pub NonNegativePercentage); impl FontStretch { /// 100% pub fn hundred() -> Self { FontStretch(NonNegativePercentage::hundred()) } /// The float value of the percentage #[inline] pub fn value(&self) -> CSSFloat { ((self.0).0).0 } } impl ToAnimatedValue for FontStretch { type AnimatedValue = Percentage; #[inline] fn to_animated_value(self) -> Self::AnimatedValue { self.0.to_animated_value() } #[inline] fn from_animated_value(animated: Self::AnimatedValue) -> Self { FontStretch(NonNegativePercentage::from_animated_value(animated)) } } impl Hash for FontStretch { fn hash(&self, hasher: &mut H) { hasher.write_u64((self.value() * 10000.).trunc() as u64); } }