1use super::Location;
4use crate::declaration::{parse_declaration, DeclarationBlock};
5use crate::error::{ParserError, PrinterError};
6use crate::macros::enum_property;
7use crate::printer::Printer;
8use crate::stylesheet::ParserOptions;
9use crate::traits::{Parse, ToCss};
10use crate::values::string::CowArcStr;
11#[cfg(feature = "visitor")]
12use crate::visitor::Visit;
13use cssparser::*;
14
15#[derive(Debug, PartialEq, Clone)]
20#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
21#[cfg_attr(
22 feature = "serde",
23 derive(serde::Serialize, serde::Deserialize),
24 serde(rename_all = "camelCase")
25)]
26#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
27pub struct PageSelector<'i> {
28 #[cfg_attr(feature = "serde", serde(borrow))]
30 pub name: Option<CowArcStr<'i>>,
31 pub pseudo_classes: Vec<PagePseudoClass>,
33}
34
35enum_property! {
36 pub enum PagePseudoClass {
40 Left,
42 Right,
44 First,
46 Last,
48 Blank,
50 }
51}
52
53impl<'i> Parse<'i> for PageSelector<'i> {
54 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
55 let name = input.try_parse(|input| input.expect_ident().map(|x| x.into())).ok();
56 let mut pseudo_classes = vec![];
57
58 loop {
59 let state = input.state();
61 match input.next_including_whitespace() {
62 Ok(Token::Colon) => {
63 pseudo_classes.push(PagePseudoClass::parse(input)?);
64 }
65 _ => {
66 input.reset(&state);
67 break;
68 }
69 }
70 }
71
72 if name.is_none() && pseudo_classes.is_empty() {
73 return Err(input.new_custom_error(ParserError::InvalidPageSelector));
74 }
75
76 Ok(PageSelector { name, pseudo_classes })
77 }
78}
79
80enum_property! {
81 pub enum PageMarginBox {
83 TopLeftCorner,
85 TopLeft,
87 TopCenter,
90 TopRight,
92 TopRightCorner,
94 LeftTop,
96 LeftMiddle,
99 LeftBottom,
101 RightTop,
103 RightMiddle,
106 RightBottom,
108 BottomLeftCorner,
110 BottomLeft,
112 BottomCenter,
115 BottomRight,
117 BottomRightCorner,
119 }
120}
121
122#[derive(Debug, PartialEq, Clone)]
124#[cfg_attr(feature = "visitor", derive(Visit))]
125#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
126#[cfg_attr(
127 feature = "serde",
128 derive(serde::Serialize, serde::Deserialize),
129 serde(rename_all = "camelCase")
130)]
131#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
132pub struct PageMarginRule<'i> {
133 pub margin_box: PageMarginBox,
135 #[cfg_attr(feature = "serde", serde(borrow))]
137 pub declarations: DeclarationBlock<'i>,
138 #[cfg_attr(feature = "visitor", skip_visit)]
140 pub loc: Location,
141}
142
143impl<'i> ToCss for PageMarginRule<'i> {
144 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
145 where
146 W: std::fmt::Write,
147 {
148 #[cfg(feature = "sourcemap")]
149 dest.add_mapping(self.loc);
150 dest.write_char('@')?;
151 self.margin_box.to_css(dest)?;
152 self.declarations.to_css_block(dest)
153 }
154}
155
156#[derive(Debug, PartialEq, Clone)]
158#[cfg_attr(feature = "visitor", derive(Visit))]
159#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
160#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
161#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
162pub struct PageRule<'i> {
163 #[cfg_attr(feature = "serde", serde(borrow))]
165 #[cfg_attr(feature = "visitor", skip_visit)]
166 pub selectors: Vec<PageSelector<'i>>,
167 pub declarations: DeclarationBlock<'i>,
169 pub rules: Vec<PageMarginRule<'i>>,
171 #[cfg_attr(feature = "visitor", skip_visit)]
173 pub loc: Location,
174}
175
176impl<'i> PageRule<'i> {
177 pub(crate) fn parse<'t, 'o>(
178 selectors: Vec<PageSelector<'i>>,
179 input: &mut Parser<'i, 't>,
180 loc: Location,
181 options: &ParserOptions<'o, 'i>,
182 ) -> Result<Self, ParseError<'i, ParserError<'i>>> {
183 let mut declarations = DeclarationBlock::new();
184 let mut rules = Vec::new();
185 let mut rule_parser = PageRuleParser {
186 declarations: &mut declarations,
187 rules: &mut rules,
188 options: &options,
189 };
190 let mut parser = RuleBodyParser::new(input, &mut rule_parser);
191
192 while let Some(decl) = parser.next() {
193 if let Err((err, _)) = decl {
194 if parser.parser.options.error_recovery {
195 parser.parser.options.warn(err);
196 continue;
197 }
198 return Err(err);
199 }
200 }
201
202 Ok(PageRule {
203 selectors,
204 declarations,
205 rules,
206 loc,
207 })
208 }
209}
210
211impl<'i> ToCss for PageRule<'i> {
212 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
213 where
214 W: std::fmt::Write,
215 {
216 #[cfg(feature = "sourcemap")]
217 dest.add_mapping(self.loc);
218 dest.write_str("@page")?;
219 if let Some(first) = self.selectors.first() {
220 if !dest.minify || first.name.is_some() {
222 dest.write_char(' ')?;
223 }
224 let mut first = true;
225 for selector in &self.selectors {
226 if first {
227 first = false;
228 } else {
229 dest.delim(',', false)?;
230 }
231 selector.to_css(dest)?;
232 }
233 }
234
235 dest.whitespace()?;
236 dest.write_char('{')?;
237 dest.indent();
238
239 let mut i = 0;
240 let len = self.declarations.len() + self.rules.len();
241
242 macro_rules! write {
243 ($decls: expr, $important: literal) => {
244 for decl in &$decls {
245 dest.newline()?;
246 decl.to_css(dest, $important)?;
247 if i != len - 1 || !dest.minify {
248 dest.write_char(';')?;
249 }
250 i += 1;
251 }
252 };
253 }
254
255 write!(self.declarations.declarations, false);
256 write!(self.declarations.important_declarations, true);
257
258 if !self.rules.is_empty() {
259 if !dest.minify && self.declarations.len() > 0 {
260 dest.write_char('\n')?;
261 }
262 dest.newline()?;
263
264 let mut first = true;
265 for rule in &self.rules {
266 if first {
267 first = false;
268 } else {
269 if !dest.minify {
270 dest.write_char('\n')?;
271 }
272 dest.newline()?;
273 }
274 rule.to_css(dest)?;
275 }
276 }
277
278 dest.dedent();
279 dest.newline()?;
280 dest.write_char('}')
281 }
282}
283
284impl<'i> ToCss for PageSelector<'i> {
285 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
286 where
287 W: std::fmt::Write,
288 {
289 if let Some(name) = &self.name {
290 dest.write_str(&name)?;
291 }
292
293 for pseudo in &self.pseudo_classes {
294 dest.write_char(':')?;
295 pseudo.to_css(dest)?;
296 }
297
298 Ok(())
299 }
300}
301
302struct PageRuleParser<'a, 'o, 'i> {
303 declarations: &'a mut DeclarationBlock<'i>,
304 rules: &'a mut Vec<PageMarginRule<'i>>,
305 options: &'a ParserOptions<'o, 'i>,
306}
307
308impl<'a, 'o, 'i> cssparser::DeclarationParser<'i> for PageRuleParser<'a, 'o, 'i> {
309 type Declaration = ();
310 type Error = ParserError<'i>;
311
312 fn parse_value<'t>(
313 &mut self,
314 name: CowRcStr<'i>,
315 input: &mut cssparser::Parser<'i, 't>,
316 ) -> Result<Self::Declaration, cssparser::ParseError<'i, Self::Error>> {
317 parse_declaration(
318 name,
319 input,
320 &mut self.declarations.declarations,
321 &mut self.declarations.important_declarations,
322 &self.options,
323 )
324 }
325}
326
327impl<'a, 'o, 'i> AtRuleParser<'i> for PageRuleParser<'a, 'o, 'i> {
328 type Prelude = PageMarginBox;
329 type AtRule = ();
330 type Error = ParserError<'i>;
331
332 fn parse_prelude<'t>(
333 &mut self,
334 name: CowRcStr<'i>,
335 input: &mut Parser<'i, 't>,
336 ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
337 let loc = input.current_source_location();
338 PageMarginBox::parse_string(&name)
339 .map_err(|_| loc.new_custom_error(ParserError::AtRuleInvalid(name.clone().into())))
340 }
341
342 fn parse_block<'t>(
343 &mut self,
344 prelude: Self::Prelude,
345 start: &ParserState,
346 input: &mut Parser<'i, 't>,
347 ) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
348 let loc = start.source_location();
349 let declarations = DeclarationBlock::parse(input, self.options)?;
350 self.rules.push(PageMarginRule {
351 margin_box: prelude,
352 declarations,
353 loc: Location {
354 source_index: self.options.source_index,
355 line: loc.line,
356 column: loc.column,
357 },
358 });
359 Ok(())
360 }
361}
362
363impl<'a, 'o, 'i> QualifiedRuleParser<'i> for PageRuleParser<'a, 'o, 'i> {
364 type Prelude = ();
365 type QualifiedRule = ();
366 type Error = ParserError<'i>;
367}
368
369impl<'a, 'o, 'i> RuleBodyItemParser<'i, (), ParserError<'i>> for PageRuleParser<'a, 'o, 'i> {
370 fn parse_qualified(&self) -> bool {
371 false
372 }
373
374 fn parse_declarations(&self) -> bool {
375 true
376 }
377}