font.rs (47983B)
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 use crate::derives::*; 8 use crate::parser::{Parse, ParserContext}; 9 use crate::values::animated::ToAnimatedValue; 10 use crate::values::computed::{ 11 Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, Number, Percentage, 12 ToComputedValue, Zoom, 13 }; 14 use crate::values::generics::font::{ 15 FeatureTagValue, FontSettings, TaggedFontValue, VariationValue, 16 }; 17 use crate::values::generics::{font as generics, NonNegative}; 18 use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue}; 19 use crate::values::specified::font::{ 20 self as specified, KeywordInfo, MAX_FONT_WEIGHT, MIN_FONT_WEIGHT, 21 }; 22 use crate::values::specified::length::{FontBaseSize, LineHeightBase, NoCalcLength}; 23 use crate::values::CSSInteger; 24 use crate::Atom; 25 use cssparser::{match_ignore_ascii_case, serialize_identifier, CssStringWriter, Parser}; 26 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; 27 use num_traits::abs; 28 use num_traits::cast::AsPrimitive; 29 use std::fmt::{self, Write}; 30 use style_traits::{CssWriter, ParseError, ToCss}; 31 32 pub use crate::values::computed::Length as MozScriptMinSize; 33 pub use crate::values::specified::font::MozScriptSizeMultiplier; 34 pub use crate::values::specified::font::{FontPalette, FontSynthesis, FontSynthesisStyle}; 35 pub use crate::values::specified::font::{ 36 FontVariantAlternates, FontVariantEastAsian, FontVariantLigatures, FontVariantNumeric, 37 QueryFontMetricsFlags, XLang, XTextScale, 38 }; 39 pub use crate::values::specified::Integer as SpecifiedInteger; 40 pub use crate::values::specified::Number as SpecifiedNumber; 41 42 /// Generic template for font property type classes that use a fixed-point 43 /// internal representation with `FRACTION_BITS` for the fractional part. 44 /// 45 /// Values are constructed from and exposed as floating-point, but stored 46 /// internally as fixed point, so there will be a quantization effect on 47 /// fractional values, depending on the number of fractional bits used. 48 /// 49 /// Using (16-bit) fixed-point types rather than floats for these style 50 /// attributes reduces the memory footprint of gfxFontEntry and gfxFontStyle; it 51 /// will also tend to reduce the number of distinct font instances that get 52 /// created, particularly when styles are animated or set to arbitrary values 53 /// (e.g. by sliders in the UI), which should reduce pressure on graphics 54 /// resources and improve cache hit rates. 55 /// 56 /// cbindgen:derive-lt 57 /// cbindgen:derive-lte 58 /// cbindgen:derive-gt 59 /// cbindgen:derive-gte 60 #[repr(C)] 61 #[derive( 62 Clone, 63 ComputeSquaredDistance, 64 Copy, 65 Debug, 66 Eq, 67 Hash, 68 MallocSizeOf, 69 PartialEq, 70 PartialOrd, 71 ToResolvedValue, 72 )] 73 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 74 pub struct FixedPoint<T, const FRACTION_BITS: u16> { 75 /// The actual representation. 76 pub value: T, 77 } 78 79 impl<T, const FRACTION_BITS: u16> FixedPoint<T, FRACTION_BITS> 80 where 81 T: AsPrimitive<f32>, 82 f32: AsPrimitive<T>, 83 u16: AsPrimitive<T>, 84 { 85 const SCALE: u16 = 1 << FRACTION_BITS; 86 const INVERSE_SCALE: f32 = 1.0 / Self::SCALE as f32; 87 88 /// Returns a fixed-point bit from a floating-point context. 89 pub fn from_float(v: f32) -> Self { 90 Self { 91 value: (v * Self::SCALE as f32).round().as_(), 92 } 93 } 94 95 /// Returns the floating-point representation. 96 pub fn to_float(&self) -> f32 { 97 self.value.as_() * Self::INVERSE_SCALE 98 } 99 } 100 101 // We implement this and mul below only for u16 types, because u32 types might need more care about 102 // overflow. But it's not hard to implement in either case. 103 impl<const FRACTION_BITS: u16> std::ops::Div for FixedPoint<u16, FRACTION_BITS> { 104 type Output = Self; 105 fn div(self, rhs: Self) -> Self { 106 Self { 107 value: (((self.value as u32) << (FRACTION_BITS as u32)) / (rhs.value as u32)) as u16, 108 } 109 } 110 } 111 impl<const FRACTION_BITS: u16> std::ops::Mul for FixedPoint<u16, FRACTION_BITS> { 112 type Output = Self; 113 fn mul(self, rhs: Self) -> Self { 114 Self { 115 value: (((self.value as u32) * (rhs.value as u32)) >> (FRACTION_BITS as u32)) as u16, 116 } 117 } 118 } 119 120 /// font-weight: range 1..1000, fractional values permitted; keywords 121 /// 'normal', 'bold' aliased to 400, 700 respectively. 122 /// 123 /// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375) 124 pub const FONT_WEIGHT_FRACTION_BITS: u16 = 6; 125 126 /// This is an alias which is useful mostly as a cbindgen / C++ inference 127 /// workaround. 128 pub type FontWeightFixedPoint = FixedPoint<u16, FONT_WEIGHT_FRACTION_BITS>; 129 130 /// A value for the font-weight property per: 131 /// 132 /// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight 133 /// 134 /// cbindgen:derive-lt 135 /// cbindgen:derive-lte 136 /// cbindgen:derive-gt 137 /// cbindgen:derive-gte 138 #[derive( 139 Clone, 140 ComputeSquaredDistance, 141 Copy, 142 Debug, 143 Hash, 144 MallocSizeOf, 145 PartialEq, 146 PartialOrd, 147 ToResolvedValue, 148 ToTyped, 149 )] 150 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 151 #[repr(C)] 152 pub struct FontWeight(FontWeightFixedPoint); 153 impl ToAnimatedValue for FontWeight { 154 type AnimatedValue = Number; 155 156 #[inline] 157 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue { 158 self.value() 159 } 160 161 #[inline] 162 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 163 FontWeight::from_float(animated) 164 } 165 } 166 167 impl ToCss for FontWeight { 168 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 169 where 170 W: fmt::Write, 171 { 172 self.value().to_css(dest) 173 } 174 } 175 176 impl FontWeight { 177 /// The `normal` keyword. 178 pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint { 179 value: 400 << FONT_WEIGHT_FRACTION_BITS, 180 }); 181 182 /// The `bold` value. 183 pub const BOLD: FontWeight = FontWeight(FontWeightFixedPoint { 184 value: 700 << FONT_WEIGHT_FRACTION_BITS, 185 }); 186 187 /// The threshold from which we consider a font bold. 188 pub const BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint { 189 value: 600 << FONT_WEIGHT_FRACTION_BITS, 190 }); 191 192 /// The threshold above which CSS font matching prefers bolder faces 193 /// over lighter ones. 194 pub const PREFER_BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint { 195 value: 500 << FONT_WEIGHT_FRACTION_BITS, 196 }); 197 198 /// Returns the `normal` keyword value. 199 pub fn normal() -> Self { 200 Self::NORMAL 201 } 202 203 /// Whether this weight is bold 204 pub fn is_bold(&self) -> bool { 205 *self >= Self::BOLD_THRESHOLD 206 } 207 208 /// Returns the value as a float. 209 pub fn value(&self) -> f32 { 210 self.0.to_float() 211 } 212 213 /// Construct a valid weight from a float value. 214 pub fn from_float(v: f32) -> Self { 215 Self(FixedPoint::from_float( 216 v.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT), 217 )) 218 } 219 220 /// Return the bolder weight. 221 /// 222 /// See the table in: 223 /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values 224 pub fn bolder(self) -> Self { 225 let value = self.value(); 226 if value < 350. { 227 return Self::NORMAL; 228 } 229 if value < 550. { 230 return Self::BOLD; 231 } 232 Self::from_float(value.max(900.)) 233 } 234 235 /// Return the lighter weight. 236 /// 237 /// See the table in: 238 /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values 239 pub fn lighter(self) -> Self { 240 let value = self.value(); 241 if value < 550. { 242 return Self::from_float(value.min(100.)); 243 } 244 if value < 750. { 245 return Self::NORMAL; 246 } 247 Self::BOLD 248 } 249 } 250 251 #[derive( 252 Animate, 253 Clone, 254 ComputeSquaredDistance, 255 Copy, 256 Debug, 257 MallocSizeOf, 258 PartialEq, 259 ToAnimatedZero, 260 ToCss, 261 ToTyped, 262 )] 263 #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))] 264 /// The computed value of font-size 265 pub struct FontSize { 266 /// The computed size, that we use to compute ems etc. This accounts for 267 /// e.g., text-zoom. 268 pub computed_size: NonNegativeLength, 269 /// The actual used size. This is the computed font size, potentially 270 /// constrained by other factors like minimum font-size settings and so on. 271 #[css(skip)] 272 pub used_size: NonNegativeLength, 273 /// If derived from a keyword, the keyword and additional transformations applied to it 274 #[css(skip)] 275 pub keyword_info: KeywordInfo, 276 } 277 278 impl FontSize { 279 /// The actual computed font size. 280 #[inline] 281 pub fn computed_size(&self) -> Length { 282 self.computed_size.0 283 } 284 285 /// The actual used font size. 286 #[inline] 287 pub fn used_size(&self) -> Length { 288 self.used_size.0 289 } 290 291 /// Apply zoom to the font-size. This is usually done by ToComputedValue. 292 #[inline] 293 pub fn zoom(&self, zoom: Zoom) -> Self { 294 Self { 295 computed_size: NonNegative(Length::new(zoom.zoom(self.computed_size.0.px()))), 296 used_size: NonNegative(Length::new(zoom.zoom(self.used_size.0.px()))), 297 keyword_info: self.keyword_info, 298 } 299 } 300 301 #[inline] 302 /// Get default value of font size. 303 pub fn medium() -> Self { 304 Self { 305 computed_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)), 306 used_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)), 307 keyword_info: KeywordInfo::medium(), 308 } 309 } 310 } 311 312 impl ToAnimatedValue for FontSize { 313 type AnimatedValue = Length; 314 315 #[inline] 316 fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue { 317 self.computed_size.0.to_animated_value(context) 318 } 319 320 #[inline] 321 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 322 FontSize { 323 computed_size: NonNegative(animated.clamp_to_non_negative()), 324 used_size: NonNegative(animated.clamp_to_non_negative()), 325 keyword_info: KeywordInfo::none(), 326 } 327 } 328 } 329 330 impl ToResolvedValue for FontSize { 331 type ResolvedValue = NonNegativeLength; 332 333 #[inline] 334 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue { 335 self.computed_size.to_resolved_value(context) 336 } 337 338 #[inline] 339 fn from_resolved_value(resolved: Self::ResolvedValue) -> Self { 340 let computed_size = NonNegativeLength::from_resolved_value(resolved); 341 Self { 342 computed_size, 343 used_size: computed_size, 344 keyword_info: KeywordInfo::none(), 345 } 346 } 347 } 348 349 #[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue, ToTyped)] 350 #[cfg_attr(feature = "servo", derive(Hash, Serialize, Deserialize))] 351 /// Specifies a prioritized list of font family names or generic family names. 352 #[repr(C)] 353 pub struct FontFamily { 354 /// The actual list of family names. 355 pub families: FontFamilyList, 356 /// Whether this font-family came from a specified system-font. 357 pub is_system_font: bool, 358 /// Whether this is the initial font-family that might react to language 359 /// changes. 360 pub is_initial: bool, 361 } 362 363 macro_rules! static_font_family { 364 ($ident:ident, $family:expr) => { 365 static $ident: std::sync::LazyLock<FontFamily> = std::sync::LazyLock::new(|| FontFamily { 366 families: FontFamilyList { 367 list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)), 368 }, 369 is_system_font: false, 370 is_initial: false, 371 }); 372 }; 373 } 374 375 impl FontFamily { 376 #[inline] 377 /// Get default font family as `serif` which is a generic font-family 378 pub fn serif() -> Self { 379 Self::generic(GenericFontFamily::Serif).clone() 380 } 381 382 /// Returns the font family for `-moz-bullet-font`. 383 #[cfg(feature = "gecko")] 384 pub(crate) fn moz_bullet() -> &'static Self { 385 static_font_family!( 386 MOZ_BULLET, 387 SingleFontFamily::FamilyName(FamilyName { 388 name: atom!("-moz-bullet-font"), 389 syntax: FontFamilyNameSyntax::Identifiers, 390 }) 391 ); 392 393 &*MOZ_BULLET 394 } 395 396 /// Returns a font family for a single system font. 397 #[cfg(feature = "gecko")] 398 pub fn for_system_font(name: &str) -> Self { 399 Self { 400 families: FontFamilyList { 401 list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName( 402 FamilyName { 403 name: Atom::from(name), 404 syntax: FontFamilyNameSyntax::Identifiers, 405 }, 406 ))), 407 }, 408 is_system_font: true, 409 is_initial: false, 410 } 411 } 412 413 /// Returns a generic font family. 414 pub fn generic(generic: GenericFontFamily) -> &'static Self { 415 macro_rules! generic_font_family { 416 ($ident:ident, $family:ident) => { 417 static_font_family!( 418 $ident, 419 SingleFontFamily::Generic(GenericFontFamily::$family) 420 ) 421 }; 422 } 423 424 generic_font_family!(SERIF, Serif); 425 generic_font_family!(SANS_SERIF, SansSerif); 426 generic_font_family!(MONOSPACE, Monospace); 427 generic_font_family!(CURSIVE, Cursive); 428 generic_font_family!(FANTASY, Fantasy); 429 #[cfg(feature = "gecko")] 430 generic_font_family!(MATH, Math); 431 #[cfg(feature = "gecko")] 432 generic_font_family!(MOZ_EMOJI, MozEmoji); 433 generic_font_family!(SYSTEM_UI, SystemUi); 434 435 let family = match generic { 436 GenericFontFamily::None => { 437 debug_assert!(false, "Bogus caller!"); 438 &*SERIF 439 }, 440 GenericFontFamily::Serif => &*SERIF, 441 GenericFontFamily::SansSerif => &*SANS_SERIF, 442 GenericFontFamily::Monospace => &*MONOSPACE, 443 GenericFontFamily::Cursive => &*CURSIVE, 444 GenericFontFamily::Fantasy => &*FANTASY, 445 #[cfg(feature = "gecko")] 446 GenericFontFamily::Math => &*MATH, 447 #[cfg(feature = "gecko")] 448 GenericFontFamily::MozEmoji => &*MOZ_EMOJI, 449 GenericFontFamily::SystemUi => &*SYSTEM_UI, 450 }; 451 debug_assert_eq!( 452 *family.families.iter().next().unwrap(), 453 SingleFontFamily::Generic(generic) 454 ); 455 family 456 } 457 } 458 459 impl MallocSizeOf for FontFamily { 460 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { 461 use malloc_size_of::MallocUnconditionalSizeOf; 462 // SharedFontList objects are generally measured from the pointer stored 463 // in the specified value. So only count this if the SharedFontList is 464 // unshared. 465 let shared_font_list = &self.families.list; 466 if shared_font_list.is_unique() { 467 shared_font_list.unconditional_size_of(ops) 468 } else { 469 0 470 } 471 } 472 } 473 474 impl ToCss for FontFamily { 475 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 476 where 477 W: fmt::Write, 478 { 479 let mut iter = self.families.iter(); 480 match iter.next() { 481 Some(f) => f.to_css(dest)?, 482 None => return Ok(()), 483 } 484 for family in iter { 485 dest.write_str(", ")?; 486 family.to_css(dest)?; 487 } 488 Ok(()) 489 } 490 } 491 492 /// The name of a font family of choice. 493 #[derive( 494 Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, 495 )] 496 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 497 #[repr(C)] 498 pub struct FamilyName { 499 /// Name of the font family. 500 pub name: Atom, 501 /// Syntax of the font family. 502 pub syntax: FontFamilyNameSyntax, 503 } 504 505 #[cfg(feature = "gecko")] 506 impl FamilyName { 507 fn is_known_icon_font_family(&self) -> bool { 508 use crate::gecko_bindings::bindings; 509 unsafe { bindings::Gecko_IsKnownIconFontFamily(self.name.as_ptr()) } 510 } 511 } 512 513 #[cfg(feature = "servo")] 514 impl FamilyName { 515 fn is_known_icon_font_family(&self) -> bool { 516 false 517 } 518 } 519 520 impl ToCss for FamilyName { 521 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 522 where 523 W: fmt::Write, 524 { 525 match self.syntax { 526 FontFamilyNameSyntax::Quoted => { 527 dest.write_char('"')?; 528 write!(CssStringWriter::new(dest), "{}", self.name)?; 529 dest.write_char('"') 530 }, 531 FontFamilyNameSyntax::Identifiers => { 532 let mut first = true; 533 for ident in self.name.to_string().split(' ') { 534 if first { 535 first = false; 536 } else { 537 dest.write_char(' ')?; 538 } 539 debug_assert!( 540 !ident.is_empty(), 541 "Family name with leading, \ 542 trailing, or consecutive white spaces should \ 543 have been marked quoted by the parser" 544 ); 545 serialize_identifier(ident, dest)?; 546 } 547 Ok(()) 548 }, 549 } 550 } 551 } 552 553 #[derive( 554 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, 555 )] 556 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 557 /// Font family names must either be given quoted as strings, 558 /// or unquoted as a sequence of one or more identifiers. 559 #[repr(u8)] 560 pub enum FontFamilyNameSyntax { 561 /// The family name was specified in a quoted form, e.g. "Font Name" 562 /// or 'Font Name'. 563 Quoted, 564 565 /// The family name was specified in an unquoted form as a sequence of 566 /// identifiers. 567 Identifiers, 568 } 569 570 /// A set of faces that vary in weight, width or slope. 571 /// cbindgen:derive-mut-casts=true 572 #[derive( 573 Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem, 574 )] 575 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))] 576 #[repr(u8)] 577 pub enum SingleFontFamily { 578 /// The name of a font family of choice. 579 FamilyName(FamilyName), 580 /// Generic family name. 581 Generic(GenericFontFamily), 582 } 583 584 fn system_ui_enabled(_: &ParserContext) -> bool { 585 static_prefs::pref!("layout.css.system-ui.enabled") 586 } 587 588 #[cfg(feature = "gecko")] 589 fn math_enabled(context: &ParserContext) -> bool { 590 context.chrome_rules_enabled() || static_prefs::pref!("mathml.font_family_math.enabled") 591 } 592 593 /// A generic font-family name. 594 /// 595 /// The order here is important, if you change it make sure that 596 /// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s 597 /// sSingleGenerics are updated as well. 598 /// 599 /// NOTE(emilio): Should be u8, but it's a u32 because of ABI issues between GCC 600 /// and LLVM see https://bugs.llvm.org/show_bug.cgi?id=44228 / bug 1600735 / 601 /// bug 1726515. 602 #[derive( 603 Clone, 604 Copy, 605 Debug, 606 Eq, 607 Hash, 608 MallocSizeOf, 609 PartialEq, 610 Parse, 611 ToCss, 612 ToComputedValue, 613 ToResolvedValue, 614 ToShmem, 615 )] 616 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 617 #[repr(u32)] 618 #[allow(missing_docs)] 619 pub enum GenericFontFamily { 620 /// No generic family specified, only for internal usage. 621 /// 622 /// NOTE(emilio): Gecko code relies on this variant being zero. 623 #[css(skip)] 624 None = 0, 625 Serif, 626 SansSerif, 627 #[parse(aliases = "-moz-fixed")] 628 Monospace, 629 Cursive, 630 Fantasy, 631 #[cfg(feature = "gecko")] 632 #[parse(condition = "math_enabled")] 633 Math, 634 #[parse(condition = "system_ui_enabled")] 635 SystemUi, 636 /// An internal value for emoji font selection. 637 #[css(skip)] 638 #[cfg(feature = "gecko")] 639 MozEmoji, 640 } 641 642 impl GenericFontFamily { 643 /// When we disallow websites to override fonts, we ignore some generic 644 /// families that the website might specify, since they're not configured by 645 /// the user. See bug 789788 and bug 1730098. 646 pub(crate) fn valid_for_user_font_prioritization(self) -> bool { 647 match self { 648 Self::None | Self::Cursive | Self::Fantasy | Self::SystemUi => false, 649 #[cfg(feature = "gecko")] 650 Self::Math | Self::MozEmoji => false, 651 Self::Serif | Self::SansSerif | Self::Monospace => true, 652 } 653 } 654 } 655 656 impl Parse for SingleFontFamily { 657 /// Parse a font-family value. 658 fn parse<'i, 't>( 659 context: &ParserContext, 660 input: &mut Parser<'i, 't>, 661 ) -> Result<Self, ParseError<'i>> { 662 if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) { 663 return Ok(SingleFontFamily::FamilyName(FamilyName { 664 name: Atom::from(&*value), 665 syntax: FontFamilyNameSyntax::Quoted, 666 })); 667 } 668 669 if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) { 670 return Ok(SingleFontFamily::Generic(generic)); 671 } 672 673 let first_ident = input.expect_ident_cloned()?; 674 let reserved = match_ignore_ascii_case! { &first_ident, 675 // https://drafts.csswg.org/css-fonts/#propdef-font-family 676 // "Font family names that happen to be the same as a keyword value 677 // (`inherit`, `serif`, `sans-serif`, `monospace`, `fantasy`, and `cursive`) 678 // must be quoted to prevent confusion with the keywords with the same names. 679 // The keywords ‘initial’ and ‘default’ are reserved for future use 680 // and must also be quoted when used as font names. 681 // UAs must not consider these keywords as matching the <family-name> type." 682 "inherit" | "initial" | "unset" | "revert" | "default" => true, 683 _ => false, 684 }; 685 686 let mut value = first_ident.as_ref().to_owned(); 687 let mut serialize_quoted = value.contains(' '); 688 689 // These keywords are not allowed by themselves. 690 // The only way this value can be valid with with another keyword. 691 if reserved { 692 let ident = input.expect_ident()?; 693 serialize_quoted = serialize_quoted || ident.contains(' '); 694 value.push(' '); 695 value.push_str(&ident); 696 } 697 while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) { 698 serialize_quoted = serialize_quoted || ident.contains(' '); 699 value.push(' '); 700 value.push_str(&ident); 701 } 702 let syntax = if serialize_quoted { 703 // For font family names which contains special white spaces, e.g. 704 // `font-family: \ a\ \ b\ \ c\ ;`, it is tricky to serialize them 705 // as identifiers correctly. Just mark them quoted so we don't need 706 // to worry about them in serialization code. 707 FontFamilyNameSyntax::Quoted 708 } else { 709 FontFamilyNameSyntax::Identifiers 710 }; 711 Ok(SingleFontFamily::FamilyName(FamilyName { 712 name: Atom::from(value), 713 syntax, 714 })) 715 } 716 } 717 718 /// A list of font families. 719 #[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)] 720 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))] 721 #[repr(C)] 722 pub struct FontFamilyList { 723 /// The actual list of font families specified. 724 pub list: crate::ArcSlice<SingleFontFamily>, 725 } 726 727 impl FontFamilyList { 728 /// Return iterator of SingleFontFamily 729 pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> { 730 self.list.iter() 731 } 732 733 /// If there's a generic font family on the list which is suitable for user 734 /// font prioritization, then move it ahead of the other families in the list, 735 /// except for any families known to be ligature-based icon fonts, where using a 736 /// generic instead of the site's specified font may cause substantial breakage. 737 /// If no suitable generic is found in the list, insert the default generic ahead 738 /// of all the listed families except for known ligature-based icon fonts. 739 #[cfg_attr(feature = "servo", allow(unused))] 740 pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) { 741 let mut index_of_first_generic = None; 742 let mut target_index = None; 743 744 for (i, f) in self.iter().enumerate() { 745 match &*f { 746 SingleFontFamily::Generic(f) => { 747 if index_of_first_generic.is_none() && f.valid_for_user_font_prioritization() { 748 // If we haven't found a target position, there's nothing to do; 749 // this entry is already ahead of everything except any whitelisted 750 // icon fonts. 751 if target_index.is_none() { 752 return; 753 } 754 index_of_first_generic = Some(i); 755 break; 756 } 757 // A non-prioritized generic (e.g. cursive, fantasy) becomes the target 758 // position for prioritization, just like arbitrary named families. 759 if target_index.is_none() { 760 target_index = Some(i); 761 } 762 }, 763 SingleFontFamily::FamilyName(fam) => { 764 // Target position for the first generic is in front of the first 765 // non-whitelisted icon font family we find. 766 if target_index.is_none() && !fam.is_known_icon_font_family() { 767 target_index = Some(i); 768 } 769 }, 770 } 771 } 772 773 let mut new_list = self.list.iter().cloned().collect::<Vec<_>>(); 774 let first_generic = match index_of_first_generic { 775 Some(i) => new_list.remove(i), 776 None => SingleFontFamily::Generic(generic), 777 }; 778 779 if let Some(i) = target_index { 780 new_list.insert(i, first_generic); 781 } else { 782 new_list.push(first_generic); 783 } 784 self.list = crate::ArcSlice::from_iter(new_list.into_iter()); 785 } 786 787 /// Returns whether we need to prioritize user fonts. 788 #[cfg_attr(feature = "servo", allow(unused))] 789 pub(crate) fn needs_user_font_prioritization(&self) -> bool { 790 self.iter().next().map_or(true, |f| match f { 791 SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(), 792 _ => true, 793 }) 794 } 795 796 /// Return the generic ID if it is a single generic font 797 pub fn single_generic(&self) -> Option<GenericFontFamily> { 798 let mut iter = self.iter(); 799 if let Some(SingleFontFamily::Generic(f)) = iter.next() { 800 if iter.next().is_none() { 801 return Some(*f); 802 } 803 } 804 None 805 } 806 } 807 808 /// Preserve the readability of text when font fallback occurs. 809 pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>; 810 811 impl FontSizeAdjust { 812 #[inline] 813 /// Default value of font-size-adjust 814 pub fn none() -> Self { 815 FontSizeAdjust::None 816 } 817 } 818 819 impl ToComputedValue for specified::FontSizeAdjust { 820 type ComputedValue = FontSizeAdjust; 821 822 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 823 use crate::font_metrics::FontMetricsOrientation; 824 825 let font_metrics = |vertical, flags| { 826 let orient = if vertical { 827 FontMetricsOrientation::MatchContextPreferVertical 828 } else { 829 FontMetricsOrientation::Horizontal 830 }; 831 let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, flags); 832 let font_size = context.style().get_font().clone_font_size().used_size.0; 833 (metrics, font_size) 834 }; 835 836 // Macro to resolve a from-font value using the given metric field. If not present, 837 // returns the fallback value, or if that is negative, resolves using ascent instead 838 // of the missing field (this is the fallback for cap-height). 839 macro_rules! resolve { 840 ($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr, $flags:expr) => {{ 841 match $value { 842 specified::FontSizeAdjustFactor::Number(f) => { 843 FontSizeAdjust::$basis(f.to_computed_value(context)) 844 }, 845 specified::FontSizeAdjustFactor::FromFont => { 846 let (metrics, font_size) = font_metrics($vertical, $flags); 847 let ratio = if let Some(metric) = metrics.$field { 848 metric / font_size 849 } else if $fallback >= 0.0 { 850 $fallback 851 } else { 852 metrics.ascent / font_size 853 }; 854 if ratio.is_nan() { 855 FontSizeAdjust::$basis(NonNegative(abs($fallback))) 856 } else { 857 FontSizeAdjust::$basis(NonNegative(ratio)) 858 } 859 }, 860 } 861 }}; 862 } 863 864 match *self { 865 Self::None => FontSizeAdjust::None, 866 Self::ExHeight(val) => { 867 resolve!( 868 ExHeight, 869 val, 870 false, 871 x_height, 872 0.5, 873 QueryFontMetricsFlags::empty() 874 ) 875 }, 876 Self::CapHeight(val) => { 877 resolve!( 878 CapHeight, 879 val, 880 false, 881 cap_height, 882 -1.0, /* fall back to ascent */ 883 QueryFontMetricsFlags::empty() 884 ) 885 }, 886 Self::ChWidth(val) => { 887 resolve!( 888 ChWidth, 889 val, 890 false, 891 zero_advance_measure, 892 0.5, 893 QueryFontMetricsFlags::NEEDS_CH 894 ) 895 }, 896 Self::IcWidth(val) => { 897 resolve!( 898 IcWidth, 899 val, 900 false, 901 ic_width, 902 1.0, 903 QueryFontMetricsFlags::NEEDS_IC 904 ) 905 }, 906 Self::IcHeight(val) => { 907 resolve!( 908 IcHeight, 909 val, 910 true, 911 ic_width, 912 1.0, 913 QueryFontMetricsFlags::NEEDS_IC 914 ) 915 }, 916 } 917 } 918 919 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 920 macro_rules! case { 921 ($basis:ident, $val:expr) => { 922 Self::$basis(specified::FontSizeAdjustFactor::Number( 923 ToComputedValue::from_computed_value($val), 924 )) 925 }; 926 } 927 match *computed { 928 FontSizeAdjust::None => Self::None, 929 FontSizeAdjust::ExHeight(ref val) => case!(ExHeight, val), 930 FontSizeAdjust::CapHeight(ref val) => case!(CapHeight, val), 931 FontSizeAdjust::ChWidth(ref val) => case!(ChWidth, val), 932 FontSizeAdjust::IcWidth(ref val) => case!(IcWidth, val), 933 FontSizeAdjust::IcHeight(ref val) => case!(IcHeight, val), 934 } 935 } 936 } 937 938 /// Use FontSettings as computed type of FontFeatureSettings. 939 pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>; 940 941 /// The computed value for font-variation-settings. 942 pub type FontVariationSettings = FontSettings<VariationValue<Number>>; 943 944 // The computed value of font-{feature,variation}-settings discards values 945 // with duplicate tags, keeping only the last occurrence of each tag. 946 fn dedup_font_settings<T>(settings_list: &mut Vec<T>) 947 where 948 T: TaggedFontValue, 949 { 950 if settings_list.len() > 1 { 951 settings_list.sort_by_key(|k| k.tag().0); 952 // dedup() keeps the first of any duplicates, but we want the last, 953 // so we implement it manually here. 954 let mut prev_tag = settings_list.last().unwrap().tag(); 955 for i in (0..settings_list.len() - 1).rev() { 956 let cur_tag = settings_list[i].tag(); 957 if cur_tag == prev_tag { 958 settings_list.remove(i); 959 } 960 prev_tag = cur_tag; 961 } 962 } 963 } 964 965 impl<T> ToComputedValue for FontSettings<T> 966 where 967 T: ToComputedValue, 968 <T as ToComputedValue>::ComputedValue: TaggedFontValue, 969 { 970 type ComputedValue = FontSettings<T::ComputedValue>; 971 972 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 973 let mut v = self 974 .0 975 .iter() 976 .map(|item| item.to_computed_value(context)) 977 .collect::<Vec<_>>(); 978 dedup_font_settings(&mut v); 979 FontSettings(v.into_boxed_slice()) 980 } 981 982 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 983 Self(computed.0.iter().map(T::from_computed_value).collect()) 984 } 985 } 986 987 /// font-language-override can only have a single 1-4 ASCII character 988 /// OpenType "language system" tag, so we should be able to compute 989 /// it and store it as a 32-bit integer 990 /// (see http://www.microsoft.com/typography/otspec/languagetags.htm). 991 #[derive( 992 Clone, 993 Copy, 994 Debug, 995 Eq, 996 MallocSizeOf, 997 PartialEq, 998 SpecifiedValueInfo, 999 ToComputedValue, 1000 ToResolvedValue, 1001 ToShmem, 1002 ToTyped, 1003 )] 1004 #[repr(C)] 1005 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 1006 #[value_info(other_values = "normal")] 1007 pub struct FontLanguageOverride(pub u32); 1008 1009 impl FontLanguageOverride { 1010 #[inline] 1011 /// Get computed default value of `font-language-override` with 0 1012 pub fn normal() -> FontLanguageOverride { 1013 FontLanguageOverride(0) 1014 } 1015 1016 /// Returns this value as a `&str`, backed by `storage`. 1017 #[inline] 1018 pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str { 1019 *storage = u32::to_be_bytes(self.0); 1020 // Safe because we ensure it's ASCII during parsing 1021 let slice = if cfg!(debug_assertions) { 1022 std::str::from_utf8(&storage[..]).unwrap() 1023 } else { 1024 unsafe { std::str::from_utf8_unchecked(&storage[..]) } 1025 }; 1026 slice.trim_end() 1027 } 1028 1029 /// Unsafe because `Self::to_str` requires the value to represent a UTF-8 1030 /// string. 1031 #[inline] 1032 pub unsafe fn from_u32(value: u32) -> Self { 1033 Self(value) 1034 } 1035 } 1036 1037 impl ToCss for FontLanguageOverride { 1038 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 1039 where 1040 W: fmt::Write, 1041 { 1042 if self.0 == 0 { 1043 return dest.write_str("normal"); 1044 } 1045 self.to_str(&mut [0; 4]).to_css(dest) 1046 } 1047 } 1048 1049 impl ToComputedValue for specified::MozScriptMinSize { 1050 type ComputedValue = MozScriptMinSize; 1051 1052 fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize { 1053 // this value is used in the computation of font-size, so 1054 // we use the parent size 1055 let base_size = FontBaseSize::InheritedStyle; 1056 let line_height_base = LineHeightBase::InheritedStyle; 1057 match self.0 { 1058 NoCalcLength::FontRelative(value) => { 1059 value.to_computed_value(cx, base_size, line_height_base) 1060 }, 1061 NoCalcLength::ServoCharacterWidth(value) => { 1062 value.to_computed_value(base_size.resolve(cx).computed_size()) 1063 }, 1064 ref l => l.to_computed_value(cx), 1065 } 1066 } 1067 1068 fn from_computed_value(other: &MozScriptMinSize) -> Self { 1069 specified::MozScriptMinSize(ToComputedValue::from_computed_value(other)) 1070 } 1071 } 1072 1073 /// The computed value of the math-depth property. 1074 pub type MathDepth = i8; 1075 1076 #[cfg(feature = "gecko")] 1077 impl ToComputedValue for specified::MathDepth { 1078 type ComputedValue = MathDepth; 1079 1080 fn to_computed_value(&self, cx: &Context) -> i8 { 1081 use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue; 1082 use std::{cmp, i8}; 1083 1084 let int = match *self { 1085 specified::MathDepth::AutoAdd => { 1086 let parent = cx.builder.get_parent_font().clone_math_depth() as i32; 1087 let style = cx.builder.get_parent_font().clone_math_style(); 1088 if style == MathStyleValue::Compact { 1089 parent.saturating_add(1) 1090 } else { 1091 parent 1092 } 1093 }, 1094 specified::MathDepth::Add(rel) => { 1095 let parent = cx.builder.get_parent_font().clone_math_depth(); 1096 (parent as i32).saturating_add(rel.to_computed_value(cx)) 1097 }, 1098 specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx), 1099 }; 1100 cmp::min(int, i8::MAX as i32) as i8 1101 } 1102 1103 fn from_computed_value(other: &i8) -> Self { 1104 let computed_value = *other as i32; 1105 specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value)) 1106 } 1107 } 1108 1109 impl ToAnimatedValue for MathDepth { 1110 type AnimatedValue = CSSInteger; 1111 1112 #[inline] 1113 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue { 1114 self.into() 1115 } 1116 1117 #[inline] 1118 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 1119 use std::{cmp, i8}; 1120 cmp::min(animated, i8::MAX as i32) as i8 1121 } 1122 } 1123 1124 /// - Use a signed 8.8 fixed-point value (representable range -128.0..128) 1125 /// 1126 /// Values of <angle> below -90 or above 90 are not permitted, so we use an out 1127 /// of range value to represent `italic`. 1128 pub const FONT_STYLE_FRACTION_BITS: u16 = 8; 1129 1130 /// This is an alias which is useful mostly as a cbindgen / C++ inference 1131 /// workaround. 1132 pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>; 1133 1134 /// The computed value of `font-style`. 1135 /// 1136 /// - Define angle of zero degrees as `normal` 1137 /// - Define out-of-range value 100 degrees as `italic` 1138 /// - Other values represent `oblique <angle>` 1139 /// 1140 /// cbindgen:derive-lt 1141 /// cbindgen:derive-lte 1142 /// cbindgen:derive-gt 1143 /// cbindgen:derive-gte 1144 #[derive( 1145 Clone, 1146 ComputeSquaredDistance, 1147 Copy, 1148 Debug, 1149 Eq, 1150 Hash, 1151 MallocSizeOf, 1152 PartialEq, 1153 PartialOrd, 1154 ToResolvedValue, 1155 ToTyped, 1156 )] 1157 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 1158 #[repr(C)] 1159 pub struct FontStyle(FontStyleFixedPoint); 1160 1161 impl FontStyle { 1162 /// The `normal` keyword, equal to `oblique` with angle zero. 1163 pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint { 1164 value: 0 << FONT_STYLE_FRACTION_BITS, 1165 }); 1166 1167 /// The italic keyword. 1168 pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint { 1169 value: 100 << FONT_STYLE_FRACTION_BITS, 1170 }); 1171 1172 /// The default angle for `font-style: oblique`. 1173 /// See also https://github.com/w3c/csswg-drafts/issues/2295 1174 pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14; 1175 1176 /// The `oblique` keyword with the default degrees. 1177 pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint { 1178 value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS, 1179 }); 1180 1181 /// The `normal` value. 1182 #[inline] 1183 pub fn normal() -> Self { 1184 Self::NORMAL 1185 } 1186 1187 /// Returns the oblique angle for this style. 1188 pub fn oblique(degrees: f32) -> Self { 1189 Self(FixedPoint::from_float( 1190 degrees 1191 .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES) 1192 .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES), 1193 )) 1194 } 1195 1196 /// Returns the oblique angle for this style. 1197 pub fn oblique_degrees(&self) -> f32 { 1198 debug_assert_ne!(*self, Self::ITALIC); 1199 self.0.to_float() 1200 } 1201 } 1202 1203 impl ToCss for FontStyle { 1204 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 1205 where 1206 W: fmt::Write, 1207 { 1208 if *self == Self::NORMAL { 1209 return dest.write_str("normal"); 1210 } 1211 if *self == Self::ITALIC { 1212 return dest.write_str("italic"); 1213 } 1214 dest.write_str("oblique")?; 1215 if *self != Self::OBLIQUE { 1216 // It's not the default oblique amount, so append the angle in degrees. 1217 dest.write_char(' ')?; 1218 Angle::from_degrees(self.oblique_degrees()).to_css(dest)?; 1219 } 1220 Ok(()) 1221 } 1222 } 1223 1224 impl ToAnimatedValue for FontStyle { 1225 type AnimatedValue = generics::FontStyle<Angle>; 1226 1227 #[inline] 1228 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue { 1229 if self == Self::ITALIC { 1230 return generics::FontStyle::Italic; 1231 } 1232 generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees())) 1233 } 1234 1235 #[inline] 1236 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 1237 match animated { 1238 generics::FontStyle::Italic => Self::ITALIC, 1239 generics::FontStyle::Oblique(ref angle) => Self::oblique(angle.degrees()), 1240 } 1241 } 1242 } 1243 1244 /// font-stretch is a percentage relative to normal. 1245 /// 1246 /// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375) 1247 /// 1248 /// We arbitrarily limit here to 1000%. (If that becomes a problem, we could 1249 /// reduce the number of fractional bits and increase the limit.) 1250 pub const FONT_STRETCH_FRACTION_BITS: u16 = 6; 1251 1252 /// This is an alias which is useful mostly as a cbindgen / C++ inference 1253 /// workaround. 1254 pub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>; 1255 1256 /// A value for the font-stretch property per: 1257 /// 1258 /// https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch 1259 /// 1260 /// cbindgen:derive-lt 1261 /// cbindgen:derive-lte 1262 /// cbindgen:derive-gt 1263 /// cbindgen:derive-gte 1264 #[derive( 1265 Clone, 1266 ComputeSquaredDistance, 1267 Copy, 1268 Debug, 1269 MallocSizeOf, 1270 PartialEq, 1271 PartialOrd, 1272 ToResolvedValue, 1273 ToTyped, 1274 )] 1275 #[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))] 1276 #[repr(C)] 1277 pub struct FontStretch(pub FontStretchFixedPoint); 1278 1279 impl FontStretch { 1280 /// The fraction bits, as an easy-to-access-constant. 1281 pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS; 1282 /// 0.5 in our floating point representation. 1283 pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1); 1284 1285 /// The `ultra-condensed` keyword. 1286 pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint { 1287 value: 50 << Self::FRACTION_BITS, 1288 }); 1289 /// The `extra-condensed` keyword. 1290 pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint { 1291 value: (62 << Self::FRACTION_BITS) + Self::HALF, 1292 }); 1293 /// The `condensed` keyword. 1294 pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint { 1295 value: 75 << Self::FRACTION_BITS, 1296 }); 1297 /// The `semi-condensed` keyword. 1298 pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint { 1299 value: (87 << Self::FRACTION_BITS) + Self::HALF, 1300 }); 1301 /// The `normal` keyword. 1302 pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint { 1303 value: 100 << Self::FRACTION_BITS, 1304 }); 1305 /// The `semi-expanded` keyword. 1306 pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint { 1307 value: (112 << Self::FRACTION_BITS) + Self::HALF, 1308 }); 1309 /// The `expanded` keyword. 1310 pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint { 1311 value: 125 << Self::FRACTION_BITS, 1312 }); 1313 /// The `extra-expanded` keyword. 1314 pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint { 1315 value: 150 << Self::FRACTION_BITS, 1316 }); 1317 /// The `ultra-expanded` keyword. 1318 pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint { 1319 value: 200 << Self::FRACTION_BITS, 1320 }); 1321 1322 /// 100% 1323 pub fn hundred() -> Self { 1324 Self::NORMAL 1325 } 1326 1327 /// Converts to a computed percentage. 1328 #[inline] 1329 pub fn to_percentage(&self) -> Percentage { 1330 Percentage(self.0.to_float() / 100.0) 1331 } 1332 1333 /// Converts from a computed percentage value. 1334 pub fn from_percentage(p: f32) -> Self { 1335 Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0))) 1336 } 1337 1338 /// Returns a relevant stretch value from a keyword. 1339 /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop 1340 pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self { 1341 use specified::FontStretchKeyword::*; 1342 match kw { 1343 UltraCondensed => Self::ULTRA_CONDENSED, 1344 ExtraCondensed => Self::EXTRA_CONDENSED, 1345 Condensed => Self::CONDENSED, 1346 SemiCondensed => Self::SEMI_CONDENSED, 1347 Normal => Self::NORMAL, 1348 SemiExpanded => Self::SEMI_EXPANDED, 1349 Expanded => Self::EXPANDED, 1350 ExtraExpanded => Self::EXTRA_EXPANDED, 1351 UltraExpanded => Self::ULTRA_EXPANDED, 1352 } 1353 } 1354 1355 /// Returns the stretch keyword if we map to one of the relevant values. 1356 pub fn as_keyword(&self) -> Option<specified::FontStretchKeyword> { 1357 use specified::FontStretchKeyword::*; 1358 // TODO: Can we use match here? 1359 if *self == Self::ULTRA_CONDENSED { 1360 return Some(UltraCondensed); 1361 } 1362 if *self == Self::EXTRA_CONDENSED { 1363 return Some(ExtraCondensed); 1364 } 1365 if *self == Self::CONDENSED { 1366 return Some(Condensed); 1367 } 1368 if *self == Self::SEMI_CONDENSED { 1369 return Some(SemiCondensed); 1370 } 1371 if *self == Self::NORMAL { 1372 return Some(Normal); 1373 } 1374 if *self == Self::SEMI_EXPANDED { 1375 return Some(SemiExpanded); 1376 } 1377 if *self == Self::EXPANDED { 1378 return Some(Expanded); 1379 } 1380 if *self == Self::EXTRA_EXPANDED { 1381 return Some(ExtraExpanded); 1382 } 1383 if *self == Self::ULTRA_EXPANDED { 1384 return Some(UltraExpanded); 1385 } 1386 None 1387 } 1388 } 1389 1390 impl ToCss for FontStretch { 1391 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 1392 where 1393 W: fmt::Write, 1394 { 1395 self.to_percentage().to_css(dest) 1396 } 1397 } 1398 1399 impl ToAnimatedValue for FontStretch { 1400 type AnimatedValue = Percentage; 1401 1402 #[inline] 1403 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue { 1404 self.to_percentage() 1405 } 1406 1407 #[inline] 1408 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 1409 Self::from_percentage(animated.0) 1410 } 1411 } 1412 1413 /// A computed value for the `line-height` property. 1414 pub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>; 1415 1416 impl ToResolvedValue for LineHeight { 1417 type ResolvedValue = Self; 1418 1419 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue { 1420 #[cfg(feature = "gecko")] 1421 { 1422 // Resolve <number> to an absolute <length> based on font size. 1423 if matches!(self, Self::Normal | Self::MozBlockHeight) { 1424 return self; 1425 } 1426 let wm = context.style.writing_mode; 1427 Self::Length( 1428 context 1429 .device 1430 .calc_line_height( 1431 context.style.get_font(), 1432 wm, 1433 Some(context.element_info.element), 1434 ) 1435 .to_resolved_value(context), 1436 ) 1437 } 1438 #[cfg(feature = "servo")] 1439 { 1440 if let LineHeight::Number(num) = &self { 1441 let size = context.style.get_font().clone_font_size().computed_size(); 1442 LineHeight::Length(NonNegativeLength::new(size.px() * num.0)) 1443 } else { 1444 self 1445 } 1446 } 1447 } 1448 1449 #[inline] 1450 fn from_resolved_value(value: Self::ResolvedValue) -> Self { 1451 value 1452 } 1453 }