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