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/. */
5 //! The [`@font-face`][ff] at-rule.
7 //! [ff]: https://drafts.csswg.org/css-fonts/#at-font-face-rule
9 use crate::error_reporting::ContextualParseError;
10 use crate::parser::{Parse, ParserContext};
11 #[cfg(feature = "gecko")]
12 use crate::properties::longhands::font_language_override;
13 use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
14 use crate::str::CssStringWriter;
15 use crate::values::computed::font::FamilyName;
16 use crate::values::generics::font::FontStyle as GenericFontStyle;
17 #[cfg(feature = "gecko")]
18 use crate::values::specified::font::SpecifiedFontFeatureSettings;
19 use crate::values::specified::font::SpecifiedFontStyle;
20 #[cfg(feature = "gecko")]
21 use crate::values::specified::font::SpecifiedFontVariationSettings;
22 use crate::values::specified::font::{AbsoluteFontWeight, FontStretch, MetricsOverride};
23 use crate::values::specified::url::SpecifiedUrl;
24 use crate::values::specified::{Angle, NonNegativePercentage};
25 #[cfg(feature = "gecko")]
26 use cssparser::UnicodeRange;
27 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
28 use cssparser::{CowRcStr, SourceLocation};
29 use selectors::parser::SelectorParseErrorKind;
30 use std::fmt::{self, Write};
31 use style_traits::values::SequenceWriter;
32 use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
33 use style_traits::{StyleParseErrorKind, ToCss};
35 /// A source for a font-face rule.
36 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
37 #[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
41 /// A `local()` source.
46 impl OneOrMoreSeparated for Source {
50 /// A POD representation for Gecko. All pointers here are non-owned and as such
51 /// can't outlive the rule they came from, but we can't enforce that via C++.
53 /// All the strings are of course utf8.
54 #[cfg(feature = "gecko")]
56 #[allow(missing_docs)]
57 pub enum FontFaceSourceListComponent {
58 Url(*const crate::gecko::url::CssUrl),
59 Local(*mut crate::gecko_bindings::structs::nsAtom),
62 utf8_bytes: *const u8,
66 /// A `UrlSource` represents a font-face source that has been specified with a
69 /// <https://drafts.csswg.org/css-fonts/#src-desc>
70 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
71 #[derive(Clone, Debug, Eq, PartialEq, ToShmem)]
72 pub struct UrlSource {
73 /// The specified url.
74 pub url: SpecifiedUrl,
75 /// The format hints specified with the `format()` function.
76 pub format_hints: Vec<String>,
79 impl ToCss for UrlSource {
80 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
84 self.url.to_css(dest)?;
85 if !self.format_hints.is_empty() {
86 dest.write_str(" format(")?;
88 let mut writer = SequenceWriter::new(dest, ", ");
89 for hint in self.format_hints.iter() {
93 dest.write_char(')')?;
99 /// A font-display value for a @font-face rule.
100 /// The font-display descriptor determines how a font face is displayed based
101 /// on whether and when it is downloaded and ready to use.
102 #[allow(missing_docs)]
103 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
105 Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss, ToShmem,
108 pub enum FontDisplay {
116 macro_rules! impl_range {
117 ($range:ident, $component:ident) => {
118 impl Parse for $range {
120 context: &ParserContext,
121 input: &mut Parser<'i, 't>,
122 ) -> Result<Self, ParseError<'i>> {
123 let first = $component::parse(context, input)?;
125 .try_parse(|input| $component::parse(context, input))
126 .unwrap_or_else(|_| first.clone());
127 Ok($range(first, second))
130 impl ToCss for $range {
131 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
135 self.0.to_css(dest)?;
136 if self.0 != self.1 {
137 dest.write_str(" ")?;
138 self.1.to_css(dest)?;
146 /// The font-weight descriptor:
148 /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-weight
149 #[derive(Clone, Debug, PartialEq, ToShmem)]
150 pub struct FontWeightRange(pub AbsoluteFontWeight, pub AbsoluteFontWeight);
151 impl_range!(FontWeightRange, AbsoluteFontWeight);
153 /// The computed representation of the above so Gecko can read them easily.
155 /// This one is needed because cbindgen doesn't know how to generate
156 /// specified::Number.
158 #[allow(missing_docs)]
159 pub struct ComputedFontWeightRange(f32, f32);
162 fn sort_range<T: PartialOrd>(a: T, b: T) -> (T, T) {
170 impl FontWeightRange {
171 /// Returns a computed font-stretch range.
172 pub fn compute(&self) -> ComputedFontWeightRange {
173 let (min, max) = sort_range(self.0.compute().0, self.1.compute().0);
174 ComputedFontWeightRange(min, max)
178 /// The font-stretch descriptor:
180 /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-stretch
181 #[derive(Clone, Debug, PartialEq, ToShmem)]
182 pub struct FontStretchRange(pub FontStretch, pub FontStretch);
183 impl_range!(FontStretchRange, FontStretch);
185 /// The computed representation of the above, so that
186 /// Gecko can read them easily.
188 #[allow(missing_docs)]
189 pub struct ComputedFontStretchRange(f32, f32);
191 impl FontStretchRange {
192 /// Returns a computed font-stretch range.
193 pub fn compute(&self) -> ComputedFontStretchRange {
194 fn compute_stretch(s: &FontStretch) -> f32 {
196 FontStretch::Keyword(ref kw) => kw.compute().0,
197 FontStretch::Stretch(ref p) => p.0.get(),
198 FontStretch::System(..) => unreachable!(),
202 let (min, max) = sort_range(compute_stretch(&self.0), compute_stretch(&self.1));
203 ComputedFontStretchRange(min, max)
207 /// The font-style descriptor:
209 /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-style
210 #[derive(Clone, Debug, PartialEq, ToShmem)]
211 #[allow(missing_docs)]
215 Oblique(Angle, Angle),
218 /// The computed representation of the above, with angles in degrees, so that
219 /// Gecko can read them easily.
221 #[allow(missing_docs)]
222 pub enum ComputedFontStyleDescriptor {
228 impl Parse for FontStyle {
230 context: &ParserContext,
231 input: &mut Parser<'i, 't>,
232 ) -> Result<Self, ParseError<'i>> {
233 let style = SpecifiedFontStyle::parse(context, input)?;
235 GenericFontStyle::Normal => FontStyle::Normal,
236 GenericFontStyle::Italic => FontStyle::Italic,
237 GenericFontStyle::Oblique(angle) => {
238 let second_angle = input
239 .try_parse(|input| SpecifiedFontStyle::parse_angle(context, input))
240 .unwrap_or_else(|_| angle.clone());
242 FontStyle::Oblique(angle, second_angle)
248 impl ToCss for FontStyle {
249 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
254 FontStyle::Normal => dest.write_str("normal"),
255 FontStyle::Italic => dest.write_str("italic"),
256 FontStyle::Oblique(ref first, ref second) => {
257 dest.write_str("oblique")?;
258 if *first != SpecifiedFontStyle::default_angle() || first != second {
259 dest.write_char(' ')?;
263 dest.write_char(' ')?;
264 second.to_css(dest)?;
273 /// Returns a computed font-style descriptor.
274 pub fn compute(&self) -> ComputedFontStyleDescriptor {
276 FontStyle::Normal => ComputedFontStyleDescriptor::Normal,
277 FontStyle::Italic => ComputedFontStyleDescriptor::Italic,
278 FontStyle::Oblique(ref first, ref second) => {
279 let (min, max) = sort_range(
280 SpecifiedFontStyle::compute_angle_degrees(first),
281 SpecifiedFontStyle::compute_angle_degrees(second),
283 ComputedFontStyleDescriptor::Oblique(min, max)
289 /// Parse the block inside a `@font-face` rule.
291 /// Note that the prelude parsing code lives in the `stylesheets` module.
292 pub fn parse_font_face_block(
293 context: &ParserContext,
295 location: SourceLocation,
296 ) -> FontFaceRuleData {
297 let mut rule = FontFaceRuleData::empty(location);
299 let parser = FontFaceRuleParser {
303 let mut iter = DeclarationListParser::new(input, parser);
304 while let Some(declaration) = iter.next() {
305 if let Err((error, slice)) = declaration {
306 let location = error.location;
307 let error = ContextualParseError::UnsupportedFontFaceDescriptor(slice, error);
308 context.log_css_error(location, error)
315 /// A @font-face rule that is known to have font-family and src declarations.
316 #[cfg(feature = "servo")]
317 pub struct FontFace<'a>(&'a FontFaceRuleData);
319 /// A list of effective sources that we send over through IPC to the font cache.
320 #[cfg(feature = "servo")]
321 #[derive(Clone, Debug)]
322 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
323 pub struct EffectiveSources(Vec<Source>);
325 #[cfg(feature = "servo")]
326 impl<'a> FontFace<'a> {
327 /// Returns the list of effective sources for that font-face, that is the
328 /// sources which don't list any format hint, or the ones which list at
329 /// least "truetype" or "opentype".
330 pub fn effective_sources(&self) -> EffectiveSources {
336 if let Source::Url(ref url_source) = **source {
337 let hints = &url_source.format_hints;
338 // We support only opentype fonts and truetype is an alias for
339 // that format. Sources without format hints need to be
340 // downloaded in case we support them.
342 hints.iter().any(|hint| {
343 hint == "truetype" || hint == "opentype" || hint == "woff"
355 #[cfg(feature = "servo")]
356 impl Iterator for EffectiveSources {
358 fn next(&mut self) -> Option<Source> {
362 fn size_hint(&self) -> (usize, Option<usize>) {
363 (self.0.len(), Some(self.0.len()))
367 struct FontFaceRuleParser<'a, 'b: 'a> {
368 context: &'a ParserContext<'b>,
369 rule: &'a mut FontFaceRuleData,
372 /// Default methods reject all at rules.
373 impl<'a, 'b, 'i> AtRuleParser<'i> for FontFaceRuleParser<'a, 'b> {
376 type Error = StyleParseErrorKind<'i>;
379 impl Parse for Source {
381 context: &ParserContext,
382 input: &mut Parser<'i, 't>,
383 ) -> Result<Source, ParseError<'i>> {
385 .try_parse(|input| input.expect_function_matching("local"))
389 .parse_nested_block(|input| FamilyName::parse(context, input))
393 let url = SpecifiedUrl::parse(context, input)?;
395 // Parsing optional format()
396 let format_hints = if input
397 .try_parse(|input| input.expect_function_matching("format"))
400 input.parse_nested_block(|input| {
401 input.parse_comma_separated(|input| Ok(input.expect_string()?.as_ref().to_owned()))
407 Ok(Source::Url(UrlSource {
409 format_hints: format_hints,
414 macro_rules! is_descriptor_enabled {
415 ("font-display") => {
416 static_prefs::pref!("layout.css.font-display.enabled")
418 ("font-variation-settings") => {
419 static_prefs::pref!("layout.css.font-variations.enabled")
421 ("ascent-override") => {
422 static_prefs::pref!("layout.css.font-metrics-overrides.enabled")
424 ("descent-override") => {
425 static_prefs::pref!("layout.css.font-metrics-overrides.enabled")
427 ("line-gap-override") => {
428 static_prefs::pref!("layout.css.font-metrics-overrides.enabled")
431 static_prefs::pref!("layout.css.size-adjust.enabled")
438 macro_rules! font_face_descriptors_common {
440 $( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty, )*
442 /// Data inside a `@font-face` rule.
444 /// <https://drafts.csswg.org/css-fonts/#font-face-rule>
445 #[derive(Clone, Debug, PartialEq, ToShmem)]
446 pub struct FontFaceRuleData {
449 pub $ident: Option<$ty>,
451 /// Line and column of the @font-face rule source code.
452 pub source_location: SourceLocation,
455 impl FontFaceRuleData {
456 /// Create an empty font-face rule
457 pub fn empty(location: SourceLocation) -> Self {
462 source_location: location,
466 /// Serialization of declarations in the FontFaceRule
467 pub fn decl_to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
469 if let Some(ref value) = self.$ident {
470 dest.write_str(concat!($name, ": "))?;
471 ToCss::to_css(value, &mut CssWriter::new(dest))?;
472 dest.write_str("; ")?;
479 impl<'a, 'b, 'i> DeclarationParser<'i> for FontFaceRuleParser<'a, 'b> {
480 type Declaration = ();
481 type Error = StyleParseErrorKind<'i>;
486 input: &mut Parser<'i, 't>,
487 ) -> Result<(), ParseError<'i>> {
488 match_ignore_ascii_case! { &*name,
490 $name if is_descriptor_enabled!($name) => {
491 // DeclarationParser also calls parse_entirely
492 // so we’d normally not need to,
493 // but in this case we do because we set the value as a side effect
494 // rather than returning it.
495 let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
496 self.rule.$ident = Some(value)
499 _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
507 impl ToCssWithGuard for FontFaceRuleData {
508 // Serialization of FontFaceRule is not specced.
509 fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
510 dest.write_str("@font-face { ")?;
511 self.decl_to_css(dest)?;
516 macro_rules! font_face_descriptors {
518 mandatory descriptors = [
519 $( #[$m_doc: meta] $m_name: tt $m_ident: ident / $m_gecko_ident: ident: $m_ty: ty, )*
521 optional descriptors = [
522 $( #[$o_doc: meta] $o_name: tt $o_ident: ident / $o_gecko_ident: ident: $o_ty: ty, )*
525 font_face_descriptors_common! {
526 $( #[$m_doc] $m_name $m_ident / $m_gecko_ident: $m_ty, )*
527 $( #[$o_doc] $o_name $o_ident / $o_gecko_ident: $o_ty, )*
530 impl FontFaceRuleData {
531 /// Per https://github.com/w3c/csswg-drafts/issues/1133 an @font-face rule
532 /// is valid as far as the CSS parser is concerned even if it doesn’t have
533 /// a font-family or src declaration.
535 /// However both are required for the rule to represent an actual font face.
536 #[cfg(feature = "servo")]
537 pub fn font_face(&self) -> Option<FontFace> {
538 if $( self.$m_ident.is_some() )&&* {
546 #[cfg(feature = "servo")]
547 impl<'a> FontFace<'a> {
550 pub fn $m_ident(&self) -> &$m_ty {
551 self.0 .$m_ident.as_ref().unwrap()
558 #[cfg(feature = "gecko")]
559 font_face_descriptors! {
560 mandatory descriptors = [
561 /// The name of this font face
562 "font-family" family / mFamily: FamilyName,
564 /// The alternative sources for this font face.
565 "src" sources / mSrc: Vec<Source>,
567 optional descriptors = [
568 /// The style of this font face.
569 "font-style" style / mStyle: FontStyle,
571 /// The weight of this font face.
572 "font-weight" weight / mWeight: FontWeightRange,
574 /// The stretch of this font face.
575 "font-stretch" stretch / mStretch: FontStretchRange,
577 /// The display of this font face.
578 "font-display" display / mDisplay: FontDisplay,
580 /// The ranges of code points outside of which this font face should not be used.
581 "unicode-range" unicode_range / mUnicodeRange: Vec<UnicodeRange>,
583 /// The feature settings of this font face.
584 "font-feature-settings" feature_settings / mFontFeatureSettings: SpecifiedFontFeatureSettings,
586 /// The variation settings of this font face.
587 "font-variation-settings" variation_settings / mFontVariationSettings: SpecifiedFontVariationSettings,
589 /// The language override of this font face.
590 "font-language-override" language_override / mFontLanguageOverride: font_language_override::SpecifiedValue,
592 /// The ascent override for this font face.
593 "ascent-override" ascent_override / mAscentOverride: MetricsOverride,
595 /// The descent override for this font face.
596 "descent-override" descent_override / mDescentOverride: MetricsOverride,
598 /// The line-gap override for this font face.
599 "line-gap-override" line_gap_override / mLineGapOverride: MetricsOverride,
601 /// The size adjustment for this font face.
602 "size-adjust" size_adjust / mSizeAdjust: NonNegativePercentage,
606 #[cfg(feature = "servo")]
607 font_face_descriptors! {
608 mandatory descriptors = [
609 /// The name of this font face
610 "font-family" family / mFamily: FamilyName,
612 /// The alternative sources for this font face.
613 "src" sources / mSrc: Vec<Source>,
615 optional descriptors = [