font_face.rs (25748B)
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 //! The [`@font-face`][ff] at-rule. 6 //! 7 //! [ff]: https://drafts.csswg.org/css-fonts/#at-font-face-rule 8 9 use crate::derives::*; 10 use crate::error_reporting::ContextualParseError; 11 use crate::parser::{Parse, ParserContext}; 12 use crate::properties::longhands::font_language_override; 13 use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; 14 use crate::values::computed::font::{FamilyName, FontStretch}; 15 use crate::values::generics::font::FontStyle as GenericFontStyle; 16 use crate::values::specified::font::{ 17 AbsoluteFontWeight, FontFeatureSettings, FontStretch as SpecifiedFontStretch, 18 FontVariationSettings, MetricsOverride, SpecifiedFontStyle, 19 }; 20 use crate::values::specified::url::SpecifiedUrl; 21 use crate::values::specified::{Angle, NonNegativePercentage}; 22 use cssparser::{ 23 match_ignore_ascii_case, AtRuleParser, CowRcStr, DeclarationParser, Parser, ParserState, 24 QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, UnicodeRange, 25 }; 26 use selectors::parser::SelectorParseErrorKind; 27 use std::fmt::{self, Write}; 28 use style_traits::{CssStringWriter, CssWriter, ParseError}; 29 use style_traits::{StyleParseErrorKind, ToCss}; 30 31 /// A source for a font-face rule. 32 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 33 #[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)] 34 pub enum Source { 35 /// A `url()` source. 36 Url(UrlSource), 37 /// A `local()` source. 38 #[css(function)] 39 Local(FamilyName), 40 } 41 42 /// A list of sources for the font-face src descriptor. 43 #[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)] 44 #[css(comma)] 45 pub struct SourceList(#[css(iterable)] pub Vec<Source>); 46 47 // We can't just use OneOrMoreSeparated to derive Parse for the Source list, 48 // because we want to filter out components that parsed as None, then fail if no 49 // valid components remain. So we provide our own implementation here. 50 impl Parse for SourceList { 51 fn parse<'i, 't>( 52 context: &ParserContext, 53 input: &mut Parser<'i, 't>, 54 ) -> Result<Self, ParseError<'i>> { 55 // Parse the comma-separated list, then let filter_map discard any None items. 56 let list = input 57 .parse_comma_separated(|input| { 58 let s = input.parse_entirely(|input| Source::parse(context, input)); 59 while input.next().is_ok() {} 60 Ok(s.ok()) 61 })? 62 .into_iter() 63 .filter_map(|s| s) 64 .collect::<Vec<Source>>(); 65 if list.is_empty() { 66 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 67 } else { 68 Ok(SourceList(list)) 69 } 70 } 71 } 72 73 /// Keywords for the font-face src descriptor's format() function. 74 /// ('None' and 'Unknown' are for internal use in gfx, not exposed to CSS.) 75 #[derive(Clone, Copy, Debug, Eq, Parse, PartialEq, ToCss, ToShmem)] 76 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 77 #[repr(u8)] 78 #[allow(missing_docs)] 79 pub enum FontFaceSourceFormatKeyword { 80 #[css(skip)] 81 None, 82 Collection, 83 EmbeddedOpentype, 84 Opentype, 85 Svg, 86 Truetype, 87 Woff, 88 Woff2, 89 #[css(skip)] 90 Unknown, 91 } 92 93 /// Flags for the @font-face tech() function, indicating font technologies 94 /// required by the resource. 95 #[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)] 96 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 97 #[repr(C)] 98 pub struct FontFaceSourceTechFlags(u16); 99 bitflags! { 100 impl FontFaceSourceTechFlags: u16 { 101 /// Font requires OpenType feature support. 102 const FEATURES_OPENTYPE = 1 << 0; 103 /// Font requires Apple Advanced Typography support. 104 const FEATURES_AAT = 1 << 1; 105 /// Font requires Graphite shaping support. 106 const FEATURES_GRAPHITE = 1 << 2; 107 /// Font requires COLRv0 rendering support (simple list of colored layers). 108 const COLOR_COLRV0 = 1 << 3; 109 /// Font requires COLRv1 rendering support (graph of paint operations). 110 const COLOR_COLRV1 = 1 << 4; 111 /// Font requires SVG glyph rendering support. 112 const COLOR_SVG = 1 << 5; 113 /// Font has bitmap glyphs in 'sbix' format. 114 const COLOR_SBIX = 1 << 6; 115 /// Font has bitmap glyphs in 'CBDT' format. 116 const COLOR_CBDT = 1 << 7; 117 /// Font requires OpenType Variations support. 118 const VARIATIONS = 1 << 8; 119 /// Font requires CPAL palette selection support. 120 const PALETTES = 1 << 9; 121 /// Font requires support for incremental downloading. 122 const INCREMENTAL = 1 << 10; 123 } 124 } 125 126 impl FontFaceSourceTechFlags { 127 /// Parse a single font-technology keyword and return its flag. 128 pub fn parse_one<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> { 129 Ok(try_match_ident_ignore_ascii_case! { input, 130 "features-opentype" => Self::FEATURES_OPENTYPE, 131 "features-aat" => Self::FEATURES_AAT, 132 "features-graphite" => Self::FEATURES_GRAPHITE, 133 "color-colrv0" => Self::COLOR_COLRV0, 134 "color-colrv1" => Self::COLOR_COLRV1, 135 "color-svg" => Self::COLOR_SVG, 136 "color-sbix" => Self::COLOR_SBIX, 137 "color-cbdt" => Self::COLOR_CBDT, 138 "variations" => Self::VARIATIONS, 139 "palettes" => Self::PALETTES, 140 "incremental" => Self::INCREMENTAL, 141 }) 142 } 143 } 144 145 impl Parse for FontFaceSourceTechFlags { 146 fn parse<'i, 't>( 147 _context: &ParserContext, 148 input: &mut Parser<'i, 't>, 149 ) -> Result<Self, ParseError<'i>> { 150 let location = input.current_source_location(); 151 // We don't actually care about the return value of parse_comma_separated, 152 // because we insert the flags into result as we go. 153 let mut result = Self::empty(); 154 input.parse_comma_separated(|input| { 155 let flag = Self::parse_one(input)?; 156 result.insert(flag); 157 Ok(()) 158 })?; 159 if !result.is_empty() { 160 Ok(result) 161 } else { 162 Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 163 } 164 } 165 } 166 167 #[allow(unused_assignments)] 168 impl ToCss for FontFaceSourceTechFlags { 169 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 170 where 171 W: fmt::Write, 172 { 173 let mut first = true; 174 175 macro_rules! write_if_flag { 176 ($s:expr => $f:ident) => { 177 if self.contains(Self::$f) { 178 if first { 179 first = false; 180 } else { 181 dest.write_str(", ")?; 182 } 183 dest.write_str($s)?; 184 } 185 }; 186 } 187 188 write_if_flag!("features-opentype" => FEATURES_OPENTYPE); 189 write_if_flag!("features-aat" => FEATURES_AAT); 190 write_if_flag!("features-graphite" => FEATURES_GRAPHITE); 191 write_if_flag!("color-colrv0" => COLOR_COLRV0); 192 write_if_flag!("color-colrv1" => COLOR_COLRV1); 193 write_if_flag!("color-svg" => COLOR_SVG); 194 write_if_flag!("color-sbix" => COLOR_SBIX); 195 write_if_flag!("color-cbdt" => COLOR_CBDT); 196 write_if_flag!("variations" => VARIATIONS); 197 write_if_flag!("palettes" => PALETTES); 198 write_if_flag!("incremental" => INCREMENTAL); 199 200 Ok(()) 201 } 202 } 203 204 /// A POD representation for Gecko. All pointers here are non-owned and as such 205 /// can't outlive the rule they came from, but we can't enforce that via C++. 206 /// 207 /// All the strings are of course utf8. 208 #[cfg(feature = "gecko")] 209 #[derive(Clone, Copy, Debug, Eq, PartialEq)] 210 #[repr(u8)] 211 #[allow(missing_docs)] 212 pub enum FontFaceSourceListComponent { 213 Url(*const crate::gecko::url::CssUrl), 214 Local(*mut crate::gecko_bindings::structs::nsAtom), 215 FormatHintKeyword(FontFaceSourceFormatKeyword), 216 FormatHintString { 217 length: usize, 218 utf8_bytes: *const u8, 219 }, 220 TechFlags(FontFaceSourceTechFlags), 221 } 222 223 #[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)] 224 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 225 #[repr(u8)] 226 #[allow(missing_docs)] 227 pub enum FontFaceSourceFormat { 228 Keyword(FontFaceSourceFormatKeyword), 229 String(String), 230 } 231 232 /// A `UrlSource` represents a font-face source that has been specified with a 233 /// `url()` function. 234 /// 235 /// <https://drafts.csswg.org/css-fonts/#src-desc> 236 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 237 #[derive(Clone, Debug, Eq, PartialEq, ToShmem)] 238 pub struct UrlSource { 239 /// The specified url. 240 pub url: SpecifiedUrl, 241 /// The format hint specified with the `format()` function, if present. 242 pub format_hint: Option<FontFaceSourceFormat>, 243 /// The font technology flags specified with the `tech()` function, if any. 244 pub tech_flags: FontFaceSourceTechFlags, 245 } 246 247 impl ToCss for UrlSource { 248 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 249 where 250 W: fmt::Write, 251 { 252 self.url.to_css(dest)?; 253 if let Some(hint) = &self.format_hint { 254 dest.write_str(" format(")?; 255 hint.to_css(dest)?; 256 dest.write_char(')')?; 257 } 258 if !self.tech_flags.is_empty() { 259 dest.write_str(" tech(")?; 260 self.tech_flags.to_css(dest)?; 261 dest.write_char(')')?; 262 } 263 Ok(()) 264 } 265 } 266 267 /// A font-display value for a @font-face rule. 268 /// The font-display descriptor determines how a font face is displayed based 269 /// on whether and when it is downloaded and ready to use. 270 #[allow(missing_docs)] 271 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 272 #[derive( 273 Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss, ToShmem, 274 )] 275 #[repr(u8)] 276 pub enum FontDisplay { 277 Auto, 278 Block, 279 Swap, 280 Fallback, 281 Optional, 282 } 283 284 macro_rules! impl_range { 285 ($range:ident, $component:ident) => { 286 impl Parse for $range { 287 fn parse<'i, 't>( 288 context: &ParserContext, 289 input: &mut Parser<'i, 't>, 290 ) -> Result<Self, ParseError<'i>> { 291 let first = $component::parse(context, input)?; 292 let second = input 293 .try_parse(|input| $component::parse(context, input)) 294 .unwrap_or_else(|_| first.clone()); 295 Ok($range(first, second)) 296 } 297 } 298 impl ToCss for $range { 299 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 300 where 301 W: fmt::Write, 302 { 303 self.0.to_css(dest)?; 304 if self.0 != self.1 { 305 dest.write_char(' ')?; 306 self.1.to_css(dest)?; 307 } 308 Ok(()) 309 } 310 } 311 }; 312 } 313 314 /// The font-weight descriptor: 315 /// 316 /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-weight 317 #[derive(Clone, Debug, PartialEq, ToShmem)] 318 pub struct FontWeightRange(pub AbsoluteFontWeight, pub AbsoluteFontWeight); 319 impl_range!(FontWeightRange, AbsoluteFontWeight); 320 321 /// The computed representation of the above so Gecko can read them easily. 322 /// 323 /// This one is needed because cbindgen doesn't know how to generate 324 /// specified::Number. 325 #[repr(C)] 326 #[allow(missing_docs)] 327 pub struct ComputedFontWeightRange(f32, f32); 328 329 #[inline] 330 fn sort_range<T: PartialOrd>(a: T, b: T) -> (T, T) { 331 if a > b { 332 (b, a) 333 } else { 334 (a, b) 335 } 336 } 337 338 impl FontWeightRange { 339 /// Returns a computed font-stretch range. 340 pub fn compute(&self) -> ComputedFontWeightRange { 341 let (min, max) = sort_range(self.0.compute().value(), self.1.compute().value()); 342 ComputedFontWeightRange(min, max) 343 } 344 } 345 346 /// The font-stretch descriptor: 347 /// 348 /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-stretch 349 #[derive(Clone, Debug, PartialEq, ToShmem)] 350 pub struct FontStretchRange(pub SpecifiedFontStretch, pub SpecifiedFontStretch); 351 impl_range!(FontStretchRange, SpecifiedFontStretch); 352 353 /// The computed representation of the above, so that Gecko can read them 354 /// easily. 355 #[repr(C)] 356 #[allow(missing_docs)] 357 pub struct ComputedFontStretchRange(FontStretch, FontStretch); 358 359 impl FontStretchRange { 360 /// Returns a computed font-stretch range. 361 pub fn compute(&self) -> ComputedFontStretchRange { 362 fn compute_stretch(s: &SpecifiedFontStretch) -> FontStretch { 363 match *s { 364 SpecifiedFontStretch::Keyword(ref kw) => kw.compute(), 365 SpecifiedFontStretch::Stretch(ref p) => FontStretch::from_percentage(p.0.get()), 366 SpecifiedFontStretch::System(..) => unreachable!(), 367 } 368 } 369 370 let (min, max) = sort_range(compute_stretch(&self.0), compute_stretch(&self.1)); 371 ComputedFontStretchRange(min, max) 372 } 373 } 374 375 /// The font-style descriptor: 376 /// 377 /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-style 378 #[derive(Clone, Debug, PartialEq, ToShmem)] 379 #[allow(missing_docs)] 380 pub enum FontStyle { 381 Italic, 382 Oblique(Angle, Angle), 383 } 384 385 /// The computed representation of the above, with angles in degrees, so that 386 /// Gecko can read them easily. 387 #[repr(u8)] 388 #[allow(missing_docs)] 389 pub enum ComputedFontStyleDescriptor { 390 Italic, 391 Oblique(f32, f32), 392 } 393 394 impl Parse for FontStyle { 395 fn parse<'i, 't>( 396 context: &ParserContext, 397 input: &mut Parser<'i, 't>, 398 ) -> Result<Self, ParseError<'i>> { 399 // We parse 'normal' explicitly here to distinguish it from 'oblique 0deg', 400 // because we must not accept a following angle. 401 if input 402 .try_parse(|i| i.expect_ident_matching("normal")) 403 .is_ok() 404 { 405 return Ok(FontStyle::Oblique(Angle::zero(), Angle::zero())); 406 } 407 408 let style = SpecifiedFontStyle::parse(context, input)?; 409 Ok(match style { 410 GenericFontStyle::Italic => FontStyle::Italic, 411 GenericFontStyle::Oblique(angle) => { 412 let second_angle = input 413 .try_parse(|input| SpecifiedFontStyle::parse_angle(context, input)) 414 .unwrap_or_else(|_| angle.clone()); 415 416 FontStyle::Oblique(angle, second_angle) 417 }, 418 }) 419 } 420 } 421 422 impl ToCss for FontStyle { 423 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 424 where 425 W: fmt::Write, 426 { 427 match *self { 428 FontStyle::Italic => dest.write_str("italic"), 429 FontStyle::Oblique(ref first, ref second) => { 430 // Not first.is_zero() because we don't want to serialize 431 // `oblique calc(0deg)` as `normal`. 432 if *first == Angle::zero() && first == second { 433 return dest.write_str("normal"); 434 } 435 dest.write_str("oblique")?; 436 if *first != SpecifiedFontStyle::default_angle() || first != second { 437 dest.write_char(' ')?; 438 first.to_css(dest)?; 439 } 440 if first != second { 441 dest.write_char(' ')?; 442 second.to_css(dest)?; 443 } 444 Ok(()) 445 }, 446 } 447 } 448 } 449 450 impl FontStyle { 451 /// Returns a computed font-style descriptor. 452 pub fn compute(&self) -> ComputedFontStyleDescriptor { 453 match *self { 454 FontStyle::Italic => ComputedFontStyleDescriptor::Italic, 455 FontStyle::Oblique(ref first, ref second) => { 456 let (min, max) = sort_range( 457 SpecifiedFontStyle::compute_angle_degrees(first), 458 SpecifiedFontStyle::compute_angle_degrees(second), 459 ); 460 ComputedFontStyleDescriptor::Oblique(min, max) 461 }, 462 } 463 } 464 } 465 466 /// Parse the block inside a `@font-face` rule. 467 /// 468 /// Note that the prelude parsing code lives in the `stylesheets` module. 469 pub fn parse_font_face_block( 470 context: &ParserContext, 471 input: &mut Parser, 472 location: SourceLocation, 473 ) -> FontFaceRuleData { 474 let mut rule = FontFaceRuleData::empty(location); 475 { 476 let mut parser = FontFaceRuleParser { 477 context, 478 rule: &mut rule, 479 }; 480 let mut iter = RuleBodyParser::new(input, &mut parser); 481 while let Some(declaration) = iter.next() { 482 if let Err((error, slice)) = declaration { 483 let location = error.location; 484 let error = ContextualParseError::UnsupportedFontFaceDescriptor(slice, error); 485 context.log_css_error(location, error) 486 } 487 } 488 } 489 rule 490 } 491 492 /// A @font-face rule that is known to have font-family and src declarations. 493 #[cfg(feature = "servo")] 494 pub struct FontFace<'a>(&'a FontFaceRuleData); 495 496 struct FontFaceRuleParser<'a, 'b: 'a> { 497 context: &'a ParserContext<'b>, 498 rule: &'a mut FontFaceRuleData, 499 } 500 501 /// Default methods reject all at rules. 502 impl<'a, 'b, 'i> AtRuleParser<'i> for FontFaceRuleParser<'a, 'b> { 503 type Prelude = (); 504 type AtRule = (); 505 type Error = StyleParseErrorKind<'i>; 506 } 507 508 impl<'a, 'b, 'i> QualifiedRuleParser<'i> for FontFaceRuleParser<'a, 'b> { 509 type Prelude = (); 510 type QualifiedRule = (); 511 type Error = StyleParseErrorKind<'i>; 512 } 513 514 impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> 515 for FontFaceRuleParser<'a, 'b> 516 { 517 fn parse_qualified(&self) -> bool { 518 false 519 } 520 fn parse_declarations(&self) -> bool { 521 true 522 } 523 } 524 525 impl Parse for Source { 526 fn parse<'i, 't>( 527 context: &ParserContext, 528 input: &mut Parser<'i, 't>, 529 ) -> Result<Source, ParseError<'i>> { 530 if input 531 .try_parse(|input| input.expect_function_matching("local")) 532 .is_ok() 533 { 534 return input 535 .parse_nested_block(|input| FamilyName::parse(context, input)) 536 .map(Source::Local); 537 } 538 539 let url = SpecifiedUrl::parse(context, input)?; 540 541 // Parsing optional format() 542 let format_hint = if input 543 .try_parse(|input| input.expect_function_matching("format")) 544 .is_ok() 545 { 546 input.parse_nested_block(|input| { 547 if let Ok(kw) = input.try_parse(FontFaceSourceFormatKeyword::parse) { 548 Ok(Some(FontFaceSourceFormat::Keyword(kw))) 549 } else { 550 let s = input.expect_string()?.as_ref().to_owned(); 551 Ok(Some(FontFaceSourceFormat::String(s))) 552 } 553 })? 554 } else { 555 None 556 }; 557 558 // Parse optional tech() 559 let tech_flags = if static_prefs::pref!("layout.css.font-tech.enabled") 560 && input 561 .try_parse(|input| input.expect_function_matching("tech")) 562 .is_ok() 563 { 564 input.parse_nested_block(|input| FontFaceSourceTechFlags::parse(context, input))? 565 } else { 566 FontFaceSourceTechFlags::empty() 567 }; 568 569 Ok(Source::Url(UrlSource { 570 url, 571 format_hint, 572 tech_flags, 573 })) 574 } 575 } 576 577 macro_rules! is_descriptor_enabled { 578 ("font-variation-settings") => { 579 static_prefs::pref!("layout.css.font-variations.enabled") 580 }; 581 ("size-adjust") => { 582 cfg!(feature = "gecko") 583 }; 584 ($name:tt) => { 585 true 586 }; 587 } 588 589 macro_rules! font_face_descriptors_common { 590 ( 591 $( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty, )* 592 ) => { 593 /// Data inside a `@font-face` rule. 594 /// 595 /// <https://drafts.csswg.org/css-fonts/#font-face-rule> 596 #[derive(Clone, Debug, PartialEq, ToShmem)] 597 pub struct FontFaceRuleData { 598 $( 599 #[$doc] 600 pub $ident: Option<$ty>, 601 )* 602 /// Line and column of the @font-face rule source code. 603 pub source_location: SourceLocation, 604 } 605 606 impl FontFaceRuleData { 607 /// Create an empty font-face rule 608 pub fn empty(location: SourceLocation) -> Self { 609 FontFaceRuleData { 610 $( 611 $ident: None, 612 )* 613 source_location: location, 614 } 615 } 616 617 /// Serialization of declarations in the FontFaceRule 618 pub fn decl_to_css(&self, dest: &mut CssStringWriter) -> fmt::Result { 619 $( 620 if let Some(ref value) = self.$ident { 621 dest.write_str(concat!($name, ": "))?; 622 value.to_css(&mut CssWriter::new(dest))?; 623 dest.write_str("; ")?; 624 } 625 )* 626 Ok(()) 627 } 628 } 629 630 impl<'a, 'b, 'i> DeclarationParser<'i> for FontFaceRuleParser<'a, 'b> { 631 type Declaration = (); 632 type Error = StyleParseErrorKind<'i>; 633 634 fn parse_value<'t>( 635 &mut self, 636 name: CowRcStr<'i>, 637 input: &mut Parser<'i, 't>, 638 _declaration_start: &ParserState, 639 ) -> Result<(), ParseError<'i>> { 640 match_ignore_ascii_case! { &*name, 641 $( 642 $name if is_descriptor_enabled!($name) => { 643 // DeclarationParser also calls parse_entirely 644 // so we’d normally not need to, 645 // but in this case we do because we set the value as a side effect 646 // rather than returning it. 647 let value = input.parse_entirely(|i| Parse::parse(self.context, i))?; 648 self.rule.$ident = Some(value) 649 }, 650 )* 651 _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))), 652 } 653 Ok(()) 654 } 655 } 656 } 657 } 658 659 impl ToCssWithGuard for FontFaceRuleData { 660 // Serialization of FontFaceRule is not specced. 661 fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { 662 dest.write_str("@font-face { ")?; 663 self.decl_to_css(dest)?; 664 dest.write_char('}') 665 } 666 } 667 668 macro_rules! font_face_descriptors { 669 ( 670 mandatory descriptors = [ 671 $( #[$m_doc: meta] $m_name: tt $m_ident: ident / $m_gecko_ident: ident: $m_ty: ty, )* 672 ] 673 optional descriptors = [ 674 $( #[$o_doc: meta] $o_name: tt $o_ident: ident / $o_gecko_ident: ident: $o_ty: ty, )* 675 ] 676 ) => { 677 font_face_descriptors_common! { 678 $( #[$m_doc] $m_name $m_ident / $m_gecko_ident: $m_ty, )* 679 $( #[$o_doc] $o_name $o_ident / $o_gecko_ident: $o_ty, )* 680 } 681 682 impl FontFaceRuleData { 683 /// Per https://github.com/w3c/csswg-drafts/issues/1133 an @font-face rule 684 /// is valid as far as the CSS parser is concerned even if it doesn’t have 685 /// a font-family or src declaration. 686 /// 687 /// However both are required for the rule to represent an actual font face. 688 #[cfg(feature = "servo")] 689 pub fn font_face(&self) -> Option<FontFace<'_>> { 690 if $( self.$m_ident.is_some() )&&* { 691 Some(FontFace(self)) 692 } else { 693 None 694 } 695 } 696 } 697 698 #[cfg(feature = "servo")] 699 impl<'a> FontFace<'a> { 700 $( 701 #[$m_doc] 702 pub fn $m_ident(&self) -> &$m_ty { 703 self.0 .$m_ident.as_ref().unwrap() 704 } 705 )* 706 } 707 } 708 } 709 710 font_face_descriptors! { 711 mandatory descriptors = [ 712 /// The name of this font face 713 "font-family" family / mFamily: FamilyName, 714 715 /// The alternative sources for this font face. 716 "src" sources / mSrc: SourceList, 717 ] 718 optional descriptors = [ 719 /// The style of this font face. 720 "font-style" style / mStyle: FontStyle, 721 722 /// The weight of this font face. 723 "font-weight" weight / mWeight: FontWeightRange, 724 725 /// The stretch of this font face. 726 "font-stretch" stretch / mStretch: FontStretchRange, 727 728 /// The display of this font face. 729 "font-display" display / mDisplay: FontDisplay, 730 731 /// The ranges of code points outside of which this font face should not be used. 732 "unicode-range" unicode_range / mUnicodeRange: Vec<UnicodeRange>, 733 734 /// The feature settings of this font face. 735 "font-feature-settings" feature_settings / mFontFeatureSettings: FontFeatureSettings, 736 737 /// The variation settings of this font face. 738 "font-variation-settings" variation_settings / mFontVariationSettings: FontVariationSettings, 739 740 /// The language override of this font face. 741 "font-language-override" language_override / mFontLanguageOverride: font_language_override::SpecifiedValue, 742 743 /// The ascent override for this font face. 744 "ascent-override" ascent_override / mAscentOverride: MetricsOverride, 745 746 /// The descent override for this font face. 747 "descent-override" descent_override / mDescentOverride: MetricsOverride, 748 749 /// The line-gap override for this font face. 750 "line-gap-override" line_gap_override / mLineGapOverride: MetricsOverride, 751 752 /// The size adjustment for this font face. 753 "size-adjust" size_adjust / mSizeAdjust: NonNegativePercentage, 754 ] 755 }