diff options
Diffstat (limited to 'servo/components/style/values/computed/font.rs')
-rw-r--r-- | servo/components/style/values/computed/font.rs | 987 |
1 files changed, 987 insertions, 0 deletions
diff --git a/servo/components/style/values/computed/font.rs b/servo/components/style/values/computed/font.rs new file mode 100644 index 0000000000..df6a8ed667 --- /dev/null +++ b/servo/components/style/values/computed/font.rs @@ -0,0 +1,987 @@ +/* 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<H: Hasher>(&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<W>(&self, dest: &mut CssWriter<W>) -> 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<W>(&self, dest: &mut CssWriter<W>) -> 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<Self, ParseError<'i>> { + 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 <family-name> 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<structs::SharedFontList>, + ), + /// A font-family generic ID. + Generic(GenericFontFamily), +} + +#[cfg(feature = "gecko")] +impl ToShmem for FontFamilyList { + fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> { + // 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<SingleFontFamily> { + 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<GenericFontFamily> { + 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<structs::SharedFontList> { + 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<structs::SharedFontList>, + &RefPtr<structs::SharedFontList>, + >(&structs::SharedFontList_sSingleGenerics[index]) + } + }, + } + } +} + +/// Iterator of FontFamily +#[cfg(feature = "gecko")] +pub struct FontFamilyNameIter<'a> { + names: &'a structs::nsTArray<structs::FontFamilyName>, + cur: usize, +} + +#[cfg(feature = "gecko")] +impl<'a> Iterator for FontFamilyNameIter<'a> { + type Item = SingleFontFamily; + + fn next(&mut self) -> Option<Self::Item> { + 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<Self, ()> { + 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<FeatureTagValue<Integer>>; + +/// The computed value for font-variation-settings. +pub type FontVariationSettings = FontSettings<VariationValue<Number>>; + +/// 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<W>(&self, dest: &mut CssWriter<W>) -> 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<u32> for FontLanguageOverride { + fn from(v: u32) -> Self { + unsafe { Self::from_u32(v) } + } +} + +#[cfg(feature = "gecko")] +impl From<FontLanguageOverride> 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<H: Hasher>(&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<FontStyleAngle>; + +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<W>(&self, dest: &mut CssWriter<W>) -> 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<H: Hasher>(&self, hasher: &mut H) { + hasher.write_u64((self.value() * 10000.).trunc() as u64); + } +} |