summaryrefslogtreecommitdiffstats
path: root/servo/components/style/values/computed/font.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/values/computed/font.rs')
-rw-r--r--servo/components/style/values/computed/font.rs987
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);
+ }
+}