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