1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 //! Computed values for font properties
6 
7 #[cfg(feature = "gecko")]
8 use crate::gecko_bindings::{bindings, structs};
9 use crate::values::animated::ToAnimatedValue;
10 use crate::values::computed::{
11     Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, NonNegativePercentage,
12 };
13 use crate::values::computed::{Number, Percentage, ToComputedValue};
14 use crate::values::generics::font::{FeatureTagValue, FontSettings, VariationValue};
15 use crate::values::generics::{font as generics, NonNegative};
16 use crate::values::specified::font::{
17     self as specified, KeywordInfo, MAX_FONT_WEIGHT, MIN_FONT_WEIGHT,
18 };
19 use crate::values::specified::length::{FontBaseSize, NoCalcLength};
20 use crate::values::CSSFloat;
21 use crate::Atom;
22 use cssparser::{serialize_identifier, CssStringWriter, Parser};
23 #[cfg(feature = "gecko")]
24 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
25 use std::fmt::{self, Write};
26 use std::hash::{Hash, Hasher};
27 use style_traits::{CssWriter, ParseError, ToCss};
28 
29 pub use crate::values::computed::Length as MozScriptMinSize;
30 pub use crate::values::specified::font::{FontSynthesis, MozScriptSizeMultiplier};
31 pub use crate::values::specified::font::{XLang, XTextZoom};
32 pub use crate::values::specified::Integer as SpecifiedInteger;
33 
34 /// A value for the font-weight property per:
35 ///
36 /// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
37 ///
38 /// This is effectively just a `Number`.
39 #[derive(
40     Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue,
41 )]
42 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
43 pub struct FontWeight(pub Number);
44 
45 impl Hash for FontWeight {
hash<H: Hasher>(&self, hasher: &mut H)46     fn hash<H: Hasher>(&self, hasher: &mut H) {
47         hasher.write_u64((self.0 * 10000.).trunc() as u64);
48     }
49 }
50 
51 impl ToAnimatedValue for FontWeight {
52     type AnimatedValue = Number;
53 
54     #[inline]
to_animated_value(self) -> Self::AnimatedValue55     fn to_animated_value(self) -> Self::AnimatedValue {
56         self.0
57     }
58 
59     #[inline]
from_animated_value(animated: Self::AnimatedValue) -> Self60     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
61         FontWeight(animated.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT))
62     }
63 }
64 
65 #[derive(
66     Animate,
67     Clone,
68     ComputeSquaredDistance,
69     Copy,
70     Debug,
71     MallocSizeOf,
72     PartialEq,
73     ToAnimatedZero,
74     ToCss,
75     ToResolvedValue,
76 )]
77 #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
78 /// The computed value of font-size
79 pub struct FontSize {
80     /// The size.
81     pub size: NonNegativeLength,
82     /// If derived from a keyword, the keyword and additional transformations applied to it
83     #[css(skip)]
84     pub keyword_info: KeywordInfo,
85 }
86 
87 impl FontWeight {
88     /// Value for normal
normal() -> Self89     pub fn normal() -> Self {
90         FontWeight(400.)
91     }
92 
93     /// Value for bold
bold() -> Self94     pub fn bold() -> Self {
95         FontWeight(700.)
96     }
97 
98     /// Convert from an Gecko weight
99     #[cfg(feature = "gecko")]
from_gecko_weight(weight: structs::FontWeight) -> Self100     pub fn from_gecko_weight(weight: structs::FontWeight) -> Self {
101         // we allow a wider range of weights than is parseable
102         // because system fonts may provide custom values
103         let weight = unsafe { bindings::Gecko_FontWeight_ToFloat(weight) };
104         FontWeight(weight)
105     }
106 
107     /// Weither this weight is bold
is_bold(&self) -> bool108     pub fn is_bold(&self) -> bool {
109         self.0 > 500.
110     }
111 
112     /// Return the bolder weight.
113     ///
114     /// See the table in:
115     /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
bolder(self) -> Self116     pub fn bolder(self) -> Self {
117         if self.0 < 350. {
118             FontWeight(400.)
119         } else if self.0 < 550. {
120             FontWeight(700.)
121         } else {
122             FontWeight(self.0.max(900.))
123         }
124     }
125 
126     /// Return the lighter weight.
127     ///
128     /// See the table in:
129     /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
lighter(self) -> Self130     pub fn lighter(self) -> Self {
131         if self.0 < 550. {
132             FontWeight(self.0.min(100.))
133         } else if self.0 < 750. {
134             FontWeight(400.)
135         } else {
136             FontWeight(700.)
137         }
138     }
139 }
140 
141 impl FontSize {
142     /// The actual computed font size.
143     #[inline]
size(&self) -> Length144     pub fn size(&self) -> Length {
145         self.size.0
146     }
147 
148     #[inline]
149     /// Get default value of font size.
medium() -> Self150     pub fn medium() -> Self {
151         Self {
152             size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
153             keyword_info: KeywordInfo::medium(),
154         }
155     }
156 }
157 
158 impl ToAnimatedValue for FontSize {
159     type AnimatedValue = Length;
160 
161     #[inline]
to_animated_value(self) -> Self::AnimatedValue162     fn to_animated_value(self) -> Self::AnimatedValue {
163         self.size.0
164     }
165 
166     #[inline]
from_animated_value(animated: Self::AnimatedValue) -> Self167     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
168         FontSize {
169             size: NonNegative(animated.clamp_to_non_negative()),
170             keyword_info: KeywordInfo::none(),
171         }
172     }
173 }
174 
175 #[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)]
176 #[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf, Serialize, Deserialize))]
177 /// Specifies a prioritized list of font family names or generic family names.
178 #[repr(C)]
179 pub struct FontFamily {
180     /// The actual list of family names.
181     pub families: FontFamilyList,
182     /// Whether this font-family came from a specified system-font.
183     pub is_system_font: bool,
184 }
185 
186 macro_rules! static_font_family {
187     ($ident:ident, $family:expr) => {
188         lazy_static! {
189             static ref $ident: FontFamily = FontFamily {
190                 families: FontFamilyList {
191                     list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
192                     fallback: GenericFontFamily::None,
193                 },
194                 is_system_font: false,
195             };
196         }
197     };
198 }
199 
200 
201 impl FontFamily {
202     #[inline]
203     /// Get default font family as `serif` which is a generic font-family
serif() -> Self204     pub fn serif() -> Self {
205         Self::generic(GenericFontFamily::Serif).clone()
206     }
207 
208     /// Returns the font family for `-moz-bullet-font`.
moz_bullet() -> &'static Self209     pub(crate) fn moz_bullet() -> &'static Self {
210         static_font_family!(MOZ_BULLET, SingleFontFamily::FamilyName(FamilyName {
211             name: atom!("-moz-bullet-font"),
212             syntax: FontFamilyNameSyntax::Identifiers,
213         }));
214 
215         &*MOZ_BULLET
216     }
217 
218     /// Returns a font family for a single system font.
for_system_font(name: &str) -> Self219     pub fn for_system_font(name: &str) -> Self {
220         Self {
221             families: FontFamilyList {
222                 list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(FamilyName {
223                     name: Atom::from(name),
224                     syntax: FontFamilyNameSyntax::Identifiers,
225                 }))),
226                 fallback: GenericFontFamily::None,
227             },
228             is_system_font: true,
229         }
230     }
231 
232     /// Returns a generic font family.
generic(generic: GenericFontFamily) -> &'static Self233     pub fn generic(generic: GenericFontFamily) -> &'static Self {
234         macro_rules! generic_font_family {
235             ($ident:ident, $family:ident) => {
236                 static_font_family!($ident, SingleFontFamily::Generic(GenericFontFamily::$family))
237             }
238         }
239 
240         generic_font_family!(SERIF, Serif);
241         generic_font_family!(SANS_SERIF, SansSerif);
242         generic_font_family!(MONOSPACE, Monospace);
243         generic_font_family!(CURSIVE, Cursive);
244         generic_font_family!(FANTASY, Fantasy);
245         generic_font_family!(MOZ_EMOJI, MozEmoji);
246 
247         match generic {
248             GenericFontFamily::None => {
249                 debug_assert!(false, "Bogus caller!");
250                 &*SERIF
251             }
252             GenericFontFamily::Serif => &*SERIF,
253             GenericFontFamily::SansSerif => &*SANS_SERIF,
254             GenericFontFamily::Monospace => &*MONOSPACE,
255             GenericFontFamily::Cursive => &*CURSIVE,
256             GenericFontFamily::Fantasy => &*FANTASY,
257             GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
258         }
259     }
260 }
261 
262 #[cfg(feature = "gecko")]
263 impl MallocSizeOf for FontFamily {
size_of(&self, ops: &mut MallocSizeOfOps) -> usize264     fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
265         use malloc_size_of::MallocUnconditionalSizeOf;
266         // SharedFontList objects are generally measured from the pointer stored
267         // in the specified value. So only count this if the SharedFontList is
268         // unshared.
269         let shared_font_list = &self.families.list;
270         if shared_font_list.is_unique() {
271             shared_font_list.unconditional_size_of(ops)
272         } else {
273             0
274         }
275     }
276 }
277 
278 impl ToCss for FontFamily {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write,279     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
280     where
281         W: fmt::Write,
282     {
283         let mut iter = self.families.iter();
284         match iter.next() {
285             Some(f) => f.to_css(dest)?,
286             None => return self.families.fallback.to_css(dest),
287         }
288         for family in iter {
289             dest.write_str(", ")?;
290             family.to_css(dest)?;
291         }
292         Ok(())
293     }
294 }
295 
296 /// The name of a font family of choice.
297 #[derive(
298     Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
299 )]
300 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
301 #[repr(C)]
302 pub struct FamilyName {
303     /// Name of the font family.
304     pub name: Atom,
305     /// Syntax of the font family.
306     pub syntax: FontFamilyNameSyntax,
307 }
308 
309 impl ToCss for FamilyName {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write,310     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
311     where
312         W: fmt::Write,
313     {
314         match self.syntax {
315             FontFamilyNameSyntax::Quoted => {
316                 dest.write_char('"')?;
317                 write!(CssStringWriter::new(dest), "{}", self.name)?;
318                 dest.write_char('"')
319             },
320             FontFamilyNameSyntax::Identifiers => {
321                 let mut first = true;
322                 for ident in self.name.to_string().split(' ') {
323                     if first {
324                         first = false;
325                     } else {
326                         dest.write_char(' ')?;
327                     }
328                     debug_assert!(
329                         !ident.is_empty(),
330                         "Family name with leading, \
331                          trailing, or consecutive white spaces should \
332                          have been marked quoted by the parser"
333                     );
334                     serialize_identifier(ident, dest)?;
335                 }
336                 Ok(())
337             },
338         }
339     }
340 }
341 
342 #[derive(
343     Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
344 )]
345 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
346 /// Font family names must either be given quoted as strings,
347 /// or unquoted as a sequence of one or more identifiers.
348 #[repr(u8)]
349 pub enum FontFamilyNameSyntax {
350     /// The family name was specified in a quoted form, e.g. "Font Name"
351     /// or 'Font Name'.
352     Quoted,
353 
354     /// The family name was specified in an unquoted form as a sequence of
355     /// identifiers.
356     Identifiers,
357 }
358 
359 /// A set of faces that vary in weight, width or slope.
360 /// cbindgen:derive-mut-casts=true
361 #[derive(
362     Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
363 )]
364 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
365 #[repr(u8)]
366 pub enum SingleFontFamily {
367     /// The name of a font family of choice.
368     FamilyName(FamilyName),
369     /// Generic family name.
370     Generic(GenericFontFamily),
371 }
372 
373 /// A generic font-family name.
374 ///
375 /// The order here is important, if you change it make sure that
376 /// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s
377 /// sSingleGenerics are updated as well.
378 ///
379 /// NOTE(emilio): Should be u8, but it's a u32 because of ABI issues between GCC
380 /// and LLVM see https://bugs.llvm.org/show_bug.cgi?id=44228 / bug 1600735 /
381 /// bug 1726515.
382 #[derive(
383     Clone,
384     Copy,
385     Debug,
386     Eq,
387     Hash,
388     MallocSizeOf,
389     PartialEq,
390     Parse,
391     ToCss,
392     ToComputedValue,
393     ToResolvedValue,
394     ToShmem,
395 )]
396 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
397 #[repr(u32)]
398 #[allow(missing_docs)]
399 pub enum GenericFontFamily {
400     /// No generic family specified, only for internal usage.
401     ///
402     /// NOTE(emilio): Gecko code relies on this variant being zero.
403     #[css(skip)]
404     None = 0,
405     Serif,
406     SansSerif,
407     #[parse(aliases = "-moz-fixed")]
408     Monospace,
409     Cursive,
410     Fantasy,
411     /// An internal value for emoji font selection.
412     #[css(skip)]
413     #[cfg(feature = "gecko")]
414     MozEmoji,
415 }
416 
417 impl SingleFontFamily {
418     /// Parse a font-family value.
parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>>419     pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
420         if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
421             return Ok(SingleFontFamily::FamilyName(FamilyName {
422                 name: Atom::from(&*value),
423                 syntax: FontFamilyNameSyntax::Quoted,
424             }));
425         }
426 
427         let first_ident = input.expect_ident_cloned()?;
428         if let Ok(generic) = GenericFontFamily::from_ident(&first_ident) {
429             return Ok(SingleFontFamily::Generic(generic));
430         }
431 
432         let reserved = match_ignore_ascii_case! { &first_ident,
433             // https://drafts.csswg.org/css-fonts/#propdef-font-family
434             // "Font family names that happen to be the same as a keyword value
435             //  (`inherit`, `serif`, `sans-serif`, `monospace`, `fantasy`, and `cursive`)
436             //  must be quoted to prevent confusion with the keywords with the same names.
437             //  The keywords ‘initial’ and ‘default’ are reserved for future use
438             //  and must also be quoted when used as font names.
439             //  UAs must not consider these keywords as matching the <family-name> type."
440             "inherit" | "initial" | "unset" | "revert" | "default" => true,
441             _ => false,
442         };
443 
444         let mut value = first_ident.as_ref().to_owned();
445         let mut serialize_quoted = value.contains(' ');
446 
447         // These keywords are not allowed by themselves.
448         // The only way this value can be valid with with another keyword.
449         if reserved {
450             let ident = input.expect_ident()?;
451             serialize_quoted = serialize_quoted || ident.contains(' ');
452             value.push(' ');
453             value.push_str(&ident);
454         }
455         while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
456             serialize_quoted = serialize_quoted || ident.contains(' ');
457             value.push(' ');
458             value.push_str(&ident);
459         }
460         let syntax = if serialize_quoted {
461             // For font family names which contains special white spaces, e.g.
462             // `font-family: \ a\ \ b\ \ c\ ;`, it is tricky to serialize them
463             // as identifiers correctly. Just mark them quoted so we don't need
464             // to worry about them in serialization code.
465             FontFamilyNameSyntax::Quoted
466         } else {
467             FontFamilyNameSyntax::Identifiers
468         };
469         Ok(SingleFontFamily::FamilyName(FamilyName {
470             name: Atom::from(value),
471             syntax,
472         }))
473     }
474 
475     #[cfg(feature = "servo")]
476     /// Get the corresponding font-family with Atom
from_atom(input: Atom) -> SingleFontFamily477     pub fn from_atom(input: Atom) -> SingleFontFamily {
478         match input {
479             atom!("serif") => return SingleFontFamily::Generic(GenericFontFamily::Serif),
480             atom!("sans-serif") => return SingleFontFamily::Generic(GenericFontFamily::SansSerif),
481             atom!("cursive") => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
482             atom!("fantasy") => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
483             atom!("monospace") => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
484             _ => {},
485         }
486 
487         match_ignore_ascii_case! { &input,
488             "serif" => return SingleFontFamily::Generic(GenericFontFamily::Serif),
489             "sans-serif" => return SingleFontFamily::Generic(GenericFontFamily::SansSerif),
490             "cursive" => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
491             "fantasy" => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
492             "monospace" => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
493             _ => {}
494         }
495 
496         // We don't know if it's quoted or not. So we set it to
497         // quoted by default.
498         SingleFontFamily::FamilyName(FamilyName {
499             name: input,
500             syntax: FontFamilyNameSyntax::Quoted,
501         })
502     }
503 }
504 
505 /// A list of font families.
506 #[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
507 #[repr(C)]
508 pub struct FontFamilyList {
509     /// The actual list of font families specified.
510     pub list: crate::ArcSlice<SingleFontFamily>,
511     /// A fallback font type (none, serif, or sans-serif, generally).
512     pub fallback: GenericFontFamily,
513 }
514 
515 impl FontFamilyList {
516     /// Return iterator of SingleFontFamily
iter(&self) -> impl Iterator<Item = &SingleFontFamily>517     pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
518         self.list.iter()
519     }
520 
521     /// Puts the fallback in the list if needed.
normalize(&mut self)522     pub fn normalize(&mut self) {
523         if self.fallback == GenericFontFamily::None {
524             return;
525         }
526         let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
527         new_list.push(SingleFontFamily::Generic(self.fallback));
528         self.list = crate::ArcSlice::from_iter(new_list.into_iter());
529     }
530 
531     /// If there's a generic font family on the list (which isn't cursive or
532     /// fantasy), then move it to the front of the list. Otherwise, prepend the
533     /// default generic.
prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily)534     pub (crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
535         let index_of_first_generic = self.iter().position(|f| {
536             match *f {
537                 SingleFontFamily::Generic(f) => f != GenericFontFamily::Cursive && f != GenericFontFamily::Fantasy,
538                 _ => false,
539             }
540         });
541 
542         if let Some(0) = index_of_first_generic {
543             return; // Already first
544         }
545 
546         let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
547         let element_to_prepend = match index_of_first_generic {
548             Some(i) => new_list.remove(i),
549             None => SingleFontFamily::Generic(generic),
550         };
551 
552         new_list.insert(0, element_to_prepend);
553         self.list = crate::ArcSlice::from_iter(new_list.into_iter());
554     }
555 
556     /// Return the generic ID if it is a single generic font
single_generic(&self) -> Option<GenericFontFamily>557     pub fn single_generic(&self) -> Option<GenericFontFamily> {
558         let mut iter = self.iter();
559         if let Some(SingleFontFamily::Generic(f)) = iter.next() {
560             if iter.next().is_none() {
561                 return Some(f.clone());
562             }
563         }
564         None
565     }
566 }
567 
568 /// Preserve the readability of text when font fallback occurs
569 pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
570 
571 impl FontSizeAdjust {
572     #[inline]
573     /// Default value of font-size-adjust
none() -> Self574     pub fn none() -> Self {
575         FontSizeAdjust::None
576     }
577 }
578 
579 /// Use VariantAlternatesList as computed type of FontVariantAlternates
580 pub type FontVariantAlternates = specified::VariantAlternatesList;
581 
582 impl FontVariantAlternates {
583     /// Get initial value with VariantAlternatesList
584     #[inline]
get_initial_value() -> Self585     pub fn get_initial_value() -> Self {
586         Self::default()
587     }
588 }
589 
590 /// Use VariantEastAsian as computed type of FontVariantEastAsian
591 pub type FontVariantEastAsian = specified::VariantEastAsian;
592 
593 /// Use VariantLigatures as computed type of FontVariantLigatures
594 pub type FontVariantLigatures = specified::VariantLigatures;
595 
596 /// Use VariantNumeric as computed type of FontVariantNumeric
597 pub type FontVariantNumeric = specified::VariantNumeric;
598 
599 /// Use FontSettings as computed type of FontFeatureSettings.
600 pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
601 
602 /// The computed value for font-variation-settings.
603 pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
604 
605 /// font-language-override can only have a single three-letter
606 /// OpenType "language system" tag, so we should be able to compute
607 /// it and store it as a 32-bit integer
608 /// (see http://www.microsoft.com/typography/otspec/languagetags.htm).
609 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToResolvedValue)]
610 #[repr(C)]
611 pub struct FontLanguageOverride(pub u32);
612 
613 impl FontLanguageOverride {
614     #[inline]
615     /// Get computed default value of `font-language-override` with 0
zero() -> FontLanguageOverride616     pub fn zero() -> FontLanguageOverride {
617         FontLanguageOverride(0)
618     }
619 
620     /// Returns this value as a `&str`, backed by `storage`.
621     #[inline]
to_str(self, storage: &mut [u8; 4]) -> &str622     pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
623         *storage = u32::to_be_bytes(self.0);
624         // Safe because we ensure it's ASCII during computing
625         let slice = if cfg!(debug_assertions) {
626             std::str::from_utf8(&storage[..]).unwrap()
627         } else {
628             unsafe { std::str::from_utf8_unchecked(&storage[..]) }
629         };
630         slice.trim_end()
631     }
632 
633     /// Parses a str, return `Self::zero()` if the input isn't a valid OpenType
634     /// "language system" tag.
635     #[inline]
from_str(lang: &str) -> Self636     pub fn from_str(lang: &str) -> Self {
637         if lang.is_empty() || lang.len() > 4 {
638             return Self::zero();
639         }
640         let mut bytes = [b' '; 4];
641         for (byte, lang_byte) in bytes.iter_mut().zip(lang.as_bytes()) {
642             if !lang_byte.is_ascii() {
643                 return Self::zero();
644             }
645             *byte = *lang_byte;
646         }
647         Self(u32::from_be_bytes(bytes))
648     }
649 
650     /// Unsafe because `Self::to_str` requires the value to represent a UTF-8
651     /// string.
652     #[inline]
from_u32(value: u32) -> Self653     pub unsafe fn from_u32(value: u32) -> Self {
654         Self(value)
655     }
656 }
657 
658 impl ToCss for FontLanguageOverride {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write,659     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
660     where
661         W: fmt::Write,
662     {
663         if self.0 == 0 {
664             return dest.write_str("normal");
665         }
666         self.to_str(&mut [0; 4]).to_css(dest)
667     }
668 }
669 
670 // FIXME(emilio): Make Gecko use the cbindgen'd fontLanguageOverride, then
671 // remove this.
672 #[cfg(feature = "gecko")]
673 impl From<u32> for FontLanguageOverride {
from(v: u32) -> Self674     fn from(v: u32) -> Self {
675         unsafe { Self::from_u32(v) }
676     }
677 }
678 
679 #[cfg(feature = "gecko")]
680 impl From<FontLanguageOverride> for u32 {
from(v: FontLanguageOverride) -> u32681     fn from(v: FontLanguageOverride) -> u32 {
682         v.0
683     }
684 }
685 
686 impl ToComputedValue for specified::MozScriptMinSize {
687     type ComputedValue = MozScriptMinSize;
688 
to_computed_value(&self, cx: &Context) -> MozScriptMinSize689     fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
690         // this value is used in the computation of font-size, so
691         // we use the parent size
692         let base_size = FontBaseSize::InheritedStyle;
693         match self.0 {
694             NoCalcLength::FontRelative(value) => value.to_computed_value(cx, base_size),
695             NoCalcLength::ServoCharacterWidth(value) => {
696                 value.to_computed_value(base_size.resolve(cx))
697             },
698             ref l => l.to_computed_value(cx),
699         }
700     }
701 
from_computed_value(other: &MozScriptMinSize) -> Self702     fn from_computed_value(other: &MozScriptMinSize) -> Self {
703         specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
704     }
705 }
706 
707 /// The computed value of the math-depth property.
708 pub type MathDepth = i8;
709 
710 #[cfg(feature = "gecko")]
711 impl ToComputedValue for specified::MathDepth {
712     type ComputedValue = MathDepth;
713 
to_computed_value(&self, cx: &Context) -> i8714     fn to_computed_value(&self, cx: &Context) -> i8 {
715         use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
716         use std::{cmp, i8};
717 
718         let int = match *self {
719             specified::MathDepth::AutoAdd => {
720                 let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
721                 let style = cx.builder.get_parent_font().clone_math_style();
722                 if style == MathStyleValue::Compact {
723                     parent.saturating_add(1)
724                 } else {
725                     parent
726                 }
727             },
728             specified::MathDepth::Add(rel) => {
729                 let parent = cx.builder.get_parent_font().clone_math_depth();
730                 (parent as i32).saturating_add(rel.to_computed_value(cx))
731             },
732             specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
733         };
734         cmp::min(int, i8::MAX as i32) as i8
735     }
736 
from_computed_value(other: &i8) -> Self737     fn from_computed_value(other: &i8) -> Self {
738         let computed_value = *other as i32;
739         specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
740     }
741 }
742 
743 /// A wrapper over an `Angle`, that handles clamping to the appropriate range
744 /// for `font-style` animation.
745 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)]
746 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
747 pub struct FontStyleAngle(pub Angle);
748 
749 impl ToAnimatedValue for FontStyleAngle {
750     type AnimatedValue = Angle;
751 
752     #[inline]
to_animated_value(self) -> Self::AnimatedValue753     fn to_animated_value(self) -> Self::AnimatedValue {
754         self.0
755     }
756 
757     #[inline]
from_animated_value(animated: Self::AnimatedValue) -> Self758     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
759         FontStyleAngle(Angle::from_degrees(
760             animated
761                 .degrees()
762                 .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
763                 .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES),
764         ))
765     }
766 }
767 
768 impl Hash for FontStyleAngle {
hash<H: Hasher>(&self, hasher: &mut H)769     fn hash<H: Hasher>(&self, hasher: &mut H) {
770         hasher.write_u64((self.0.degrees() * 10000.).trunc() as u64);
771     }
772 }
773 
774 /// The computed value of `font-style`.
775 ///
776 /// FIXME(emilio): Angle should be a custom type to handle clamping during
777 /// animation.
778 pub type FontStyle = generics::FontStyle<FontStyleAngle>;
779 
780 impl FontStyle {
781     /// The `normal` value.
782     #[inline]
normal() -> Self783     pub fn normal() -> Self {
784         generics::FontStyle::Normal
785     }
786 
787     /// The default angle for font-style: oblique. This is 20deg per spec:
788     ///
789     /// https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle
790     #[inline]
default_angle() -> FontStyleAngle791     pub fn default_angle() -> FontStyleAngle {
792         FontStyleAngle(Angle::from_degrees(
793             specified::DEFAULT_FONT_STYLE_OBLIQUE_ANGLE_DEGREES,
794         ))
795     }
796 
797     /// Get the font style from Gecko's nsFont struct.
798     #[cfg(feature = "gecko")]
from_gecko(style: structs::FontSlantStyle) -> Self799     pub fn from_gecko(style: structs::FontSlantStyle) -> Self {
800         let mut angle = 0.;
801         let mut italic = false;
802         let mut normal = false;
803         unsafe {
804             bindings::Gecko_FontSlantStyle_Get(style, &mut normal, &mut italic, &mut angle);
805         }
806         if normal {
807             return generics::FontStyle::Normal;
808         }
809         if italic {
810             return generics::FontStyle::Italic;
811         }
812         generics::FontStyle::Oblique(FontStyleAngle(Angle::from_degrees(angle)))
813     }
814 }
815 
816 impl ToCss for FontStyle {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write,817     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
818     where
819         W: fmt::Write,
820     {
821         match *self {
822             generics::FontStyle::Normal => dest.write_str("normal"),
823             generics::FontStyle::Italic => dest.write_str("italic"),
824             generics::FontStyle::Oblique(ref angle) => {
825                 dest.write_str("oblique")?;
826                 // Use `degrees` instead of just comparing Angle because
827                 // `degrees` can return slightly different values due to
828                 // floating point conversions.
829                 if angle.0.degrees() != Self::default_angle().0.degrees() {
830                     dest.write_char(' ')?;
831                     angle.to_css(dest)?;
832                 }
833                 Ok(())
834             },
835         }
836     }
837 }
838 
839 /// A value for the font-stretch property per:
840 ///
841 /// https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch
842 #[derive(
843     Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue,
844 )]
845 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
846 pub struct FontStretch(pub NonNegativePercentage);
847 
848 impl FontStretch {
849     /// 100%
hundred() -> Self850     pub fn hundred() -> Self {
851         FontStretch(NonNegativePercentage::hundred())
852     }
853 
854     /// The float value of the percentage
855     #[inline]
value(&self) -> CSSFloat856     pub fn value(&self) -> CSSFloat {
857         ((self.0).0).0
858     }
859 }
860 
861 impl ToAnimatedValue for FontStretch {
862     type AnimatedValue = Percentage;
863 
864     #[inline]
to_animated_value(self) -> Self::AnimatedValue865     fn to_animated_value(self) -> Self::AnimatedValue {
866         self.0.to_animated_value()
867     }
868 
869     #[inline]
from_animated_value(animated: Self::AnimatedValue) -> Self870     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
871         FontStretch(NonNegativePercentage::from_animated_value(animated))
872     }
873 }
874 
875 impl Hash for FontStretch {
hash<H: Hasher>(&self, hasher: &mut H)876     fn hash<H: Hasher>(&self, hasher: &mut H) {
877         hasher.write_u64((self.value() * 10000.).trunc() as u64);
878     }
879 }
880