lewp_css/domain/at_rules/font_face/
font_face_at_rule.rs

1// This file is part of css. It is subject to the license terms in the COPYRIGHT file found in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/css/master/COPYRIGHT. No part of predicator, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the COPYRIGHT file.
2// Copyright © 2017 The developers of css. See the COPYRIGHT file in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/css/master/COPYRIGHT.
3
4use {
5    super::{
6        FamilyName,
7        FontDisplay,
8        FontFace,
9        FontFeatureSettings,
10        FontLanguageOverride,
11        FontStretch,
12        FontStyle,
13        FontWeight,
14        Source,
15    },
16    crate::{
17        parsers::{FontFaceAtRuleParser, ParserContext},
18        CustomParseError,
19    },
20    cssparser::{
21        DeclarationListParser,
22        ParseError,
23        Parser,
24        ToCss,
25        UnicodeRange,
26    },
27    std::fmt,
28};
29
30/// A `@font-face` rule.
31///
32/// <https://drafts.csswg.org/css-fonts/#font-face-rule>
33#[derive(Debug, Clone)]
34pub struct FontFaceAtRule {
35    /// The name of this font face
36    pub family: Option<FamilyName>,
37
38    /// The alternative sources for this font face.
39    pub sources: Option<Vec<Source>>,
40
41    /// The style of this font face
42    pub style: Option<FontStyle>,
43
44    /// The weight of this font face
45    pub weight: Option<FontWeight>,
46
47    /// The stretch of this font face
48    pub stretch: Option<FontStretch>,
49
50    /// The display of this font face
51    pub display: Option<FontDisplay>,
52
53    /// The ranges of code points outside of which this font face should not be used.
54    pub unicode_range: Option<Vec<UnicodeRange>>,
55
56    /// The feature settings of this font face.
57    pub feature_settings: Option<FontFeatureSettings>,
58
59    /// The language override of this font face.
60    pub language_override: Option<FontLanguageOverride>,
61}
62
63impl ToCss for FontFaceAtRule {
64    fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
65        #[inline(always)]
66        fn writePropertyDeclaration<W: fmt::Write, T: ToCss>(
67            afterFirst: &mut bool,
68            dest: &mut W,
69            name: &str,
70            value: &Option<T>,
71        ) -> fmt::Result {
72            if let &Some(ref value) = value {
73                if *afterFirst {
74                    dest.write_char(';')?;
75                } else {
76                    *afterFirst = true;
77                }
78
79                dest.write_str(name)?;
80                dest.write_char(':')?;
81                value.to_css(dest)
82            } else {
83                Ok(())
84            }
85        }
86
87        #[inline(always)]
88        fn writePropertyDeclarationValues<W: fmt::Write, T: ToCss>(
89            afterFirst: &mut bool,
90            dest: &mut W,
91            name: &str,
92            value: &Option<Vec<T>>,
93        ) -> fmt::Result {
94            if let &Some(ref value) = value {
95                if value.is_empty() {
96                    return Ok(());
97                }
98
99                if *afterFirst {
100                    dest.write_char(';')?;
101                } else {
102                    *afterFirst = true;
103                }
104
105                dest.write_str(name)?;
106                dest.write_char(':')?;
107
108                let mut iterator = value.iter();
109                iterator.next().unwrap().to_css(dest)?;
110                for value in iterator {
111                    dest.write_char(',')?;
112                    value.to_css(dest)?;
113                }
114            }
115
116            Ok(())
117        }
118
119        dest.write_str("@font-face{")?;
120
121        let mut afterFirst = false;
122        writePropertyDeclaration(
123            &mut afterFirst,
124            dest,
125            "font-family",
126            &self.family,
127        )?;
128        writePropertyDeclarationValues(
129            &mut afterFirst,
130            dest,
131            "src",
132            &self.sources,
133        )?;
134        writePropertyDeclaration(
135            &mut afterFirst,
136            dest,
137            "font-style",
138            &self.style,
139        )?;
140        writePropertyDeclaration(
141            &mut afterFirst,
142            dest,
143            "font-weight",
144            &self.weight,
145        )?;
146        writePropertyDeclaration(
147            &mut afterFirst,
148            dest,
149            "font-stretch",
150            &self.stretch,
151        )?;
152        writePropertyDeclaration(
153            &mut afterFirst,
154            dest,
155            "font-display",
156            &self.display,
157        )?;
158        writePropertyDeclarationValues(
159            &mut afterFirst,
160            dest,
161            "unicode-range",
162            &self.unicode_range,
163        )?;
164        writePropertyDeclaration(
165            &mut afterFirst,
166            dest,
167            "font-feature-settings",
168            &self.feature_settings,
169        )?;
170        writePropertyDeclaration(
171            &mut afterFirst,
172            dest,
173            "font-language-override",
174            &self.language_override,
175        )?;
176
177        dest.write_char('}')
178    }
179}
180
181impl FontFaceAtRule {
182    #[inline(always)]
183    fn empty() -> Self {
184        Self {
185            family: None,
186            sources: None,
187            style: None,
188            weight: None,
189            stretch: None,
190            display: None,
191            unicode_range: None,
192            feature_settings: None,
193            language_override: None,
194        }
195    }
196
197    /// Parse the block inside a `@font-face` rule.
198    pub(crate) fn parse_body<'i: 't, 't>(
199        context: &ParserContext,
200        input: &mut Parser<'i, 't>,
201    ) -> Result<FontFaceAtRule, ParseError<'i, CustomParseError<'i>>> {
202        let mut rule = Self::empty();
203
204        {
205            let parser = FontFaceAtRuleParser {
206                context,
207                rule: &mut rule,
208            };
209
210            let iter = DeclarationListParser::new(input, parser);
211            for declaration in iter {
212                if declaration.is_err() {
213                    return Err(declaration.unwrap_err().0);
214                }
215            }
216        }
217
218        Ok(rule)
219    }
220
221    /// Per <https://github.com/w3c/csswg-drafts/issues/1133> an @font-face rule is valid as far as the CSS parser is concerned even if it doesn't have a font-family or src declaration.
222    ///
223    /// However both are required for the rule to represent an actual font face.
224    pub fn font_face(&self) -> Option<FontFace> {
225        if self.family.is_some() && self.sources.is_some() {
226            Some(FontFace(self))
227        } else {
228            None
229        }
230    }
231}