11//! The `@page` rule.
22
33use super :: Location ;
4- use crate :: declaration:: DeclarationBlock ;
4+ use crate :: declaration:: { parse_declaration , DeclarationBlock } ;
55use crate :: error:: { ParserError , PrinterError } ;
66use crate :: macros:: enum_property;
77use crate :: printer:: Printer ;
8+ use crate :: stylesheet:: ParserOptions ;
89use crate :: traits:: { Parse , ToCss } ;
910use crate :: values:: string:: CowArcStr ;
1011use crate :: visitor:: Visit ;
@@ -69,6 +70,74 @@ impl<'i> Parse<'i> for PageSelector<'i> {
6970 }
7071}
7172
73+ enum_property ! {
74+ /// A [page margin box](https://www.w3.org/TR/css-page-3/#margin-boxes).
75+ pub enum PageMarginBox {
76+ /// A fixed-size box defined by the intersection of the top and left margins of the page box.
77+ "top-left-corner" : TopLeftCorner ,
78+ /// A variable-width box filling the top page margin between the top-left-corner and top-center page-margin boxes.
79+ "top-left" : TopLeft ,
80+ /// A variable-width box centered horizontally between the page’s left and right border edges and filling the
81+ /// page top margin between the top-left and top-right page-margin boxes.
82+ "top-center" : TopCenter ,
83+ /// A variable-width box filling the top page margin between the top-center and top-right-corner page-margin boxes.
84+ "top-right" : TopRight ,
85+ /// A fixed-size box defined by the intersection of the top and right margins of the page box.
86+ "top-right-corner" : TopRightCorner ,
87+ /// A variable-height box filling the left page margin between the top-left-corner and left-middle page-margin boxes.
88+ "left-top" : LeftTop ,
89+ /// A variable-height box centered vertically between the page’s top and bottom border edges and filling the
90+ /// left page margin between the left-top and left-bottom page-margin boxes.
91+ "left-middle" : LeftMiddle ,
92+ /// A variable-height box filling the left page margin between the left-middle and bottom-left-corner page-margin boxes.
93+ "left-bottom" : LeftBottom ,
94+ /// A variable-height box filling the right page margin between the top-right-corner and right-middle page-margin boxes.
95+ "right-top" : RightTop ,
96+ /// A variable-height box centered vertically between the page’s top and bottom border edges and filling the right
97+ /// page margin between the right-top and right-bottom page-margin boxes.
98+ "right-middle" : RightMiddle ,
99+ /// A variable-height box filling the right page margin between the right-middle and bottom-right-corner page-margin boxes.
100+ "right-bottom" : RightBottom ,
101+ /// A fixed-size box defined by the intersection of the bottom and left margins of the page box.
102+ "bottom-left-corner" : BottomLeftCorner ,
103+ /// A variable-width box filling the bottom page margin between the bottom-left-corner and bottom-center page-margin boxes.
104+ "bottom-left" : BottomLeft ,
105+ /// A variable-width box centered horizontally between the page’s left and right border edges and filling the bottom
106+ /// page margin between the bottom-left and bottom-right page-margin boxes.
107+ "bottom-center" : BottomCenter ,
108+ /// A variable-width box filling the bottom page margin between the bottom-center and bottom-right-corner page-margin boxes.
109+ "bottom-right" : BottomRight ,
110+ /// A fixed-size box defined by the intersection of the bottom and right margins of the page box.
111+ "bottom-right-corner" : BottomRightCorner ,
112+ }
113+ }
114+
115+ /// A [page margin rule](https://www.w3.org/TR/css-page-3/#margin-at-rules) rule.
116+ #[ derive( Debug , PartialEq , Clone , Visit ) ]
117+ #[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
118+ pub struct PageMarginRule < ' i > {
119+ /// The margin box identifier for this rule.
120+ pub margin_box : PageMarginBox ,
121+ /// The declarations within the rule.
122+ #[ cfg_attr( feature = "serde" , serde( borrow) ) ]
123+ pub declarations : DeclarationBlock < ' i > ,
124+ /// The location of the rule in the source file.
125+ #[ skip_visit]
126+ pub loc : Location ,
127+ }
128+
129+ impl < ' i > ToCss for PageMarginRule < ' i > {
130+ fn to_css < W > ( & self , dest : & mut Printer < W > ) -> Result < ( ) , PrinterError >
131+ where
132+ W : std:: fmt:: Write ,
133+ {
134+ dest. add_mapping ( self . loc ) ;
135+ dest. write_char ( '@' ) ?;
136+ self . margin_box . to_css ( dest) ?;
137+ self . declarations . to_css_block ( dest)
138+ }
139+ }
140+
72141/// A [@page](https://www.w3.org/TR/css-page-3/#at-page-rule) rule.
73142#[ derive( Debug , PartialEq , Clone , Visit ) ]
74143#[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
@@ -79,11 +148,50 @@ pub struct PageRule<'i> {
79148 pub selectors : Vec < PageSelector < ' i > > ,
80149 /// The declarations within the `@page` rule.
81150 pub declarations : DeclarationBlock < ' i > ,
151+ /// The nested margin rules.
152+ pub rules : Vec < PageMarginRule < ' i > > ,
82153 /// The location of the rule in the source file.
83154 #[ skip_visit]
84155 pub loc : Location ,
85156}
86157
158+ impl < ' i > PageRule < ' i > {
159+ pub ( crate ) fn parse < ' t , ' o , T > (
160+ selectors : Vec < PageSelector < ' i > > ,
161+ input : & mut Parser < ' i , ' t > ,
162+ loc : Location ,
163+ options : & ParserOptions < ' o , ' i , T > ,
164+ ) -> Result < Self , ParseError < ' i , ParserError < ' i > > > {
165+ let mut declarations = DeclarationBlock :: new ( ) ;
166+ let mut rules = Vec :: new ( ) ;
167+ let mut parser = DeclarationListParser :: new (
168+ input,
169+ PageRuleParser {
170+ declarations : & mut declarations,
171+ rules : & mut rules,
172+ options : & options,
173+ } ,
174+ ) ;
175+
176+ while let Some ( decl) = parser. next ( ) {
177+ if let Err ( ( err, _) ) = decl {
178+ if parser. parser . options . error_recovery {
179+ parser. parser . options . warn ( err) ;
180+ continue ;
181+ }
182+ return Err ( err) ;
183+ }
184+ }
185+
186+ Ok ( PageRule {
187+ selectors,
188+ declarations,
189+ rules,
190+ loc,
191+ } )
192+ }
193+ }
194+
87195impl < ' i > ToCss for PageRule < ' i > {
88196 fn to_css < W > ( & self , dest : & mut Printer < W > ) -> Result < ( ) , PrinterError >
89197 where
@@ -106,7 +214,53 @@ impl<'i> ToCss for PageRule<'i> {
106214 selector. to_css ( dest) ?;
107215 }
108216 }
109- self . declarations . to_css_block ( dest)
217+
218+ dest. whitespace ( ) ?;
219+ dest. write_char ( '{' ) ?;
220+ dest. indent ( ) ;
221+
222+ let mut i = 0 ;
223+ let len = self . declarations . len ( ) + self . rules . len ( ) ;
224+
225+ macro_rules! write {
226+ ( $decls: expr, $important: literal) => {
227+ for decl in & $decls {
228+ dest. newline( ) ?;
229+ decl. to_css( dest, $important) ?;
230+ if i != len - 1 || !dest. minify {
231+ dest. write_char( ';' ) ?;
232+ }
233+ i += 1 ;
234+ }
235+ } ;
236+ }
237+
238+ write ! ( self . declarations. declarations, false ) ;
239+ write ! ( self . declarations. important_declarations, true ) ;
240+
241+ if !self . rules . is_empty ( ) {
242+ if !dest. minify && self . declarations . len ( ) > 0 {
243+ dest. write_char ( '\n' ) ?;
244+ }
245+ dest. newline ( ) ?;
246+
247+ let mut first = true ;
248+ for rule in & self . rules {
249+ if first {
250+ first = false ;
251+ } else {
252+ if !dest. minify {
253+ dest. write_char ( '\n' ) ?;
254+ }
255+ dest. newline ( ) ?;
256+ }
257+ rule. to_css ( dest) ?;
258+ }
259+ }
260+
261+ dest. dedent ( ) ;
262+ dest. newline ( ) ?;
263+ dest. write_char ( '}' )
110264 }
111265}
112266
@@ -127,3 +281,64 @@ impl<'i> ToCss for PageSelector<'i> {
127281 Ok ( ( ) )
128282 }
129283}
284+
285+ struct PageRuleParser < ' a , ' o , ' i , T > {
286+ declarations : & ' a mut DeclarationBlock < ' i > ,
287+ rules : & ' a mut Vec < PageMarginRule < ' i > > ,
288+ options : & ' a ParserOptions < ' o , ' i , T > ,
289+ }
290+
291+ impl < ' a , ' o , ' i , T > cssparser:: DeclarationParser < ' i > for PageRuleParser < ' a , ' o , ' i , T > {
292+ type Declaration = ( ) ;
293+ type Error = ParserError < ' i > ;
294+
295+ fn parse_value < ' t > (
296+ & mut self ,
297+ name : CowRcStr < ' i > ,
298+ input : & mut cssparser:: Parser < ' i , ' t > ,
299+ ) -> Result < Self :: Declaration , cssparser:: ParseError < ' i , Self :: Error > > {
300+ parse_declaration (
301+ name,
302+ input,
303+ & mut self . declarations . declarations ,
304+ & mut self . declarations . important_declarations ,
305+ & self . options ,
306+ )
307+ }
308+ }
309+
310+ impl < ' a , ' o , ' i , T > AtRuleParser < ' i > for PageRuleParser < ' a , ' o , ' i , T > {
311+ type Prelude = PageMarginBox ;
312+ type AtRule = ( ) ;
313+ type Error = ParserError < ' i > ;
314+
315+ fn parse_prelude < ' t > (
316+ & mut self ,
317+ name : CowRcStr < ' i > ,
318+ input : & mut Parser < ' i , ' t > ,
319+ ) -> Result < Self :: Prelude , ParseError < ' i , Self :: Error > > {
320+ let loc = input. current_source_location ( ) ;
321+ PageMarginBox :: parse_string ( & name)
322+ . map_err ( |_| loc. new_custom_error ( ParserError :: AtRuleInvalid ( name. clone ( ) . into ( ) ) ) )
323+ }
324+
325+ fn parse_block < ' t > (
326+ & mut self ,
327+ prelude : Self :: Prelude ,
328+ start : & ParserState ,
329+ input : & mut Parser < ' i , ' t > ,
330+ ) -> Result < Self :: AtRule , ParseError < ' i , Self :: Error > > {
331+ let loc = start. source_location ( ) ;
332+ let declarations = DeclarationBlock :: parse ( input, self . options ) ?;
333+ self . rules . push ( PageMarginRule {
334+ margin_box : prelude,
335+ declarations,
336+ loc : Location {
337+ source_index : self . options . source_index ,
338+ line : loc. line ,
339+ column : loc. column ,
340+ } ,
341+ } ) ;
342+ Ok ( ( ) )
343+ }
344+ }
0 commit comments