@@ -12,8 +12,8 @@ Since it's very simple we will start with limitations:
1212
1313## Limitations
1414
15- - [At -rules](https://www.w3.org/TR/CSS21/syndata.html#at-rules) are not supported.
16- They will be skipped during parsing.
15+ - [Most at -rules](https://www.w3.org/TR/CSS21/syndata.html#at-rules) are not supported.
16+ They will be skipped during parsing. The only supported at-rule is `@font-face`.
1717- Property values are not parsed.
1818 In CSS like `* { width: 5px }` you will get a `width` property with a `5px` value as a string.
1919- CDO/CDC comments are not supported.
@@ -24,6 +24,7 @@ Since it's very simple we will start with limitations:
2424
2525- Selector matching support.
2626- The rules are sorted by specificity.
27+ - `@font-face` parsing support.
2728- `!important` parsing support.
2829- Has a high-level parsers and low-level, zero-allocation tokenizers.
2930- No unsafe.
@@ -192,6 +193,13 @@ pub struct Declaration<'a> {
192193 pub important : bool ,
193194}
194195
196+ /// A `@font-face` rule.
197+ #[ derive( Clone , Debug ) ]
198+ pub struct FontFaceRule < ' a > {
199+ /// A list of declarations inside this `@font-face` rule.
200+ pub declarations : Vec < Declaration < ' a > > ,
201+ }
202+
195203/// A rule.
196204#[ derive( Clone , Debug ) ]
197205pub struct Rule < ' a > {
@@ -206,17 +214,23 @@ pub struct Rule<'a> {
206214pub struct StyleSheet < ' a > {
207215 /// A list of rules.
208216 pub rules : Vec < Rule < ' a > > ,
217+ /// A list of `@font-face` rules.
218+ pub font_faces : Vec < FontFaceRule < ' a > > ,
209219}
210220
211221impl < ' a > StyleSheet < ' a > {
212222 /// Creates an empty style sheet.
213223 pub fn new ( ) -> Self {
214- StyleSheet { rules : Vec :: new ( ) }
224+ StyleSheet {
225+ rules : Vec :: new ( ) ,
226+ font_faces : Vec :: new ( ) ,
227+ }
215228 }
216229
217230 /// Parses a style sheet from text.
218231 ///
219- /// At-rules are not supported and will be skipped.
232+ /// Most at-rules are not supported and will be skipped, except `@font-face`
233+ /// rules which are parsed into [`FontFaceRule`]s.
220234 ///
221235 /// # Errors
222236 ///
@@ -242,7 +256,7 @@ impl<'a> StyleSheet<'a> {
242256 break ;
243257 }
244258
245- let _ = consume_statement ( & mut s, & mut self . rules ) ;
259+ let _ = consume_statement ( & mut s, & mut self . rules , & mut self . font_faces ) ;
246260 }
247261
248262 if !s. at_end ( ) {
@@ -286,19 +300,50 @@ impl Default for StyleSheet<'_> {
286300 }
287301}
288302
289- fn consume_statement < ' a > ( s : & mut Stream < ' a > , rules : & mut Vec < Rule < ' a > > ) -> Result < ( ) , Error > {
303+ fn consume_statement < ' a > (
304+ s : & mut Stream < ' a > ,
305+ rules : & mut Vec < Rule < ' a > > ,
306+ font_faces : & mut Vec < FontFaceRule < ' a > > ,
307+ ) -> Result < ( ) , Error > {
290308 if s. curr_byte ( ) == Ok ( b'@' ) {
291309 s. advance ( 1 ) ;
292- consume_at_rule ( s)
310+ consume_at_rule ( s, font_faces )
293311 } else {
294312 consume_rule_set ( s, rules)
295313 }
296314}
297315
298- fn consume_at_rule ( s : & mut Stream < ' _ > ) -> Result < ( ) , Error > {
316+ fn consume_at_rule < ' a > (
317+ s : & mut Stream < ' a > ,
318+ font_faces : & mut Vec < FontFaceRule < ' a > > ,
319+ ) -> Result < ( ) , Error > {
299320 let ident = s. consume_ident ( ) ?;
300- warn ! ( "The @{} rule is not supported. Skipped." , ident) ;
301321
322+ if ident == "font-face" {
323+ s. skip_spaces_and_comments ( ) ?;
324+
325+ if s. curr_byte ( ) == Ok ( b'{' ) {
326+ s. advance ( 1 ) ;
327+
328+ let declarations = consume_declarations ( s) ?;
329+ s. try_consume_byte ( b'}' ) ;
330+
331+ if !declarations. is_empty ( ) {
332+ font_faces. push ( FontFaceRule { declarations } ) ;
333+ }
334+ } else {
335+ // Malformed `@font-face`; fall back to skipping it as an unknown at-rule.
336+ skip_at_rule_body ( s) ?;
337+ }
338+ } else {
339+ warn ! ( "The @{} rule is not supported. Skipped." , ident) ;
340+ skip_at_rule_body ( s) ?;
341+ }
342+
343+ Ok ( ( ) )
344+ }
345+
346+ fn skip_at_rule_body ( s : & mut Stream < ' _ > ) -> Result < ( ) , Error > {
302347 s. skip_bytes ( |c| c != b';' && c != b'{' ) ;
303348
304349 match s. curr_byte ( ) ? {
0 commit comments