typst_library/layout/
page.rs

1use std::borrow::Cow;
2use std::num::NonZeroUsize;
3use std::ops::RangeInclusive;
4use std::str::FromStr;
5
6use comemo::Track;
7use typst_utils::{singleton, NonZeroExt, Scalar};
8
9use crate::diag::{bail, SourceResult};
10use crate::engine::Engine;
11use crate::foundations::{
12    cast, elem, Args, AutoValue, Cast, Construct, Content, Context, Dict, Fold, Func,
13    NativeElement, Set, Smart, StyleChain, Value,
14};
15use crate::introspection::Introspector;
16use crate::layout::{
17    Abs, Alignment, FlushElem, Frame, HAlignment, Length, OuterVAlignment, Ratio, Rel,
18    Sides, SpecificAlignment,
19};
20use crate::model::{DocumentInfo, Numbering};
21use crate::text::LocalName;
22use crate::visualize::{Color, Paint};
23
24/// Layouts its child onto one or multiple pages.
25///
26/// Although this function is primarily used in set rules to affect page
27/// properties, it can also be used to explicitly render its argument onto
28/// a set of pages of its own.
29///
30/// Pages can be set to use `{auto}` as their width or height. In this case, the
31/// pages will grow to fit their content on the respective axis.
32///
33/// The [Guide for Page Setup]($guides/page-setup-guide) explains how to use
34/// this and related functions to set up a document with many examples.
35///
36/// # Example
37/// ```example
38/// >>> #set page(margin: auto)
39/// #set page("us-letter")
40///
41/// There you go, US friends!
42/// ```
43#[elem(Construct)]
44pub struct PageElem {
45    /// A standard paper size to set width and height.
46    ///
47    /// This is just a shorthand for setting `width` and `height` and, as such,
48    /// cannot be retrieved in a context expression.
49    #[external]
50    #[default(Paper::A4)]
51    pub paper: Paper,
52
53    /// The width of the page.
54    ///
55    /// ```example
56    /// #set page(
57    ///   width: 3cm,
58    ///   margin: (x: 0cm),
59    /// )
60    ///
61    /// #for i in range(3) {
62    ///   box(square(width: 1cm))
63    /// }
64    /// ```
65    #[resolve]
66    #[parse(
67        let paper = args.named_or_find::<Paper>("paper")?;
68        args.named("width")?
69            .or_else(|| paper.map(|paper| Smart::Custom(paper.width().into())))
70    )]
71    #[default(Smart::Custom(Paper::A4.width().into()))]
72    #[ghost]
73    pub width: Smart<Length>,
74
75    /// The height of the page.
76    ///
77    /// If this is set to `{auto}`, page breaks can only be triggered manually
78    /// by inserting a [page break]($pagebreak). Most examples throughout this
79    /// documentation use `{auto}` for the height of the page to dynamically
80    /// grow and shrink to fit their content.
81    #[resolve]
82    #[parse(
83        args.named("height")?
84            .or_else(|| paper.map(|paper| Smart::Custom(paper.height().into())))
85    )]
86    #[default(Smart::Custom(Paper::A4.height().into()))]
87    #[ghost]
88    pub height: Smart<Length>,
89
90    /// Whether the page is flipped into landscape orientation.
91    ///
92    /// ```example
93    /// #set page(
94    ///   "us-business-card",
95    ///   flipped: true,
96    ///   fill: rgb("f2e5dd"),
97    /// )
98    ///
99    /// #set align(bottom + end)
100    /// #text(14pt)[*Sam H. Richards*] \
101    /// _Procurement Manager_
102    ///
103    /// #set text(10pt)
104    /// 17 Main Street \
105    /// New York, NY 10001 \
106    /// +1 555 555 5555
107    /// ```
108    #[default(false)]
109    #[ghost]
110    pub flipped: bool,
111
112    /// The page's margins.
113    ///
114    /// - `{auto}`: The margins are set automatically to 2.5/21 times the smaller
115    ///   dimension of the page. This results in 2.5cm margins for an A4 page.
116    /// - A single length: The same margin on all sides.
117    /// - A dictionary: With a dictionary, the margins can be set individually.
118    ///   The dictionary can contain the following keys in order of precedence:
119    ///   - `top`: The top margin.
120    ///   - `right`: The right margin.
121    ///   - `bottom`: The bottom margin.
122    ///   - `left`: The left margin.
123    ///   - `inside`: The margin at the inner side of the page (where the
124    ///     [binding]($page.binding) is).
125    ///   - `outside`: The margin at the outer side of the page (opposite to the
126    ///     [binding]($page.binding)).
127    ///   - `x`: The horizontal margins.
128    ///   - `y`: The vertical margins.
129    ///   - `rest`: The margins on all sides except those for which the
130    ///     dictionary explicitly sets a size.
131    ///
132    /// The values for `left` and `right` are mutually exclusive with
133    /// the values for `inside` and `outside`.
134    ///
135    /// ```example
136    /// #set page(
137    ///  width: 3cm,
138    ///  height: 4cm,
139    ///  margin: (x: 8pt, y: 4pt),
140    /// )
141    ///
142    /// #rect(
143    ///   width: 100%,
144    ///   height: 100%,
145    ///   fill: aqua,
146    /// )
147    /// ```
148    #[fold]
149    #[ghost]
150    pub margin: Margin,
151
152    /// On which side the pages will be bound.
153    ///
154    /// - `{auto}`: Equivalent to `left` if the [text direction]($text.dir)
155    ///   is left-to-right and `right` if it is right-to-left.
156    /// - `left`: Bound on the left side.
157    /// - `right`: Bound on the right side.
158    ///
159    /// This affects the meaning of the `inside` and `outside` options for
160    /// margins.
161    #[ghost]
162    pub binding: Smart<Binding>,
163
164    /// How many columns the page has.
165    ///
166    /// If you need to insert columns into a page or other container, you can
167    /// also use the [`columns` function]($columns).
168    ///
169    /// ```example:single
170    /// #set page(columns: 2, height: 4.8cm)
171    /// Climate change is one of the most
172    /// pressing issues of our time, with
173    /// the potential to devastate
174    /// communities, ecosystems, and
175    /// economies around the world. It's
176    /// clear that we need to take urgent
177    /// action to reduce our carbon
178    /// emissions and mitigate the impacts
179    /// of a rapidly changing climate.
180    /// ```
181    #[default(NonZeroUsize::ONE)]
182    #[ghost]
183    pub columns: NonZeroUsize,
184
185    /// The page's background fill.
186    ///
187    /// Setting this to something non-transparent instructs the printer to color
188    /// the complete page. If you are considering larger production runs, it may
189    /// be more environmentally friendly and cost-effective to source pre-dyed
190    /// pages and not set this property.
191    ///
192    /// When set to `{none}`, the background becomes transparent. Note that PDF
193    /// pages will still appear with a (usually white) background in viewers,
194    /// but they are actually transparent. (If you print them, no color is used
195    /// for the background.)
196    ///
197    /// The default of `{auto}` results in `{none}` for PDF output, and
198    /// `{white}` for PNG and SVG.
199    ///
200    /// ```example
201    /// #set page(fill: rgb("444352"))
202    /// #set text(fill: rgb("fdfdfd"))
203    /// *Dark mode enabled.*
204    /// ```
205    #[borrowed]
206    #[ghost]
207    pub fill: Smart<Option<Paint>>,
208
209    /// How to [number]($numbering) the pages.
210    ///
211    /// If an explicit `footer` (or `header` for top-aligned numbering) is
212    /// given, the numbering is ignored.
213    ///
214    /// ```example
215    /// #set page(
216    ///   height: 100pt,
217    ///   margin: (top: 16pt, bottom: 24pt),
218    ///   numbering: "1 / 1",
219    /// )
220    ///
221    /// #lorem(48)
222    /// ```
223    #[borrowed]
224    #[ghost]
225    pub numbering: Option<Numbering>,
226
227    /// A supplement for the pages.
228    ///
229    /// For page references, this is added before the page number.
230    ///
231    /// ```example
232    /// #set page(numbering: "1.", supplement: [p.])
233    ///
234    /// = Introduction <intro>
235    /// We are on #ref(<intro>, form: "page")!
236    /// ```
237    #[ghost]
238    pub supplement: Smart<Option<Content>>,
239
240    /// The alignment of the page numbering.
241    ///
242    /// If the vertical component is `top`, the numbering is placed into the
243    /// header and if it is `bottom`, it is placed in the footer. Horizon
244    /// alignment is forbidden. If an explicit matching `header` or `footer` is
245    /// given, the numbering is ignored.
246    ///
247    /// ```example
248    /// #set page(
249    ///   margin: (top: 16pt, bottom: 24pt),
250    ///   numbering: "1",
251    ///   number-align: right,
252    /// )
253    ///
254    /// #lorem(30)
255    /// ```
256    #[default(SpecificAlignment::Both(HAlignment::Center, OuterVAlignment::Bottom))]
257    #[ghost]
258    pub number_align: SpecificAlignment<HAlignment, OuterVAlignment>,
259
260    /// The page's header. Fills the top margin of each page.
261    ///
262    /// - Content: Shows the content as the header.
263    /// - `{auto}`: Shows the page number if a `numbering` is set and
264    ///   `number-align` is `top`.
265    /// - `{none}`: Suppresses the header.
266    ///
267    /// ```example
268    /// #set par(justify: true)
269    /// #set page(
270    ///   margin: (top: 32pt, bottom: 20pt),
271    ///   header: [
272    ///     #set text(8pt)
273    ///     #smallcaps[Typst Academy]
274    ///     #h(1fr) _Exercise Sheet 3_
275    ///   ],
276    /// )
277    ///
278    /// #lorem(19)
279    /// ```
280    #[borrowed]
281    #[ghost]
282    pub header: Smart<Option<Content>>,
283
284    /// The amount the header is raised into the top margin.
285    #[resolve]
286    #[default(Ratio::new(0.3).into())]
287    #[ghost]
288    pub header_ascent: Rel<Length>,
289
290    /// The page's footer. Fills the bottom margin of each page.
291    ///
292    /// - Content: Shows the content as the footer.
293    /// - `{auto}`: Shows the page number if a `numbering` is set and
294    ///   `number-align` is `bottom`.
295    /// - `{none}`: Suppresses the footer.
296    ///
297    /// For just a page number, the `numbering` property typically suffices. If
298    /// you want to create a custom footer but still display the page number,
299    /// you can directly access the [page counter]($counter).
300    ///
301    /// ```example
302    /// #set par(justify: true)
303    /// #set page(
304    ///   height: 100pt,
305    ///   margin: 20pt,
306    ///   footer: context [
307    ///     #set align(right)
308    ///     #set text(8pt)
309    ///     #counter(page).display(
310    ///       "1 of I",
311    ///       both: true,
312    ///     )
313    ///   ]
314    /// )
315    ///
316    /// #lorem(48)
317    /// ```
318    #[borrowed]
319    #[ghost]
320    pub footer: Smart<Option<Content>>,
321
322    /// The amount the footer is lowered into the bottom margin.
323    #[resolve]
324    #[default(Ratio::new(0.3).into())]
325    #[ghost]
326    pub footer_descent: Rel<Length>,
327
328    /// Content in the page's background.
329    ///
330    /// This content will be placed behind the page's body. It can be
331    /// used to place a background image or a watermark.
332    ///
333    /// ```example
334    /// #set page(background: rotate(24deg,
335    ///   text(18pt, fill: rgb("FFCBC4"))[
336    ///     *CONFIDENTIAL*
337    ///   ]
338    /// ))
339    ///
340    /// = Typst's secret plans
341    /// In the year 2023, we plan to take
342    /// over the world (of typesetting).
343    /// ```
344    #[borrowed]
345    #[ghost]
346    pub background: Option<Content>,
347
348    /// Content in the page's foreground.
349    ///
350    /// This content will overlay the page's body.
351    ///
352    /// ```example
353    /// #set page(foreground: text(24pt)[🥸])
354    ///
355    /// Reviewer 2 has marked our paper
356    /// "Weak Reject" because they did
357    /// not understand our approach...
358    /// ```
359    #[borrowed]
360    #[ghost]
361    pub foreground: Option<Content>,
362
363    /// The contents of the page(s).
364    ///
365    /// Multiple pages will be created if the content does not fit on a single
366    /// page. A new page with the page properties prior to the function invocation
367    /// will be created after the body has been typeset.
368    #[external]
369    #[required]
370    pub body: Content,
371}
372
373impl Construct for PageElem {
374    fn construct(engine: &mut Engine, args: &mut Args) -> SourceResult<Content> {
375        // The page constructor is special: It doesn't create a page element.
376        // Instead, it just ensures that the passed content lives in a separate
377        // page and styles it.
378        let styles = Self::set(engine, args)?;
379        let body = args.expect::<Content>("body")?;
380        Ok(Content::sequence([
381            PagebreakElem::shared_weak().clone(),
382            // We put an effectless, invisible non-tag element on the page.
383            // This has two desirable consequences:
384            // - The page is kept even if the body is empty
385            // - The page doesn't inherit shared styles from the body
386            FlushElem::new().pack(),
387            body,
388            PagebreakElem::shared_boundary().clone(),
389        ])
390        .styled_with_map(styles))
391    }
392}
393
394impl LocalName for PageElem {
395    const KEY: &'static str = "page";
396}
397
398/// A manual page break.
399///
400/// Must not be used inside any containers.
401///
402/// # Example
403/// ```example
404/// The next page contains
405/// more details on compound theory.
406/// #pagebreak()
407///
408/// == Compound Theory
409/// In 1984, the first ...
410/// ```
411#[elem(title = "Page Break")]
412pub struct PagebreakElem {
413    /// If `{true}`, the page break is skipped if the current page is already
414    /// empty.
415    #[default(false)]
416    pub weak: bool,
417
418    /// If given, ensures that the next page will be an even/odd page, with an
419    /// empty page in between if necessary.
420    ///
421    /// ```example
422    /// #set page(height: 30pt)
423    ///
424    /// First.
425    /// #pagebreak(to: "odd")
426    /// Third.
427    /// ```
428    pub to: Option<Parity>,
429
430    /// Whether this pagebreak designates an end boundary of a page run. This is
431    /// an even weaker version of pagebreak `weak` because it not only doesn't
432    /// force an empty page, but also doesn't force its initial styles onto a
433    /// staged empty page.
434    #[internal]
435    #[parse(None)]
436    #[default(false)]
437    pub boundary: bool,
438}
439
440impl PagebreakElem {
441    /// Get the globally shared weak pagebreak element.
442    pub fn shared_weak() -> &'static Content {
443        singleton!(Content, PagebreakElem::new().with_weak(true).pack())
444    }
445
446    /// Get the globally shared boundary pagebreak element.
447    pub fn shared_boundary() -> &'static Content {
448        singleton!(
449            Content,
450            PagebreakElem::new().with_weak(true).with_boundary(true).pack()
451        )
452    }
453}
454
455/// A finished document with metadata and page frames.
456#[derive(Debug, Default, Clone)]
457pub struct PagedDocument {
458    /// The document's finished pages.
459    pub pages: Vec<Page>,
460    /// Details about the document.
461    pub info: DocumentInfo,
462    /// Provides the ability to execute queries on the document.
463    pub introspector: Introspector,
464}
465
466/// A finished page.
467#[derive(Debug, Clone)]
468pub struct Page {
469    /// The frame that defines the page.
470    pub frame: Frame,
471    /// How the page is filled.
472    ///
473    /// - When `None`, the background is transparent.
474    /// - When `Auto`, the background is transparent for PDF and white
475    ///   for raster and SVG targets.
476    ///
477    /// Exporters should access the resolved value of this property through
478    /// `fill_or_transparent()` or `fill_or_white()`.
479    pub fill: Smart<Option<Paint>>,
480    /// The page's numbering.
481    pub numbering: Option<Numbering>,
482    /// The page's supplement.
483    pub supplement: Content,
484    /// The logical page number (controlled by `counter(page)` and may thus not
485    /// match the physical number).
486    pub number: usize,
487}
488
489impl Page {
490    /// Get the configured background or `None` if it is `Auto`.
491    ///
492    /// This is used in PDF export.
493    pub fn fill_or_transparent(&self) -> Option<Paint> {
494        self.fill.clone().unwrap_or(None)
495    }
496
497    /// Get the configured background or white if it is `Auto`.
498    ///
499    /// This is used in raster and SVG export.
500    pub fn fill_or_white(&self) -> Option<Paint> {
501        self.fill.clone().unwrap_or_else(|| Some(Color::WHITE.into()))
502    }
503}
504
505/// Specification of the page's margins.
506#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
507pub struct Margin {
508    /// The margins for each side.
509    pub sides: Sides<Option<Smart<Rel<Length>>>>,
510    /// Whether to swap `left` and `right` to make them `inside` and `outside`
511    /// (when to swap depends on the binding).
512    pub two_sided: Option<bool>,
513}
514
515impl Margin {
516    /// Create an instance with four equal components.
517    pub fn splat(value: Option<Smart<Rel<Length>>>) -> Self {
518        Self { sides: Sides::splat(value), two_sided: None }
519    }
520}
521
522impl Default for Margin {
523    fn default() -> Self {
524        Self {
525            sides: Sides::splat(Some(Smart::Auto)),
526            two_sided: None,
527        }
528    }
529}
530
531impl Fold for Margin {
532    fn fold(self, outer: Self) -> Self {
533        Margin {
534            sides: self.sides.fold(outer.sides),
535            two_sided: self.two_sided.fold(outer.two_sided),
536        }
537    }
538}
539
540cast! {
541    Margin,
542    self => {
543        let two_sided = self.two_sided.unwrap_or(false);
544        if !two_sided && self.sides.is_uniform() {
545            if let Some(left) = self.sides.left {
546                return left.into_value();
547            }
548        }
549
550        let mut dict = Dict::new();
551        let mut handle = |key: &str, component: Option<Smart<Rel<Length>>>| {
552            if let Some(c) = component {
553                dict.insert(key.into(), c.into_value());
554            }
555        };
556
557        handle("top", self.sides.top);
558        handle("bottom", self.sides.bottom);
559        if two_sided {
560            handle("inside", self.sides.left);
561            handle("outside", self.sides.right);
562        } else {
563            handle("left", self.sides.left);
564            handle("right", self.sides.right);
565        }
566
567        Value::Dict(dict)
568    },
569    _: AutoValue => Self::splat(Some(Smart::Auto)),
570    v: Rel<Length> => Self::splat(Some(Smart::Custom(v))),
571    mut dict: Dict => {
572        let mut take = |key| dict.take(key).ok().map(Value::cast).transpose();
573
574        let rest = take("rest")?;
575        let x = take("x")?.or(rest);
576        let y = take("y")?.or(rest);
577        let top = take("top")?.or(y);
578        let bottom = take("bottom")?.or(y);
579        let outside = take("outside")?;
580        let inside = take("inside")?;
581        let left = take("left")?;
582        let right = take("right")?;
583
584        let implicitly_two_sided = outside.is_some() || inside.is_some();
585        let implicitly_not_two_sided = left.is_some() || right.is_some();
586        if implicitly_two_sided && implicitly_not_two_sided {
587            bail!("`inside` and `outside` are mutually exclusive with `left` and `right`");
588        }
589
590        // - If 'implicitly_two_sided' is false here, then
591        //   'implicitly_not_two_sided' will be guaranteed to be true
592        //    due to the previous two 'if' conditions.
593        // - If both are false, this means that this margin change does not
594        //   affect lateral margins, and thus shouldn't make a difference on
595        //   the 'two_sided' attribute of this margin.
596        let two_sided = (implicitly_two_sided || implicitly_not_two_sided)
597            .then_some(implicitly_two_sided);
598
599        dict.finish(&[
600            "left", "top", "right", "bottom", "outside", "inside", "x", "y", "rest",
601        ])?;
602
603        Margin {
604            sides: Sides {
605                left: inside.or(left).or(x),
606                top,
607                right: outside.or(right).or(x),
608                bottom,
609            },
610            two_sided,
611        }
612    }
613}
614
615/// Specification of the page's binding.
616#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
617pub enum Binding {
618    /// Bound on the left, as customary in LTR languages.
619    Left,
620    /// Bound on the right, as customary in RTL languages.
621    Right,
622}
623
624impl Binding {
625    /// Whether to swap left and right margin for the page with this number.
626    pub fn swap(self, number: NonZeroUsize) -> bool {
627        match self {
628            // Left-bound must swap on even pages
629            // (because it is correct on the first page).
630            Self::Left => number.get() % 2 == 0,
631            // Right-bound must swap on odd pages
632            // (because it is wrong on the first page).
633            Self::Right => number.get() % 2 == 1,
634        }
635    }
636}
637
638cast! {
639    Binding,
640    self => match self {
641        Self::Left => Alignment::LEFT.into_value(),
642        Self::Right => Alignment::RIGHT.into_value(),
643    },
644    v: Alignment => match v {
645        Alignment::LEFT => Self::Left,
646        Alignment::RIGHT => Self::Right,
647        _ => bail!("must be `left` or `right`"),
648    },
649}
650
651/// A header, footer, foreground or background definition.
652#[derive(Debug, Clone, Hash)]
653pub enum Marginal {
654    /// Bare content.
655    Content(Content),
656    /// A closure mapping from a page number to content.
657    Func(Func),
658}
659
660impl Marginal {
661    /// Resolve the marginal based on the page number.
662    pub fn resolve(
663        &self,
664        engine: &mut Engine,
665        styles: StyleChain,
666        page: usize,
667    ) -> SourceResult<Cow<'_, Content>> {
668        Ok(match self {
669            Self::Content(content) => Cow::Borrowed(content),
670            Self::Func(func) => Cow::Owned(
671                func.call(engine, Context::new(None, Some(styles)).track(), [page])?
672                    .display(),
673            ),
674        })
675    }
676}
677
678cast! {
679    Marginal,
680    self => match self {
681        Self::Content(v) => v.into_value(),
682        Self::Func(v) => v.into_value(),
683    },
684    v: Content => Self::Content(v),
685    v: Func => Self::Func(v),
686}
687
688/// A list of page ranges to be exported.
689#[derive(Debug, Clone)]
690pub struct PageRanges(Vec<PageRange>);
691
692/// A range of pages to export.
693///
694/// The range is one-indexed. For example, `1..=3` indicates the first, second
695/// and third pages should be exported.
696pub type PageRange = RangeInclusive<Option<NonZeroUsize>>;
697
698impl PageRanges {
699    /// Create new page ranges.
700    pub fn new(ranges: Vec<PageRange>) -> Self {
701        Self(ranges)
702    }
703
704    /// Check if a page, given its number, should be included when exporting the
705    /// document while restricting the exported pages to these page ranges.
706    /// This is the one-indexed version of 'includes_page_index'.
707    pub fn includes_page(&self, page: NonZeroUsize) -> bool {
708        self.includes_page_index(page.get() - 1)
709    }
710
711    /// Check if a page, given its index, should be included when exporting the
712    /// document while restricting the exported pages to these page ranges.
713    /// This is the zero-indexed version of 'includes_page'.
714    pub fn includes_page_index(&self, page: usize) -> bool {
715        let page = NonZeroUsize::try_from(page + 1).unwrap();
716        self.0.iter().any(|range| match (range.start(), range.end()) {
717            (Some(start), Some(end)) => (start..=end).contains(&&page),
718            (Some(start), None) => (start..).contains(&&page),
719            (None, Some(end)) => (..=end).contains(&&page),
720            (None, None) => true,
721        })
722    }
723}
724
725/// Whether something should be even or odd.
726#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
727pub enum Parity {
728    /// Next page will be an even page.
729    Even,
730    /// Next page will be an odd page.
731    Odd,
732}
733
734impl Parity {
735    /// Whether the given number matches the parity.
736    pub fn matches(self, number: usize) -> bool {
737        match self {
738            Self::Even => number % 2 == 0,
739            Self::Odd => number % 2 == 1,
740        }
741    }
742}
743
744/// Specification of a paper.
745#[derive(Debug, Copy, Clone, Hash)]
746pub struct Paper {
747    /// The name of the paper.
748    name: &'static str,
749    /// The width of the paper in millimeters.
750    width: Scalar,
751    /// The height of the paper in millimeters.
752    height: Scalar,
753}
754
755impl Paper {
756    /// The width of the paper.
757    pub fn width(self) -> Abs {
758        Abs::mm(self.width.get())
759    }
760
761    /// The height of the paper.
762    pub fn height(self) -> Abs {
763        Abs::mm(self.height.get())
764    }
765}
766
767/// Defines paper constants and a paper parsing implementation.
768macro_rules! papers {
769    ($(($var:ident: $width:expr, $height: expr, $name:literal))*) => {
770        /// Predefined papers.
771        ///
772        /// Each paper is parsable from its name in kebab-case.
773        impl Paper {
774            $(pub const $var: Self = Self {
775                name: $name,
776                width: Scalar::new($width),
777                height: Scalar::new($height),
778            };)*
779        }
780
781        impl FromStr for Paper {
782            type Err = &'static str;
783
784            fn from_str(name: &str) -> Result<Self, Self::Err> {
785                match name.to_lowercase().as_str() {
786                    $($name => Ok(Self::$var),)*
787                    _ => Err("unknown paper size"),
788                }
789            }
790        }
791
792        cast! {
793            Paper,
794            self => self.name.into_value(),
795            $(
796                /// Produces a paper of the respective size.
797                $name => Self::$var,
798            )*
799        }
800    };
801}
802
803// All paper sizes in mm.
804//
805// Resources:
806// - https://papersizes.io/
807// - https://en.wikipedia.org/wiki/Paper_size
808// - https://www.theedkins.co.uk/jo/units/oldunits/print.htm
809// - https://vintagepaper.co/blogs/news/traditional-paper-sizes
810papers! {
811    // ---------------------------------------------------------------------- //
812    // ISO 216 A Series
813    (A0:  841.0, 1189.0, "a0")
814    (A1:  594.0,  841.0, "a1")
815    (A2:  420.0,  594.0, "a2")
816    (A3:  297.0,  420.0, "a3")
817    (A4:  210.0,  297.0, "a4")
818    (A5:  148.0,  210.0, "a5")
819    (A6:  105.0,  148.0, "a6")
820    (A7:   74.0,  105.0, "a7")
821    (A8:   52.0,   74.0, "a8")
822    (A9:   37.0,   52.0, "a9")
823    (A10:  26.0,   37.0, "a10")
824    (A11:  18.0,   26.0, "a11")
825
826    // ISO 216 B Series
827    (ISO_B1: 707.0, 1000.0, "iso-b1")
828    (ISO_B2: 500.0,  707.0,  "iso-b2")
829    (ISO_B3: 353.0,  500.0,  "iso-b3")
830    (ISO_B4: 250.0,  353.0,  "iso-b4")
831    (ISO_B5: 176.0,  250.0,  "iso-b5")
832    (ISO_B6: 125.0,  176.0,  "iso-b6")
833    (ISO_B7:  88.0,  125.0,  "iso-b7")
834    (ISO_B8:  62.0,   88.0,  "iso-b8")
835
836    // ISO 216 C Series
837    (ISO_C3: 324.0, 458.0, "iso-c3")
838    (ISO_C4: 229.0, 324.0, "iso-c4")
839    (ISO_C5: 162.0, 229.0, "iso-c5")
840    (ISO_C6: 114.0, 162.0, "iso-c6")
841    (ISO_C7:  81.0, 114.0, "iso-c7")
842    (ISO_C8:  57.0,  81.0, "iso-c8")
843
844    // DIN D Series (extension to ISO)
845    (DIN_D3: 272.0, 385.0, "din-d3")
846    (DIN_D4: 192.0, 272.0, "din-d4")
847    (DIN_D5: 136.0, 192.0, "din-d5")
848    (DIN_D6:  96.0, 136.0, "din-d6")
849    (DIN_D7:  68.0,  96.0, "din-d7")
850    (DIN_D8:  48.0,  68.0, "din-d8")
851
852    // SIS (used in academia)
853    (SIS_G5: 169.0, 239.0, "sis-g5")
854    (SIS_E5: 115.0, 220.0, "sis-e5")
855
856    // ANSI Extensions
857    (ANSI_A: 216.0,  279.0, "ansi-a")
858    (ANSI_B: 279.0,  432.0, "ansi-b")
859    (ANSI_C: 432.0,  559.0, "ansi-c")
860    (ANSI_D: 559.0,  864.0, "ansi-d")
861    (ANSI_E: 864.0, 1118.0, "ansi-e")
862
863    // ANSI Architectural Paper
864    (ARCH_A:  229.0,  305.0, "arch-a")
865    (ARCH_B:  305.0,  457.0, "arch-b")
866    (ARCH_C:  457.0,  610.0, "arch-c")
867    (ARCH_D:  610.0,  914.0, "arch-d")
868    (ARCH_E1: 762.0, 1067.0, "arch-e1")
869    (ARCH_E:  914.0, 1219.0, "arch-e")
870
871    // JIS B Series
872    (JIS_B0:  1030.0, 1456.0, "jis-b0")
873    (JIS_B1:   728.0, 1030.0, "jis-b1")
874    (JIS_B2:   515.0,  728.0, "jis-b2")
875    (JIS_B3:   364.0,  515.0, "jis-b3")
876    (JIS_B4:   257.0,  364.0, "jis-b4")
877    (JIS_B5:   182.0,  257.0, "jis-b5")
878    (JIS_B6:   128.0,  182.0, "jis-b6")
879    (JIS_B7:    91.0,  128.0, "jis-b7")
880    (JIS_B8:    64.0,   91.0, "jis-b8")
881    (JIS_B9:    45.0,   64.0, "jis-b9")
882    (JIS_B10:   32.0,   45.0, "jis-b10")
883    (JIS_B11:   22.0,   32.0, "jis-b11")
884
885    // SAC D Series
886    (SAC_D0: 764.0, 1064.0, "sac-d0")
887    (SAC_D1: 532.0,  760.0, "sac-d1")
888    (SAC_D2: 380.0,  528.0, "sac-d2")
889    (SAC_D3: 264.0,  376.0, "sac-d3")
890    (SAC_D4: 188.0,  260.0, "sac-d4")
891    (SAC_D5: 130.0,  184.0, "sac-d5")
892    (SAC_D6:  92.0,  126.0, "sac-d6")
893
894    // ISO 7810 ID
895    (ISO_ID_1: 85.6, 53.98, "iso-id-1")
896    (ISO_ID_2: 74.0, 105.0, "iso-id-2")
897    (ISO_ID_3: 88.0, 125.0, "iso-id-3")
898
899    // ---------------------------------------------------------------------- //
900    // Asia
901    (ASIA_F4: 210.0, 330.0, "asia-f4")
902
903    // Japan
904    (JP_SHIROKU_BAN_4: 264.0, 379.0, "jp-shiroku-ban-4")
905    (JP_SHIROKU_BAN_5: 189.0, 262.0, "jp-shiroku-ban-5")
906    (JP_SHIROKU_BAN_6: 127.0, 188.0, "jp-shiroku-ban-6")
907    (JP_KIKU_4:        227.0, 306.0, "jp-kiku-4")
908    (JP_KIKU_5:        151.0, 227.0, "jp-kiku-5")
909    (JP_BUSINESS_CARD:  91.0,  55.0, "jp-business-card")
910
911    // China
912    (CN_BUSINESS_CARD: 90.0, 54.0, "cn-business-card")
913
914    // Europe
915    (EU_BUSINESS_CARD: 85.0, 55.0, "eu-business-card")
916
917    // French Traditional (AFNOR)
918    (FR_TELLIERE:          340.0, 440.0, "fr-tellière")
919    (FR_COURONNE_ECRITURE: 360.0, 460.0, "fr-couronne-écriture")
920    (FR_COURONNE_EDITION:  370.0, 470.0, "fr-couronne-édition")
921    (FR_RAISIN:            500.0, 650.0, "fr-raisin")
922    (FR_CARRE:             450.0, 560.0, "fr-carré")
923    (FR_JESUS:             560.0, 760.0, "fr-jésus")
924
925    // United Kingdom Imperial
926    (UK_BRIEF:    406.4, 342.9, "uk-brief")
927    (UK_DRAFT:    254.0, 406.4, "uk-draft")
928    (UK_FOOLSCAP: 203.2, 330.2, "uk-foolscap")
929    (UK_QUARTO:   203.2, 254.0, "uk-quarto")
930    (UK_CROWN:    508.0, 381.0, "uk-crown")
931    (UK_BOOK_A:   111.0, 178.0, "uk-book-a")
932    (UK_BOOK_B:   129.0, 198.0, "uk-book-b")
933
934    // Unites States
935    (US_LETTER:         215.9,  279.4, "us-letter")
936    (US_LEGAL:          215.9,  355.6, "us-legal")
937    (US_TABLOID:        279.4,  431.8, "us-tabloid")
938    (US_EXECUTIVE:      84.15,  266.7, "us-executive")
939    (US_FOOLSCAP_FOLIO: 215.9,  342.9, "us-foolscap-folio")
940    (US_STATEMENT:      139.7,  215.9, "us-statement")
941    (US_LEDGER:         431.8,  279.4, "us-ledger")
942    (US_OFICIO:         215.9, 340.36, "us-oficio")
943    (US_GOV_LETTER:     203.2,  266.7, "us-gov-letter")
944    (US_GOV_LEGAL:      215.9,  330.2, "us-gov-legal")
945    (US_BUSINESS_CARD:   88.9,   50.8, "us-business-card")
946    (US_DIGEST:         139.7,  215.9, "us-digest")
947    (US_TRADE:          152.4,  228.6, "us-trade")
948
949    // ---------------------------------------------------------------------- //
950    // Other
951    (NEWSPAPER_COMPACT:    280.0,    430.0, "newspaper-compact")
952    (NEWSPAPER_BERLINER:   315.0,    470.0, "newspaper-berliner")
953    (NEWSPAPER_BROADSHEET: 381.0,    578.0, "newspaper-broadsheet")
954    (PRESENTATION_16_9:    297.0, 167.0625, "presentation-16-9")
955    (PRESENTATION_4_3:     280.0,    210.0, "presentation-4-3")
956}
957
958#[cfg(test)]
959mod tests {
960    use super::*;
961
962    #[test]
963    fn test_paged_document_is_send_and_sync() {
964        fn ensure_send_and_sync<T: Send + Sync>() {}
965        ensure_send_and_sync::<PagedDocument>();
966    }
967}