font.rs (65446B)
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 //! Specified values for font properties 6 7 use crate::context::QuirksMode; 8 use crate::derives::*; 9 use crate::parser::{Parse, ParserContext}; 10 use crate::values::computed::font::{FamilyName, FontFamilyList, SingleFontFamily}; 11 use crate::values::computed::Percentage as ComputedPercentage; 12 use crate::values::computed::{font as computed, Length, NonNegativeLength}; 13 use crate::values::computed::{CSSPixelLength, Context, ToComputedValue}; 14 use crate::values::generics::font::{ 15 self as generics, FeatureTagValue, FontSettings, FontTag, GenericLineHeight, VariationValue, 16 }; 17 use crate::values::generics::NonNegative; 18 use crate::values::specified::length::{FontBaseSize, LineHeightBase, PX_PER_PT}; 19 use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage}; 20 use crate::values::specified::{ 21 FontRelativeLength, NoCalcLength, NonNegativeLengthPercentage, NonNegativeNumber, 22 NonNegativePercentage, Number, 23 }; 24 use crate::values::{serialize_atom_identifier, CustomIdent, SelectorParseErrorKind}; 25 use crate::Atom; 26 use cssparser::{match_ignore_ascii_case, Parser, Token}; 27 #[cfg(feature = "gecko")] 28 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf}; 29 use std::fmt::{self, Write}; 30 use style_traits::{CssWriter, KeywordsCollectFn, ParseError}; 31 use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; 32 33 // FIXME(emilio): The system font code is copy-pasta, and should be cleaned up. 34 macro_rules! system_font_methods { 35 ($ty:ident, $field:ident) => { 36 system_font_methods!($ty); 37 38 fn compute_system(&self, _context: &Context) -> <$ty as ToComputedValue>::ComputedValue { 39 debug_assert!(matches!(*self, $ty::System(..))); 40 #[cfg(feature = "gecko")] 41 { 42 _context.cached_system_font.as_ref().unwrap().$field.clone() 43 } 44 #[cfg(feature = "servo")] 45 { 46 unreachable!() 47 } 48 } 49 }; 50 51 ($ty:ident) => { 52 /// Get a specified value that represents a system font. 53 pub fn system_font(f: SystemFont) -> Self { 54 $ty::System(f) 55 } 56 57 /// Retreive a SystemFont from the specified value. 58 pub fn get_system(&self) -> Option<SystemFont> { 59 if let $ty::System(s) = *self { 60 Some(s) 61 } else { 62 None 63 } 64 } 65 }; 66 } 67 68 /// System fonts. 69 #[repr(u8)] 70 #[derive( 71 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, 72 )] 73 #[allow(missing_docs)] 74 #[cfg(feature = "gecko")] 75 pub enum SystemFont { 76 /// https://drafts.csswg.org/css-fonts/#valdef-font-caption 77 Caption, 78 /// https://drafts.csswg.org/css-fonts/#valdef-font-icon 79 Icon, 80 /// https://drafts.csswg.org/css-fonts/#valdef-font-menu 81 Menu, 82 /// https://drafts.csswg.org/css-fonts/#valdef-font-message-box 83 MessageBox, 84 /// https://drafts.csswg.org/css-fonts/#valdef-font-small-caption 85 SmallCaption, 86 /// https://drafts.csswg.org/css-fonts/#valdef-font-status-bar 87 StatusBar, 88 /// Internal system font, used by the `<menupopup>`s on macOS. 89 #[parse(condition = "ParserContext::chrome_rules_enabled")] 90 MozPullDownMenu, 91 /// Internal system font, used for `<button>` elements. 92 #[parse(condition = "ParserContext::chrome_rules_enabled")] 93 MozButton, 94 /// Internal font, used by `<select>` elements. 95 #[parse(condition = "ParserContext::chrome_rules_enabled")] 96 MozList, 97 /// Internal font, used by `<input>` elements. 98 #[parse(condition = "ParserContext::chrome_rules_enabled")] 99 MozField, 100 #[css(skip)] 101 End, // Just for indexing purposes. 102 } 103 104 // We don't parse system fonts in servo, but in the interest of not 105 // littering a lot of code with `if engine == "gecko"` conditionals, 106 // we have a dummy system font module that does nothing 107 108 #[derive( 109 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, 110 )] 111 #[allow(missing_docs)] 112 #[cfg(feature = "servo")] 113 /// void enum for system font, can never exist 114 pub enum SystemFont {} 115 116 #[allow(missing_docs)] 117 #[cfg(feature = "servo")] 118 impl SystemFont { 119 pub fn parse(_: &mut Parser) -> Result<Self, ()> { 120 Err(()) 121 } 122 } 123 124 const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8; 125 const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71; 126 127 /// The minimum font-weight value per: 128 /// 129 /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values 130 pub const MIN_FONT_WEIGHT: f32 = 1.; 131 132 /// The maximum font-weight value per: 133 /// 134 /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values 135 pub const MAX_FONT_WEIGHT: f32 = 1000.; 136 137 /// A specified font-weight value. 138 /// 139 /// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight 140 #[derive( 141 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped, 142 )] 143 pub enum FontWeight { 144 /// `<font-weight-absolute>` 145 Absolute(AbsoluteFontWeight), 146 /// Bolder variant 147 Bolder, 148 /// Lighter variant 149 Lighter, 150 /// System font variant. 151 #[css(skip)] 152 System(SystemFont), 153 } 154 155 impl FontWeight { 156 system_font_methods!(FontWeight, font_weight); 157 158 /// `normal` 159 #[inline] 160 pub fn normal() -> Self { 161 FontWeight::Absolute(AbsoluteFontWeight::Normal) 162 } 163 164 /// Get a specified FontWeight from a gecko keyword 165 pub fn from_gecko_keyword(kw: u32) -> Self { 166 debug_assert!(kw % 100 == 0); 167 debug_assert!(kw as f32 <= MAX_FONT_WEIGHT); 168 FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32))) 169 } 170 } 171 172 impl ToComputedValue for FontWeight { 173 type ComputedValue = computed::FontWeight; 174 175 #[inline] 176 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 177 match *self { 178 FontWeight::Absolute(ref abs) => abs.compute(), 179 FontWeight::Bolder => context 180 .builder 181 .get_parent_font() 182 .clone_font_weight() 183 .bolder(), 184 FontWeight::Lighter => context 185 .builder 186 .get_parent_font() 187 .clone_font_weight() 188 .lighter(), 189 FontWeight::System(_) => self.compute_system(context), 190 } 191 } 192 193 #[inline] 194 fn from_computed_value(computed: &computed::FontWeight) -> Self { 195 FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::from_computed_value( 196 &computed.value(), 197 ))) 198 } 199 } 200 201 /// An absolute font-weight value for a @font-face rule. 202 /// 203 /// https://drafts.csswg.org/css-fonts-4/#font-weight-absolute-values 204 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] 205 pub enum AbsoluteFontWeight { 206 /// A `<number>`, with the additional constraints specified in: 207 /// 208 /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values 209 Weight(Number), 210 /// Normal font weight. Same as 400. 211 Normal, 212 /// Bold font weight. Same as 700. 213 Bold, 214 } 215 216 impl AbsoluteFontWeight { 217 /// Returns the computed value for this absolute font weight. 218 pub fn compute(&self) -> computed::FontWeight { 219 match *self { 220 AbsoluteFontWeight::Weight(weight) => computed::FontWeight::from_float(weight.get()), 221 AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL, 222 AbsoluteFontWeight::Bold => computed::FontWeight::BOLD, 223 } 224 } 225 } 226 227 impl Parse for AbsoluteFontWeight { 228 fn parse<'i, 't>( 229 context: &ParserContext, 230 input: &mut Parser<'i, 't>, 231 ) -> Result<Self, ParseError<'i>> { 232 if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) { 233 // We could add another AllowedNumericType value, but it doesn't 234 // seem worth it just for a single property with such a weird range, 235 // so we do the clamping here manually. 236 if !number.was_calc() 237 && (number.get() < MIN_FONT_WEIGHT || number.get() > MAX_FONT_WEIGHT) 238 { 239 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); 240 } 241 return Ok(AbsoluteFontWeight::Weight(number)); 242 } 243 244 Ok(try_match_ident_ignore_ascii_case! { input, 245 "normal" => AbsoluteFontWeight::Normal, 246 "bold" => AbsoluteFontWeight::Bold, 247 }) 248 } 249 } 250 251 /// The specified value of the `font-style` property, without the system font 252 /// crap. 253 pub type SpecifiedFontStyle = generics::FontStyle<Angle>; 254 255 impl ToCss for SpecifiedFontStyle { 256 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 257 where 258 W: Write, 259 { 260 match *self { 261 generics::FontStyle::Italic => dest.write_str("italic"), 262 generics::FontStyle::Oblique(ref angle) => { 263 // Not angle.is_zero() because we don't want to serialize 264 // `oblique calc(0deg)` as `normal`. 265 if *angle == Angle::zero() { 266 dest.write_str("normal")?; 267 } else { 268 dest.write_str("oblique")?; 269 if *angle != Self::default_angle() { 270 dest.write_char(' ')?; 271 angle.to_css(dest)?; 272 } 273 } 274 Ok(()) 275 }, 276 } 277 } 278 } 279 280 impl Parse for SpecifiedFontStyle { 281 fn parse<'i, 't>( 282 context: &ParserContext, 283 input: &mut Parser<'i, 't>, 284 ) -> Result<Self, ParseError<'i>> { 285 Ok(try_match_ident_ignore_ascii_case! { input, 286 "normal" => generics::FontStyle::normal(), 287 "italic" => generics::FontStyle::Italic, 288 "oblique" => { 289 let angle = input.try_parse(|input| Self::parse_angle(context, input)) 290 .unwrap_or_else(|_| Self::default_angle()); 291 292 generics::FontStyle::Oblique(angle) 293 }, 294 }) 295 } 296 } 297 298 impl ToComputedValue for SpecifiedFontStyle { 299 type ComputedValue = computed::FontStyle; 300 301 fn to_computed_value(&self, _: &Context) -> Self::ComputedValue { 302 match *self { 303 Self::Italic => computed::FontStyle::ITALIC, 304 Self::Oblique(ref angle) => computed::FontStyle::oblique(angle.degrees()), 305 } 306 } 307 308 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 309 if *computed == computed::FontStyle::ITALIC { 310 return Self::Italic; 311 } 312 let degrees = computed.oblique_degrees(); 313 generics::FontStyle::Oblique(Angle::from_degrees(degrees, /* was_calc = */ false)) 314 } 315 } 316 317 /// From https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle: 318 /// 319 /// Values less than -90deg or values greater than 90deg are 320 /// invalid and are treated as parse errors. 321 /// 322 /// The maximum angle value that `font-style: oblique` should compute to. 323 pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.; 324 325 /// The minimum angle value that `font-style: oblique` should compute to. 326 pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.; 327 328 impl SpecifiedFontStyle { 329 /// Gets a clamped angle in degrees from a specified Angle. 330 pub fn compute_angle_degrees(angle: &Angle) -> f32 { 331 angle 332 .degrees() 333 .max(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES) 334 .min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES) 335 } 336 337 /// Parse a suitable angle for font-style: oblique. 338 pub fn parse_angle<'i, 't>( 339 context: &ParserContext, 340 input: &mut Parser<'i, 't>, 341 ) -> Result<Angle, ParseError<'i>> { 342 let angle = Angle::parse(context, input)?; 343 if angle.was_calc() { 344 return Ok(angle); 345 } 346 347 let degrees = angle.degrees(); 348 if degrees < FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES 349 || degrees > FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES 350 { 351 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); 352 } 353 return Ok(angle); 354 } 355 356 /// The default angle for `font-style: oblique`. 357 pub fn default_angle() -> Angle { 358 Angle::from_degrees( 359 computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32, 360 /* was_calc = */ false, 361 ) 362 } 363 } 364 365 /// The specified value of the `font-style` property. 366 #[derive( 367 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped, 368 )] 369 #[allow(missing_docs)] 370 pub enum FontStyle { 371 Specified(SpecifiedFontStyle), 372 #[css(skip)] 373 System(SystemFont), 374 } 375 376 impl FontStyle { 377 /// Return the `normal` value. 378 #[inline] 379 pub fn normal() -> Self { 380 FontStyle::Specified(generics::FontStyle::normal()) 381 } 382 383 system_font_methods!(FontStyle, font_style); 384 } 385 386 impl ToComputedValue for FontStyle { 387 type ComputedValue = computed::FontStyle; 388 389 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 390 match *self { 391 FontStyle::Specified(ref specified) => specified.to_computed_value(context), 392 FontStyle::System(..) => self.compute_system(context), 393 } 394 } 395 396 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 397 FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed)) 398 } 399 } 400 401 /// A value for the `font-stretch` property. 402 /// 403 /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop 404 #[allow(missing_docs)] 405 #[derive( 406 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped, 407 )] 408 pub enum FontStretch { 409 Stretch(NonNegativePercentage), 410 Keyword(FontStretchKeyword), 411 #[css(skip)] 412 System(SystemFont), 413 } 414 415 /// A keyword value for `font-stretch`. 416 #[derive( 417 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, 418 )] 419 #[allow(missing_docs)] 420 pub enum FontStretchKeyword { 421 Normal, 422 Condensed, 423 UltraCondensed, 424 ExtraCondensed, 425 SemiCondensed, 426 SemiExpanded, 427 Expanded, 428 ExtraExpanded, 429 UltraExpanded, 430 } 431 432 impl FontStretchKeyword { 433 /// Turns the keyword into a computed value. 434 pub fn compute(&self) -> computed::FontStretch { 435 computed::FontStretch::from_keyword(*self) 436 } 437 438 /// Does the opposite operation to `compute`, in order to serialize keywords 439 /// if possible. 440 pub fn from_percentage(p: f32) -> Option<Self> { 441 computed::FontStretch::from_percentage(p).as_keyword() 442 } 443 } 444 445 impl FontStretch { 446 /// `normal`. 447 pub fn normal() -> Self { 448 FontStretch::Keyword(FontStretchKeyword::Normal) 449 } 450 451 system_font_methods!(FontStretch, font_stretch); 452 } 453 454 impl ToComputedValue for FontStretch { 455 type ComputedValue = computed::FontStretch; 456 457 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 458 match *self { 459 FontStretch::Stretch(ref percentage) => { 460 let percentage = percentage.to_computed_value(context).0; 461 computed::FontStretch::from_percentage(percentage.0) 462 }, 463 FontStretch::Keyword(ref kw) => kw.compute(), 464 FontStretch::System(_) => self.compute_system(context), 465 } 466 } 467 468 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 469 FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative( 470 computed.to_percentage(), 471 ))) 472 } 473 } 474 475 /// CSS font keywords 476 #[derive( 477 Animate, 478 Clone, 479 ComputeSquaredDistance, 480 Copy, 481 Debug, 482 MallocSizeOf, 483 Parse, 484 PartialEq, 485 SpecifiedValueInfo, 486 ToAnimatedValue, 487 ToAnimatedZero, 488 ToComputedValue, 489 ToCss, 490 ToResolvedValue, 491 ToShmem, 492 Serialize, 493 Deserialize, 494 )] 495 #[allow(missing_docs)] 496 #[repr(u8)] 497 pub enum FontSizeKeyword { 498 #[css(keyword = "xx-small")] 499 XXSmall, 500 XSmall, 501 Small, 502 Medium, 503 Large, 504 XLarge, 505 #[css(keyword = "xx-large")] 506 XXLarge, 507 #[css(keyword = "xxx-large")] 508 XXXLarge, 509 /// Indicate whether to apply font-size: math is specified so that extra 510 /// scaling due to math-depth changes is applied during the cascade. 511 #[cfg(feature = "gecko")] 512 Math, 513 #[css(skip)] 514 None, 515 } 516 517 impl FontSizeKeyword { 518 /// Convert to an HTML <font size> value 519 #[inline] 520 pub fn html_size(self) -> u8 { 521 self as u8 522 } 523 524 /// Returns true if the font size is the math keyword 525 #[cfg(feature = "gecko")] 526 pub fn is_math(self) -> bool { 527 matches!(self, Self::Math) 528 } 529 530 /// Returns true if the font size is the math keyword 531 #[cfg(feature = "servo")] 532 pub fn is_math(self) -> bool { 533 false 534 } 535 } 536 537 impl Default for FontSizeKeyword { 538 fn default() -> Self { 539 FontSizeKeyword::Medium 540 } 541 } 542 543 #[derive( 544 Animate, 545 Clone, 546 ComputeSquaredDistance, 547 Copy, 548 Debug, 549 MallocSizeOf, 550 PartialEq, 551 ToAnimatedValue, 552 ToAnimatedZero, 553 ToComputedValue, 554 ToCss, 555 ToResolvedValue, 556 ToShmem, 557 )] 558 #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))] 559 /// Additional information for keyword-derived font sizes. 560 pub struct KeywordInfo { 561 /// The keyword used 562 pub kw: FontSizeKeyword, 563 /// A factor to be multiplied by the computed size of the keyword 564 #[css(skip)] 565 pub factor: f32, 566 /// An additional fixed offset to add to the kw * factor in the case of 567 /// `calc()`. 568 #[css(skip)] 569 pub offset: CSSPixelLength, 570 } 571 572 impl KeywordInfo { 573 /// KeywordInfo value for font-size: medium 574 pub fn medium() -> Self { 575 Self::new(FontSizeKeyword::Medium) 576 } 577 578 /// KeywordInfo value for font-size: none 579 pub fn none() -> Self { 580 Self::new(FontSizeKeyword::None) 581 } 582 583 fn new(kw: FontSizeKeyword) -> Self { 584 KeywordInfo { 585 kw, 586 factor: 1., 587 offset: CSSPixelLength::new(0.), 588 } 589 } 590 591 /// Computes the final size for this font-size keyword, accounting for 592 /// text-zoom. 593 fn to_computed_value(&self, context: &Context) -> CSSPixelLength { 594 debug_assert_ne!(self.kw, FontSizeKeyword::None); 595 #[cfg(feature = "gecko")] 596 debug_assert_ne!(self.kw, FontSizeKeyword::Math); 597 let base = context.maybe_zoom_text(self.kw.to_length(context).0); 598 let zoom_factor = context.style().effective_zoom.value(); 599 CSSPixelLength::new(base.px() * self.factor * zoom_factor) 600 + context.maybe_zoom_text(self.offset) 601 } 602 603 /// Given a parent keyword info (self), apply an additional factor/offset to 604 /// it. 605 fn compose(self, factor: f32) -> Self { 606 if self.kw == FontSizeKeyword::None { 607 return self; 608 } 609 KeywordInfo { 610 kw: self.kw, 611 factor: self.factor * factor, 612 offset: self.offset * factor, 613 } 614 } 615 } 616 617 impl SpecifiedValueInfo for KeywordInfo { 618 fn collect_completion_keywords(f: KeywordsCollectFn) { 619 <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f); 620 } 621 } 622 623 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)] 624 /// A specified font-size value 625 pub enum FontSize { 626 /// A length; e.g. 10px. 627 Length(LengthPercentage), 628 /// A keyword value, along with a ratio and absolute offset. 629 /// The ratio in any specified keyword value 630 /// will be 1 (with offset 0), but we cascade keywordness even 631 /// after font-relative (percent and em) values 632 /// have been applied, which is where the ratio 633 /// comes in. The offset comes in if we cascaded a calc value, 634 /// where the font-relative portion (em and percentage) will 635 /// go into the ratio, and the remaining units all computed together 636 /// will go into the offset. 637 /// See bug 1355707. 638 Keyword(KeywordInfo), 639 /// font-size: smaller 640 Smaller, 641 /// font-size: larger 642 Larger, 643 /// Derived from a specified system font. 644 #[css(skip)] 645 System(SystemFont), 646 } 647 648 /// Specifies a prioritized list of font family names or generic family names. 649 #[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem, ToTyped)] 650 #[cfg_attr(feature = "servo", derive(Hash))] 651 pub enum FontFamily { 652 /// List of `font-family` 653 #[css(comma)] 654 Values(#[css(iterable)] FontFamilyList), 655 /// System font 656 #[css(skip)] 657 System(SystemFont), 658 } 659 660 impl FontFamily { 661 system_font_methods!(FontFamily, font_family); 662 } 663 664 impl ToComputedValue for FontFamily { 665 type ComputedValue = computed::FontFamily; 666 667 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 668 match *self { 669 FontFamily::Values(ref list) => computed::FontFamily { 670 families: list.clone(), 671 is_system_font: false, 672 is_initial: false, 673 }, 674 FontFamily::System(_) => self.compute_system(context), 675 } 676 } 677 678 fn from_computed_value(other: &computed::FontFamily) -> Self { 679 FontFamily::Values(other.families.clone()) 680 } 681 } 682 683 #[cfg(feature = "gecko")] 684 impl MallocSizeOf for FontFamily { 685 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { 686 match *self { 687 FontFamily::Values(ref v) => { 688 // Although the family list is refcounted, we always attribute 689 // its size to the specified value. 690 v.list.unconditional_size_of(ops) 691 }, 692 FontFamily::System(_) => 0, 693 } 694 } 695 } 696 697 impl Parse for FontFamily { 698 /// <family-name># 699 /// <family-name> = <string> | [ <ident>+ ] 700 /// TODO: <generic-family> 701 fn parse<'i, 't>( 702 context: &ParserContext, 703 input: &mut Parser<'i, 't>, 704 ) -> Result<FontFamily, ParseError<'i>> { 705 let values = 706 input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?; 707 Ok(FontFamily::Values(FontFamilyList { 708 list: crate::ArcSlice::from_iter(values.into_iter()), 709 })) 710 } 711 } 712 713 impl SpecifiedValueInfo for FontFamily {} 714 715 /// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other 716 /// way around because we want the former to exclude generic family keywords. 717 impl Parse for FamilyName { 718 fn parse<'i, 't>( 719 context: &ParserContext, 720 input: &mut Parser<'i, 't>, 721 ) -> Result<Self, ParseError<'i>> { 722 match SingleFontFamily::parse(context, input) { 723 Ok(SingleFontFamily::FamilyName(name)) => Ok(name), 724 Ok(SingleFontFamily::Generic(_)) => { 725 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 726 }, 727 Err(e) => Err(e), 728 } 729 } 730 } 731 732 /// A factor for one of the font-size-adjust metrics, which may be either a number 733 /// or the `from-font` keyword. 734 #[derive( 735 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, 736 )] 737 pub enum FontSizeAdjustFactor { 738 /// An explicitly-specified number. 739 Number(NonNegativeNumber), 740 /// The from-font keyword: resolve the number from font metrics. 741 FromFont, 742 } 743 744 /// Specified value for font-size-adjust, intended to help 745 /// preserve the readability of text when font fallback occurs. 746 /// 747 /// https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop 748 pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>; 749 750 impl Parse for FontSizeAdjust { 751 fn parse<'i, 't>( 752 context: &ParserContext, 753 input: &mut Parser<'i, 't>, 754 ) -> Result<Self, ParseError<'i>> { 755 let location = input.current_source_location(); 756 // First check if we have an adjustment factor without a metrics-basis keyword. 757 if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) { 758 return Ok(Self::ExHeight(factor)); 759 } 760 761 let ident = input.expect_ident()?; 762 let basis = match_ignore_ascii_case! { &ident, 763 "none" => return Ok(Self::None), 764 // Check for size adjustment basis keywords. 765 "ex-height" => Self::ExHeight, 766 "cap-height" => Self::CapHeight, 767 "ch-width" => Self::ChWidth, 768 "ic-width" => Self::IcWidth, 769 "ic-height" => Self::IcHeight, 770 // Unknown keyword. 771 _ => return Err(location.new_custom_error( 772 SelectorParseErrorKind::UnexpectedIdent(ident.clone()) 773 )), 774 }; 775 776 Ok(basis(FontSizeAdjustFactor::parse(context, input)?)) 777 } 778 } 779 780 /// This is the ratio applied for font-size: larger 781 /// and smaller by both Firefox and Chrome 782 const LARGER_FONT_SIZE_RATIO: f32 = 1.2; 783 784 /// The default font size. 785 pub const FONT_MEDIUM_PX: f32 = 16.0; 786 /// The default line height. 787 pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2; 788 /// The default ex height -- https://drafts.csswg.org/css-values/#ex 789 /// > In the cases where it is impossible or impractical to determine the x-height, a value of 0.5em must be assumed 790 pub const FONT_MEDIUM_EX_PX: f32 = FONT_MEDIUM_PX * 0.5; 791 /// The default cap height -- https://drafts.csswg.org/css-values/#cap 792 /// > In the cases where it is impossible or impractical to determine the cap-height, the font’s ascent must be used 793 pub const FONT_MEDIUM_CAP_PX: f32 = FONT_MEDIUM_PX; 794 /// The default advance measure -- https://drafts.csswg.org/css-values/#ch 795 /// > Thus, the ch unit falls back to 0.5em in the general case 796 pub const FONT_MEDIUM_CH_PX: f32 = FONT_MEDIUM_PX * 0.5; 797 /// The default idographic advance measure -- https://drafts.csswg.org/css-values/#ic 798 /// > In the cases where it is impossible or impractical to determine the ideographic advance measure, it must be assumed to be 1em 799 pub const FONT_MEDIUM_IC_PX: f32 = FONT_MEDIUM_PX; 800 801 impl FontSizeKeyword { 802 #[inline] 803 fn to_length(&self, cx: &Context) -> NonNegativeLength { 804 let font = cx.style().get_font(); 805 806 #[cfg(feature = "servo")] 807 let family = &font.font_family.families; 808 #[cfg(feature = "gecko")] 809 let family = &font.mFont.family.families; 810 811 let generic = family 812 .single_generic() 813 .unwrap_or(computed::GenericFontFamily::None); 814 815 #[cfg(feature = "gecko")] 816 let base_size = unsafe { 817 Atom::with(font.mLanguage.mRawPtr, |language| { 818 cx.device().base_size_for_generic(language, generic) 819 }) 820 }; 821 #[cfg(feature = "servo")] 822 let base_size = cx.device().base_size_for_generic(generic); 823 824 self.to_length_without_context(cx.quirks_mode, base_size) 825 } 826 827 /// Resolve a keyword length without any context, with explicit arguments. 828 #[inline] 829 pub fn to_length_without_context( 830 &self, 831 quirks_mode: QuirksMode, 832 base_size: Length, 833 ) -> NonNegativeLength { 834 #[cfg(feature = "gecko")] 835 debug_assert_ne!(*self, FontSizeKeyword::Math); 836 // The tables in this function are originally from 837 // nsRuleNode::CalcFontPointSize in Gecko: 838 // 839 // https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#3150 840 // 841 // Mapping from base size and HTML size to pixels 842 // The first index is (base_size - 9), the second is the 843 // HTML size. "0" is CSS keyword xx-small, not HTML size 0, 844 // since HTML size 0 is the same as 1. 845 // 846 // xxs xs s m l xl xxl - 847 // - 0/1 2 3 4 5 6 7 848 static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [ 849 [9, 9, 9, 9, 11, 14, 18, 27], 850 [9, 9, 9, 10, 12, 15, 20, 30], 851 [9, 9, 10, 11, 13, 17, 22, 33], 852 [9, 9, 10, 12, 14, 18, 24, 36], 853 [9, 10, 12, 13, 16, 20, 26, 39], 854 [9, 10, 12, 14, 17, 21, 28, 42], 855 [9, 10, 13, 15, 18, 23, 30, 45], 856 [9, 10, 13, 16, 18, 24, 32, 48], 857 ]; 858 859 // This table gives us compatibility with WinNav4 for the default fonts only. 860 // In WinNav4, the default fonts were: 861 // 862 // Times/12pt == Times/16px at 96ppi 863 // Courier/10pt == Courier/13px at 96ppi 864 // 865 // xxs xs s m l xl xxl - 866 // - 1 2 3 4 5 6 7 867 static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [ 868 [9, 9, 9, 9, 11, 14, 18, 28], 869 [9, 9, 9, 10, 12, 15, 20, 31], 870 [9, 9, 9, 11, 13, 17, 22, 34], 871 [9, 9, 10, 12, 14, 18, 24, 37], 872 [9, 9, 10, 13, 16, 20, 26, 40], 873 [9, 9, 11, 14, 17, 21, 28, 42], 874 [9, 10, 12, 15, 17, 23, 30, 45], 875 [9, 10, 13, 16, 18, 24, 32, 48], 876 ]; 877 878 static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300]; 879 let base_size_px = base_size.px().round() as i32; 880 let html_size = self.html_size() as usize; 881 NonNegative(if base_size_px >= 9 && base_size_px <= 16 { 882 let mapping = if quirks_mode == QuirksMode::Quirks { 883 QUIRKS_FONT_SIZE_MAPPING 884 } else { 885 FONT_SIZE_MAPPING 886 }; 887 Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32) 888 } else { 889 base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0 890 }) 891 } 892 } 893 894 impl FontSize { 895 /// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size> 896 pub fn from_html_size(size: u8) -> Self { 897 FontSize::Keyword(KeywordInfo::new(match size { 898 // If value is less than 1, let it be 1. 899 0 | 1 => FontSizeKeyword::XSmall, 900 2 => FontSizeKeyword::Small, 901 3 => FontSizeKeyword::Medium, 902 4 => FontSizeKeyword::Large, 903 5 => FontSizeKeyword::XLarge, 904 6 => FontSizeKeyword::XXLarge, 905 // If value is greater than 7, let it be 7. 906 _ => FontSizeKeyword::XXXLarge, 907 })) 908 } 909 910 /// Compute it against a given base font size 911 pub fn to_computed_value_against( 912 &self, 913 context: &Context, 914 base_size: FontBaseSize, 915 line_height_base: LineHeightBase, 916 ) -> computed::FontSize { 917 let compose_keyword = |factor| { 918 context 919 .style() 920 .get_parent_font() 921 .clone_font_size() 922 .keyword_info 923 .compose(factor) 924 }; 925 let mut info = KeywordInfo::none(); 926 let size = match *self { 927 FontSize::Length(LengthPercentage::Length(ref l)) => { 928 if let NoCalcLength::FontRelative(ref value) = *l { 929 if let FontRelativeLength::Em(em) = *value { 930 // If the parent font was keyword-derived, this is 931 // too. Tack the em unit onto the factor 932 info = compose_keyword(em); 933 } 934 } 935 let result = 936 l.to_computed_value_with_base_size(context, base_size, line_height_base); 937 if l.should_zoom_text() { 938 context.maybe_zoom_text(result) 939 } else { 940 result 941 } 942 }, 943 FontSize::Length(LengthPercentage::Percentage(pc)) => { 944 // If the parent font was keyword-derived, this is too. 945 // Tack the % onto the factor 946 info = compose_keyword(pc.0); 947 (base_size.resolve(context).computed_size() * pc.0).normalized() 948 }, 949 FontSize::Length(LengthPercentage::Calc(ref calc)) => { 950 let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base); 951 calc.resolve(base_size.resolve(context).computed_size()) 952 }, 953 FontSize::Keyword(i) => { 954 if i.kw.is_math() { 955 // Scaling is done in recompute_math_font_size_if_needed(). 956 info = compose_keyword(1.); 957 // i.kw will always be FontSizeKeyword::Math here. But writing it this 958 // allows this code to compile for servo where the Math variant is cfg'd out. 959 info.kw = i.kw; 960 FontRelativeLength::Em(1.).to_computed_value( 961 context, 962 base_size, 963 line_height_base, 964 ) 965 } else { 966 // As a specified keyword, this is keyword derived 967 info = i; 968 i.to_computed_value(context).clamp_to_non_negative() 969 } 970 }, 971 FontSize::Smaller => { 972 info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO); 973 FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO).to_computed_value( 974 context, 975 base_size, 976 line_height_base, 977 ) 978 }, 979 FontSize::Larger => { 980 info = compose_keyword(LARGER_FONT_SIZE_RATIO); 981 FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value( 982 context, 983 base_size, 984 line_height_base, 985 ) 986 }, 987 988 FontSize::System(_) => { 989 #[cfg(feature = "servo")] 990 { 991 unreachable!() 992 } 993 #[cfg(feature = "gecko")] 994 { 995 context 996 .cached_system_font 997 .as_ref() 998 .unwrap() 999 .font_size 1000 .computed_size() 1001 } 1002 }, 1003 }; 1004 computed::FontSize { 1005 computed_size: NonNegative(size), 1006 used_size: NonNegative(size), 1007 keyword_info: info, 1008 } 1009 } 1010 } 1011 1012 impl ToComputedValue for FontSize { 1013 type ComputedValue = computed::FontSize; 1014 1015 #[inline] 1016 fn to_computed_value(&self, context: &Context) -> computed::FontSize { 1017 self.to_computed_value_against( 1018 context, 1019 FontBaseSize::InheritedStyle, 1020 LineHeightBase::InheritedStyle, 1021 ) 1022 } 1023 1024 #[inline] 1025 fn from_computed_value(computed: &computed::FontSize) -> Self { 1026 FontSize::Length(LengthPercentage::Length( 1027 ToComputedValue::from_computed_value(&computed.computed_size()), 1028 )) 1029 } 1030 } 1031 1032 impl FontSize { 1033 system_font_methods!(FontSize); 1034 1035 /// Get initial value for specified font size. 1036 #[inline] 1037 pub fn medium() -> Self { 1038 FontSize::Keyword(KeywordInfo::medium()) 1039 } 1040 1041 /// Parses a font-size, with quirks. 1042 pub fn parse_quirky<'i, 't>( 1043 context: &ParserContext, 1044 input: &mut Parser<'i, 't>, 1045 allow_quirks: AllowQuirks, 1046 ) -> Result<FontSize, ParseError<'i>> { 1047 if let Ok(lp) = input 1048 .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks)) 1049 { 1050 return Ok(FontSize::Length(lp)); 1051 } 1052 1053 if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) { 1054 return Ok(FontSize::Keyword(KeywordInfo::new(kw))); 1055 } 1056 1057 try_match_ident_ignore_ascii_case! { input, 1058 "smaller" => Ok(FontSize::Smaller), 1059 "larger" => Ok(FontSize::Larger), 1060 } 1061 } 1062 } 1063 1064 impl Parse for FontSize { 1065 /// <length> | <percentage> | <absolute-size> | <relative-size> 1066 fn parse<'i, 't>( 1067 context: &ParserContext, 1068 input: &mut Parser<'i, 't>, 1069 ) -> Result<FontSize, ParseError<'i>> { 1070 FontSize::parse_quirky(context, input, AllowQuirks::No) 1071 } 1072 } 1073 1074 bitflags! { 1075 #[derive(Clone, Copy)] 1076 /// Flags of variant alternates in bit 1077 struct VariantAlternatesParsingFlags: u8 { 1078 /// None of variant alternates enabled 1079 const NORMAL = 0; 1080 /// Historical forms 1081 const HISTORICAL_FORMS = 0x01; 1082 /// Stylistic Alternates 1083 const STYLISTIC = 0x02; 1084 /// Stylistic Sets 1085 const STYLESET = 0x04; 1086 /// Character Variant 1087 const CHARACTER_VARIANT = 0x08; 1088 /// Swash glyphs 1089 const SWASH = 0x10; 1090 /// Ornaments glyphs 1091 const ORNAMENTS = 0x20; 1092 /// Annotation forms 1093 const ANNOTATION = 0x40; 1094 } 1095 } 1096 1097 #[derive( 1098 Clone, 1099 Debug, 1100 MallocSizeOf, 1101 PartialEq, 1102 SpecifiedValueInfo, 1103 ToCss, 1104 ToComputedValue, 1105 ToResolvedValue, 1106 ToShmem, 1107 )] 1108 #[repr(C, u8)] 1109 /// Set of variant alternates 1110 pub enum VariantAlternates { 1111 /// Enables display of stylistic alternates 1112 #[css(function)] 1113 Stylistic(CustomIdent), 1114 /// Enables display with stylistic sets 1115 #[css(comma, function)] 1116 Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>), 1117 /// Enables display of specific character variants 1118 #[css(comma, function)] 1119 CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>), 1120 /// Enables display of swash glyphs 1121 #[css(function)] 1122 Swash(CustomIdent), 1123 /// Enables replacement of default glyphs with ornaments 1124 #[css(function)] 1125 Ornaments(CustomIdent), 1126 /// Enables display of alternate annotation forms 1127 #[css(function)] 1128 Annotation(CustomIdent), 1129 /// Enables display of historical forms 1130 HistoricalForms, 1131 } 1132 1133 #[derive( 1134 Clone, 1135 Debug, 1136 Default, 1137 MallocSizeOf, 1138 PartialEq, 1139 SpecifiedValueInfo, 1140 ToComputedValue, 1141 ToCss, 1142 ToResolvedValue, 1143 ToShmem, 1144 ToTyped, 1145 )] 1146 #[repr(transparent)] 1147 /// List of Variant Alternates 1148 pub struct FontVariantAlternates( 1149 #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>, 1150 ); 1151 1152 impl FontVariantAlternates { 1153 /// Returns the length of all variant alternates. 1154 pub fn len(&self) -> usize { 1155 self.0.iter().fold(0, |acc, alternate| match *alternate { 1156 VariantAlternates::Swash(_) 1157 | VariantAlternates::Stylistic(_) 1158 | VariantAlternates::Ornaments(_) 1159 | VariantAlternates::Annotation(_) => acc + 1, 1160 VariantAlternates::Styleset(ref slice) 1161 | VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(), 1162 _ => acc, 1163 }) 1164 } 1165 } 1166 1167 impl FontVariantAlternates { 1168 #[inline] 1169 /// Get initial specified value with VariantAlternatesList 1170 pub fn get_initial_specified_value() -> Self { 1171 Default::default() 1172 } 1173 } 1174 1175 impl Parse for FontVariantAlternates { 1176 /// normal | 1177 /// [ stylistic(<feature-value-name>) || 1178 /// historical-forms || 1179 /// styleset(<feature-value-name> #) || 1180 /// character-variant(<feature-value-name> #) || 1181 /// swash(<feature-value-name>) || 1182 /// ornaments(<feature-value-name>) || 1183 /// annotation(<feature-value-name>) ] 1184 fn parse<'i, 't>( 1185 _: &ParserContext, 1186 input: &mut Parser<'i, 't>, 1187 ) -> Result<FontVariantAlternates, ParseError<'i>> { 1188 if input 1189 .try_parse(|input| input.expect_ident_matching("normal")) 1190 .is_ok() 1191 { 1192 return Ok(Default::default()); 1193 } 1194 1195 let mut stylistic = None; 1196 let mut historical = None; 1197 let mut styleset = None; 1198 let mut character_variant = None; 1199 let mut swash = None; 1200 let mut ornaments = None; 1201 let mut annotation = None; 1202 1203 // Parse values for the various alternate types in any order. 1204 let mut parsed_alternates = VariantAlternatesParsingFlags::empty(); 1205 macro_rules! check_if_parsed( 1206 ($input:expr, $flag:path) => ( 1207 if parsed_alternates.contains($flag) { 1208 return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 1209 } 1210 parsed_alternates |= $flag; 1211 ) 1212 ); 1213 while let Ok(_) = input.try_parse(|input| match *input.next()? { 1214 Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => { 1215 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS); 1216 historical = Some(VariantAlternates::HistoricalForms); 1217 Ok(()) 1218 }, 1219 Token::Function(ref name) => { 1220 let name = name.clone(); 1221 input.parse_nested_block(|i| { 1222 match_ignore_ascii_case! { &name, 1223 "swash" => { 1224 check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH); 1225 let ident = CustomIdent::parse(i, &[])?; 1226 swash = Some(VariantAlternates::Swash(ident)); 1227 Ok(()) 1228 }, 1229 "stylistic" => { 1230 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC); 1231 let ident = CustomIdent::parse(i, &[])?; 1232 stylistic = Some(VariantAlternates::Stylistic(ident)); 1233 Ok(()) 1234 }, 1235 "ornaments" => { 1236 check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS); 1237 let ident = CustomIdent::parse(i, &[])?; 1238 ornaments = Some(VariantAlternates::Ornaments(ident)); 1239 Ok(()) 1240 }, 1241 "annotation" => { 1242 check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION); 1243 let ident = CustomIdent::parse(i, &[])?; 1244 annotation = Some(VariantAlternates::Annotation(ident)); 1245 Ok(()) 1246 }, 1247 "styleset" => { 1248 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET); 1249 let idents = i.parse_comma_separated(|i| { 1250 CustomIdent::parse(i, &[]) 1251 })?; 1252 styleset = Some(VariantAlternates::Styleset(idents.into())); 1253 Ok(()) 1254 }, 1255 "character-variant" => { 1256 check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT); 1257 let idents = i.parse_comma_separated(|i| { 1258 CustomIdent::parse(i, &[]) 1259 })?; 1260 character_variant = Some(VariantAlternates::CharacterVariant(idents.into())); 1261 Ok(()) 1262 }, 1263 _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)), 1264 } 1265 }) 1266 }, 1267 _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), 1268 }) {} 1269 1270 if parsed_alternates.is_empty() { 1271 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); 1272 } 1273 1274 // Collect the parsed values in canonical order, so that we'll serialize correctly. 1275 let mut alternates = Vec::new(); 1276 macro_rules! push_if_some( 1277 ($value:expr) => ( 1278 if let Some(v) = $value { 1279 alternates.push(v); 1280 } 1281 ) 1282 ); 1283 push_if_some!(stylistic); 1284 push_if_some!(historical); 1285 push_if_some!(styleset); 1286 push_if_some!(character_variant); 1287 push_if_some!(swash); 1288 push_if_some!(ornaments); 1289 push_if_some!(annotation); 1290 1291 Ok(FontVariantAlternates(alternates.into())) 1292 } 1293 } 1294 1295 #[derive( 1296 Clone, 1297 Copy, 1298 Debug, 1299 Eq, 1300 MallocSizeOf, 1301 PartialEq, 1302 Parse, 1303 SpecifiedValueInfo, 1304 ToComputedValue, 1305 ToCss, 1306 ToResolvedValue, 1307 ToShmem, 1308 ToTyped, 1309 )] 1310 #[css(bitflags( 1311 single = "normal", 1312 mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby", 1313 validate_mixed = "Self::validate_mixed_flags", 1314 ))] 1315 #[repr(C)] 1316 /// Variants for east asian variant 1317 pub struct FontVariantEastAsian(u16); 1318 bitflags! { 1319 impl FontVariantEastAsian: u16 { 1320 /// None of the features 1321 const NORMAL = 0; 1322 /// Enables rendering of JIS78 forms (OpenType feature: jp78) 1323 const JIS78 = 1 << 0; 1324 /// Enables rendering of JIS83 forms (OpenType feature: jp83). 1325 const JIS83 = 1 << 1; 1326 /// Enables rendering of JIS90 forms (OpenType feature: jp90). 1327 const JIS90 = 1 << 2; 1328 /// Enables rendering of JIS2004 forms (OpenType feature: jp04). 1329 const JIS04 = 1 << 3; 1330 /// Enables rendering of simplified forms (OpenType feature: smpl). 1331 const SIMPLIFIED = 1 << 4; 1332 /// Enables rendering of traditional forms (OpenType feature: trad). 1333 const TRADITIONAL = 1 << 5; 1334 1335 /// These values are exclusive with each other. 1336 const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0; 1337 1338 /// Enables rendering of full-width variants (OpenType feature: fwid). 1339 const FULL_WIDTH = 1 << 6; 1340 /// Enables rendering of proportionally-spaced variants (OpenType feature: pwid). 1341 const PROPORTIONAL_WIDTH = 1 << 7; 1342 /// Enables display of ruby variant glyphs (OpenType feature: ruby). 1343 const RUBY = 1 << 8; 1344 } 1345 } 1346 1347 impl FontVariantEastAsian { 1348 /// The number of variants. 1349 pub const COUNT: usize = 9; 1350 1351 fn validate_mixed_flags(&self) -> bool { 1352 if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) { 1353 // full-width and proportional-width are exclusive with each other. 1354 return false; 1355 } 1356 let jis = self.intersection(Self::JIS_GROUP); 1357 if !jis.is_empty() && !jis.bits().is_power_of_two() { 1358 return false; 1359 } 1360 true 1361 } 1362 } 1363 1364 #[derive( 1365 Clone, 1366 Copy, 1367 Debug, 1368 Eq, 1369 MallocSizeOf, 1370 PartialEq, 1371 Parse, 1372 SpecifiedValueInfo, 1373 ToComputedValue, 1374 ToCss, 1375 ToResolvedValue, 1376 ToShmem, 1377 ToTyped, 1378 )] 1379 #[css(bitflags( 1380 single = "normal,none", 1381 mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual", 1382 validate_mixed = "Self::validate_mixed_flags", 1383 ))] 1384 #[repr(C)] 1385 /// Variants of ligatures 1386 pub struct FontVariantLigatures(u16); 1387 bitflags! { 1388 impl FontVariantLigatures: u16 { 1389 /// Specifies that common default features are enabled 1390 const NORMAL = 0; 1391 /// Specifies that no features are enabled; 1392 const NONE = 1; 1393 /// Enables display of common ligatures 1394 const COMMON_LIGATURES = 1 << 1; 1395 /// Disables display of common ligatures 1396 const NO_COMMON_LIGATURES = 1 << 2; 1397 /// Enables display of discretionary ligatures 1398 const DISCRETIONARY_LIGATURES = 1 << 3; 1399 /// Disables display of discretionary ligatures 1400 const NO_DISCRETIONARY_LIGATURES = 1 << 4; 1401 /// Enables display of historical ligatures 1402 const HISTORICAL_LIGATURES = 1 << 5; 1403 /// Disables display of historical ligatures 1404 const NO_HISTORICAL_LIGATURES = 1 << 6; 1405 /// Enables display of contextual alternates 1406 const CONTEXTUAL = 1 << 7; 1407 /// Disables display of contextual alternates 1408 const NO_CONTEXTUAL = 1 << 8; 1409 } 1410 } 1411 1412 impl FontVariantLigatures { 1413 /// The number of variants. 1414 pub const COUNT: usize = 9; 1415 1416 fn validate_mixed_flags(&self) -> bool { 1417 // Mixing a value and its disabling value is forbidden. 1418 if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES) 1419 || self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES) 1420 || self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES) 1421 || self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL) 1422 { 1423 return false; 1424 } 1425 true 1426 } 1427 } 1428 1429 /// Variants of numeric values 1430 #[derive( 1431 Clone, 1432 Copy, 1433 Debug, 1434 Eq, 1435 MallocSizeOf, 1436 PartialEq, 1437 Parse, 1438 SpecifiedValueInfo, 1439 ToComputedValue, 1440 ToCss, 1441 ToResolvedValue, 1442 ToShmem, 1443 ToTyped, 1444 )] 1445 #[css(bitflags( 1446 single = "normal", 1447 mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero", 1448 validate_mixed = "Self::validate_mixed_flags", 1449 ))] 1450 #[repr(C)] 1451 pub struct FontVariantNumeric(u8); 1452 bitflags! { 1453 impl FontVariantNumeric : u8 { 1454 /// Specifies that common default features are enabled 1455 const NORMAL = 0; 1456 /// Enables display of lining numerals. 1457 const LINING_NUMS = 1 << 0; 1458 /// Enables display of old-style numerals. 1459 const OLDSTYLE_NUMS = 1 << 1; 1460 /// Enables display of proportional numerals. 1461 const PROPORTIONAL_NUMS = 1 << 2; 1462 /// Enables display of tabular numerals. 1463 const TABULAR_NUMS = 1 << 3; 1464 /// Enables display of lining diagonal fractions. 1465 const DIAGONAL_FRACTIONS = 1 << 4; 1466 /// Enables display of lining stacked fractions. 1467 const STACKED_FRACTIONS = 1 << 5; 1468 /// Enables display of slashed zeros. 1469 const SLASHED_ZERO = 1 << 6; 1470 /// Enables display of letter forms used with ordinal numbers. 1471 const ORDINAL = 1 << 7; 1472 } 1473 } 1474 1475 impl FontVariantNumeric { 1476 /// The number of variants. 1477 pub const COUNT: usize = 8; 1478 1479 /// normal | 1480 /// [ <numeric-figure-values> || 1481 /// <numeric-spacing-values> || 1482 /// <numeric-fraction-values> || 1483 /// ordinal || 1484 /// slashed-zero ] 1485 /// <numeric-figure-values> = [ lining-nums | oldstyle-nums ] 1486 /// <numeric-spacing-values> = [ proportional-nums | tabular-nums ] 1487 /// <numeric-fraction-values> = [ diagonal-fractions | stacked-fractions ] 1488 fn validate_mixed_flags(&self) -> bool { 1489 if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS) 1490 || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS) 1491 || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS) 1492 { 1493 return false; 1494 } 1495 true 1496 } 1497 } 1498 1499 /// This property provides low-level control over OpenType or TrueType font features. 1500 pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>; 1501 1502 /// For font-language-override, use the same representation as the computed value. 1503 pub use crate::values::computed::font::FontLanguageOverride; 1504 1505 impl Parse for FontLanguageOverride { 1506 /// normal | <string> 1507 fn parse<'i, 't>( 1508 _: &ParserContext, 1509 input: &mut Parser<'i, 't>, 1510 ) -> Result<FontLanguageOverride, ParseError<'i>> { 1511 if input 1512 .try_parse(|input| input.expect_ident_matching("normal")) 1513 .is_ok() 1514 { 1515 return Ok(FontLanguageOverride::normal()); 1516 } 1517 1518 let string = input.expect_string()?; 1519 1520 // The OpenType spec requires tags to be 1 to 4 ASCII characters: 1521 // https://learn.microsoft.com/en-gb/typography/opentype/spec/otff#data-types 1522 if string.is_empty() || string.len() > 4 || !string.is_ascii() { 1523 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); 1524 } 1525 1526 let mut bytes = [b' '; 4]; 1527 for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) { 1528 *byte = *str_byte; 1529 } 1530 1531 Ok(FontLanguageOverride(u32::from_be_bytes(bytes))) 1532 } 1533 } 1534 1535 /// A value for any of the font-synthesis-{weight,small-caps,position} properties. 1536 #[repr(u8)] 1537 #[derive( 1538 Clone, 1539 Copy, 1540 Debug, 1541 Eq, 1542 Hash, 1543 MallocSizeOf, 1544 Parse, 1545 PartialEq, 1546 SpecifiedValueInfo, 1547 ToComputedValue, 1548 ToCss, 1549 ToResolvedValue, 1550 ToShmem, 1551 ToTyped, 1552 )] 1553 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 1554 pub enum FontSynthesis { 1555 /// This attribute may be synthesized if not supported by a face. 1556 Auto, 1557 /// Do not attempt to synthesis this style attribute. 1558 None, 1559 } 1560 1561 /// A value for the font-synthesis-style property. 1562 #[repr(u8)] 1563 #[derive( 1564 Clone, 1565 Copy, 1566 Debug, 1567 Eq, 1568 MallocSizeOf, 1569 Parse, 1570 PartialEq, 1571 SpecifiedValueInfo, 1572 ToComputedValue, 1573 ToCss, 1574 ToResolvedValue, 1575 ToShmem, 1576 ToTyped, 1577 )] 1578 pub enum FontSynthesisStyle { 1579 /// This attribute may be synthesized if not supported by a face. 1580 Auto, 1581 /// Do not attempt to synthesis this style attribute. 1582 None, 1583 /// Allow synthesis for oblique, but not for italic. 1584 ObliqueOnly, 1585 } 1586 1587 #[derive( 1588 Clone, 1589 Debug, 1590 Eq, 1591 MallocSizeOf, 1592 PartialEq, 1593 SpecifiedValueInfo, 1594 ToComputedValue, 1595 ToResolvedValue, 1596 ToShmem, 1597 ToTyped, 1598 )] 1599 #[repr(C)] 1600 /// Allows authors to choose a palette from those supported by a color font 1601 /// (and potentially @font-palette-values overrides). 1602 pub struct FontPalette(Atom); 1603 1604 #[allow(missing_docs)] 1605 impl FontPalette { 1606 pub fn normal() -> Self { 1607 Self(atom!("normal")) 1608 } 1609 pub fn light() -> Self { 1610 Self(atom!("light")) 1611 } 1612 pub fn dark() -> Self { 1613 Self(atom!("dark")) 1614 } 1615 } 1616 1617 impl Parse for FontPalette { 1618 /// normal | light | dark | dashed-ident 1619 fn parse<'i, 't>( 1620 _context: &ParserContext, 1621 input: &mut Parser<'i, 't>, 1622 ) -> Result<FontPalette, ParseError<'i>> { 1623 let location = input.current_source_location(); 1624 let ident = input.expect_ident()?; 1625 match_ignore_ascii_case! { &ident, 1626 "normal" => Ok(Self::normal()), 1627 "light" => Ok(Self::light()), 1628 "dark" => Ok(Self::dark()), 1629 _ => if ident.starts_with("--") { 1630 Ok(Self(Atom::from(ident.as_ref()))) 1631 } else { 1632 Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))) 1633 }, 1634 } 1635 } 1636 } 1637 1638 impl ToCss for FontPalette { 1639 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 1640 where 1641 W: Write, 1642 { 1643 serialize_atom_identifier(&self.0, dest) 1644 } 1645 } 1646 1647 /// This property provides low-level control over OpenType or TrueType font 1648 /// variations. 1649 pub type FontVariationSettings = FontSettings<VariationValue<Number>>; 1650 1651 fn parse_one_feature_value<'i, 't>( 1652 context: &ParserContext, 1653 input: &mut Parser<'i, 't>, 1654 ) -> Result<Integer, ParseError<'i>> { 1655 if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) { 1656 return Ok(integer); 1657 } 1658 1659 try_match_ident_ignore_ascii_case! { input, 1660 "on" => Ok(Integer::new(1)), 1661 "off" => Ok(Integer::new(0)), 1662 } 1663 } 1664 1665 impl Parse for FeatureTagValue<Integer> { 1666 /// https://drafts.csswg.org/css-fonts-4/#feature-tag-value 1667 fn parse<'i, 't>( 1668 context: &ParserContext, 1669 input: &mut Parser<'i, 't>, 1670 ) -> Result<Self, ParseError<'i>> { 1671 let tag = FontTag::parse(context, input)?; 1672 let value = input 1673 .try_parse(|i| parse_one_feature_value(context, i)) 1674 .unwrap_or_else(|_| Integer::new(1)); 1675 1676 Ok(Self { tag, value }) 1677 } 1678 } 1679 1680 impl Parse for VariationValue<Number> { 1681 /// This is the `<string> <number>` part of the font-variation-settings 1682 /// syntax. 1683 fn parse<'i, 't>( 1684 context: &ParserContext, 1685 input: &mut Parser<'i, 't>, 1686 ) -> Result<Self, ParseError<'i>> { 1687 let tag = FontTag::parse(context, input)?; 1688 let value = Number::parse(context, input)?; 1689 Ok(Self { tag, value }) 1690 } 1691 } 1692 1693 /// A metrics override value for a @font-face descriptor 1694 /// 1695 /// https://drafts.csswg.org/css-fonts/#font-metrics-override-desc 1696 #[derive( 1697 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, 1698 )] 1699 pub enum MetricsOverride { 1700 /// A non-negative `<percentage>` of the computed font size 1701 Override(NonNegativePercentage), 1702 /// Normal metrics from the font. 1703 Normal, 1704 } 1705 1706 impl MetricsOverride { 1707 #[inline] 1708 /// Get default value with `normal` 1709 pub fn normal() -> MetricsOverride { 1710 MetricsOverride::Normal 1711 } 1712 1713 /// The ToComputedValue implementation, used for @font-face descriptors. 1714 /// 1715 /// Valid override percentages must be non-negative; we return -1.0 to indicate 1716 /// the absence of an override (i.e. 'normal'). 1717 #[inline] 1718 pub fn compute(&self) -> ComputedPercentage { 1719 match *self { 1720 MetricsOverride::Normal => ComputedPercentage(-1.0), 1721 MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()), 1722 } 1723 } 1724 } 1725 1726 #[derive( 1727 Clone, 1728 Copy, 1729 Debug, 1730 MallocSizeOf, 1731 Parse, 1732 PartialEq, 1733 SpecifiedValueInfo, 1734 ToComputedValue, 1735 ToCss, 1736 ToResolvedValue, 1737 ToShmem, 1738 ToTyped, 1739 )] 1740 #[repr(u8)] 1741 /// How to do font-size scaling. 1742 pub enum XTextScale { 1743 /// Both min-font-size and text zoom are enabled. 1744 All, 1745 /// Text-only zoom is enabled, but min-font-size is not honored. 1746 ZoomOnly, 1747 /// Neither of them is enabled. 1748 None, 1749 } 1750 1751 impl XTextScale { 1752 /// Returns whether text zoom is enabled. 1753 #[inline] 1754 pub fn text_zoom_enabled(self) -> bool { 1755 self != Self::None 1756 } 1757 } 1758 1759 #[derive( 1760 Clone, 1761 Debug, 1762 MallocSizeOf, 1763 PartialEq, 1764 SpecifiedValueInfo, 1765 ToComputedValue, 1766 ToCss, 1767 ToResolvedValue, 1768 ToShmem, 1769 ToTyped, 1770 )] 1771 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 1772 /// Internal property that reflects the lang attribute 1773 pub struct XLang(#[css(skip)] pub Atom); 1774 1775 impl XLang { 1776 #[inline] 1777 /// Get default value for `-x-lang` 1778 pub fn get_initial_value() -> XLang { 1779 XLang(atom!("")) 1780 } 1781 } 1782 1783 impl Parse for XLang { 1784 fn parse<'i, 't>( 1785 _: &ParserContext, 1786 input: &mut Parser<'i, 't>, 1787 ) -> Result<XLang, ParseError<'i>> { 1788 debug_assert!( 1789 false, 1790 "Should be set directly by presentation attributes only." 1791 ); 1792 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 1793 } 1794 } 1795 1796 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))] 1797 #[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] 1798 /// Specifies the minimum font size allowed due to changes in scriptlevel. 1799 /// Ref: https://wiki.mozilla.org/MathML:mstyle 1800 pub struct MozScriptMinSize(pub NoCalcLength); 1801 1802 impl MozScriptMinSize { 1803 #[inline] 1804 /// Calculate initial value of -moz-script-min-size. 1805 pub fn get_initial_value() -> Length { 1806 Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT) 1807 } 1808 } 1809 1810 impl Parse for MozScriptMinSize { 1811 fn parse<'i, 't>( 1812 _: &ParserContext, 1813 input: &mut Parser<'i, 't>, 1814 ) -> Result<MozScriptMinSize, ParseError<'i>> { 1815 debug_assert!( 1816 false, 1817 "Should be set directly by presentation attributes only." 1818 ); 1819 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 1820 } 1821 } 1822 1823 /// A value for the `math-depth` property. 1824 /// https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property 1825 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))] 1826 #[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)] 1827 pub enum MathDepth { 1828 /// Increment math-depth if math-style is compact. 1829 AutoAdd, 1830 1831 /// Add the function's argument to math-depth. 1832 #[css(function)] 1833 Add(Integer), 1834 1835 /// Set math-depth to the specified value. 1836 Absolute(Integer), 1837 } 1838 1839 impl Parse for MathDepth { 1840 fn parse<'i, 't>( 1841 context: &ParserContext, 1842 input: &mut Parser<'i, 't>, 1843 ) -> Result<MathDepth, ParseError<'i>> { 1844 if input 1845 .try_parse(|i| i.expect_ident_matching("auto-add")) 1846 .is_ok() 1847 { 1848 return Ok(MathDepth::AutoAdd); 1849 } 1850 if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) { 1851 return Ok(MathDepth::Absolute(math_depth_value)); 1852 } 1853 input.expect_function_matching("add")?; 1854 let math_depth_delta_value = 1855 input.parse_nested_block(|input| Integer::parse(context, input))?; 1856 Ok(MathDepth::Add(math_depth_delta_value)) 1857 } 1858 } 1859 1860 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))] 1861 #[derive( 1862 Clone, 1863 Copy, 1864 Debug, 1865 PartialEq, 1866 SpecifiedValueInfo, 1867 ToComputedValue, 1868 ToCss, 1869 ToResolvedValue, 1870 ToShmem, 1871 )] 1872 /// Specifies the multiplier to be used to adjust font size 1873 /// due to changes in scriptlevel. 1874 /// 1875 /// Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.mstyle.attrs 1876 pub struct MozScriptSizeMultiplier(pub f32); 1877 1878 impl MozScriptSizeMultiplier { 1879 #[inline] 1880 /// Get default value of `-moz-script-size-multiplier` 1881 pub fn get_initial_value() -> MozScriptSizeMultiplier { 1882 MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32) 1883 } 1884 } 1885 1886 impl Parse for MozScriptSizeMultiplier { 1887 fn parse<'i, 't>( 1888 _: &ParserContext, 1889 input: &mut Parser<'i, 't>, 1890 ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> { 1891 debug_assert!( 1892 false, 1893 "Should be set directly by presentation attributes only." 1894 ); 1895 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 1896 } 1897 } 1898 1899 impl From<f32> for MozScriptSizeMultiplier { 1900 fn from(v: f32) -> Self { 1901 MozScriptSizeMultiplier(v) 1902 } 1903 } 1904 1905 impl From<MozScriptSizeMultiplier> for f32 { 1906 fn from(v: MozScriptSizeMultiplier) -> f32 { 1907 v.0 1908 } 1909 } 1910 1911 /// A specified value for the `line-height` property. 1912 pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>; 1913 1914 impl ToComputedValue for LineHeight { 1915 type ComputedValue = computed::LineHeight; 1916 1917 #[inline] 1918 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 1919 match *self { 1920 GenericLineHeight::Normal => GenericLineHeight::Normal, 1921 #[cfg(feature = "gecko")] 1922 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight, 1923 GenericLineHeight::Number(number) => { 1924 GenericLineHeight::Number(number.to_computed_value(context)) 1925 }, 1926 GenericLineHeight::Length(ref non_negative_lp) => { 1927 let result = match non_negative_lp.0 { 1928 LengthPercentage::Length(NoCalcLength::Absolute(ref abs)) => { 1929 context.maybe_zoom_text(abs.to_computed_value(context)) 1930 }, 1931 LengthPercentage::Length(ref length) => { 1932 // line-height units specifically resolve against parent's 1933 // font and line-height properties, while the rest of font 1934 // relative units still resolve against the element's own 1935 // properties. 1936 length.to_computed_value_with_base_size( 1937 context, 1938 FontBaseSize::CurrentStyle, 1939 LineHeightBase::InheritedStyle, 1940 ) 1941 }, 1942 LengthPercentage::Percentage(ref p) => FontRelativeLength::Em(p.0) 1943 .to_computed_value( 1944 context, 1945 FontBaseSize::CurrentStyle, 1946 LineHeightBase::InheritedStyle, 1947 ), 1948 LengthPercentage::Calc(ref calc) => { 1949 let computed_calc = calc.to_computed_value_zoomed( 1950 context, 1951 FontBaseSize::CurrentStyle, 1952 LineHeightBase::InheritedStyle, 1953 ); 1954 let base = context.style().get_font().clone_font_size().computed_size(); 1955 computed_calc.resolve(base) 1956 }, 1957 }; 1958 GenericLineHeight::Length(result.into()) 1959 }, 1960 } 1961 } 1962 1963 #[inline] 1964 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 1965 match *computed { 1966 GenericLineHeight::Normal => GenericLineHeight::Normal, 1967 #[cfg(feature = "gecko")] 1968 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight, 1969 GenericLineHeight::Number(ref number) => { 1970 GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number)) 1971 }, 1972 GenericLineHeight::Length(ref length) => { 1973 GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into()) 1974 }, 1975 } 1976 } 1977 } 1978 1979 /// Flags for the query_font_metrics() function. 1980 #[repr(C)] 1981 pub struct QueryFontMetricsFlags(u8); 1982 1983 bitflags! { 1984 impl QueryFontMetricsFlags: u8 { 1985 /// Should we use the user font set? 1986 const USE_USER_FONT_SET = 1 << 0; 1987 /// Does the caller need the `ch` unit (width of the ZERO glyph)? 1988 const NEEDS_CH = 1 << 1; 1989 /// Does the caller need the `ic` unit (width of the WATER ideograph)? 1990 const NEEDS_IC = 1 << 2; 1991 /// Does the caller need math scales to be retrieved? 1992 const NEEDS_MATH_SCALES = 1 << 3; 1993 } 1994 }