summaryrefslogtreecommitdiff
path: root/src/style.rs
blob: 2c74adde4b66fe5bfaae96579495785ab12b5541 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! Styles for text and pages.

use toddle::fallback;
use toddle::query::{FallbackTree, FontVariant, FontStyle, FontWeight};
use crate::length::{Length, Size, Margins, Value4, ScaleLength};
use crate::paper::{Paper, PaperClass, PAPER_A4};

/// Defines properties of pages and text.
#[derive(Debug, Default, Clone, PartialEq)]
pub struct LayoutStyle {
    /// The style for text.
    pub text: TextStyle,
    /// The style for pages.
    pub page: PageStyle,
}

/// Defines which fonts to use and how to space text.
#[derive(Debug, Clone, PartialEq)]
pub struct TextStyle {
    /// A tree of font names and generic family names.
    pub fallback: FallbackTree,
    /// The selected font variant.
    pub variant: FontVariant,
    /// Whether the bolder toggle is active or inactive. This determines
    /// whether the next `*` adds or removes font weight.
    pub bolder: bool,
    /// The base font size.
    pub base_font_size: Length,
    /// The font scale to apply on the base font size.
    pub font_scale: f64,
    /// The word spacing (as a multiple of the font size).
    pub word_spacing_scale: f64,
    /// The line spacing (as a multiple of the font size).
    pub line_spacing_scale: f64,
    /// The paragraphs spacing (as a multiple of the font size).
    pub paragraph_spacing_scale: f64,
}

impl TextStyle {
    /// The scaled font size.
    pub fn font_size(&self) -> Length {
        self.base_font_size * self.font_scale
    }

    /// The absolute word spacing.
    pub fn word_spacing(&self) -> Length {
        self.word_spacing_scale * self.font_size()
    }

    /// The absolute line spacing.
    pub fn line_spacing(&self) -> Length {
        (self.line_spacing_scale - 1.0) * self.font_size()
    }

    /// The absolute paragraph spacing.
    pub fn paragraph_spacing(&self) -> Length {
        (self.paragraph_spacing_scale - 1.0) * self.font_size()
    }
}

impl Default for TextStyle {
    fn default() -> TextStyle {
        TextStyle {
            fallback: fallback! {
                list: ["sans-serif"],
                classes: {
                    "serif" => ["source serif pro", "noto serif"],
                    "sans-serif" => ["source sans pro", "noto sans"],
                    "monospace" => ["source code pro", "noto sans mono"],
                    "math" => ["latin modern math", "serif"],
                },
                base: ["source sans pro", "noto sans",
                       "noto emoji", "latin modern math"],
            },
            variant: FontVariant {
                style: FontStyle::Normal,
                weight: FontWeight(400),
            },
            bolder: false,
            base_font_size: Length::pt(11.0),
            font_scale: 1.0,
            word_spacing_scale: 0.25,
            line_spacing_scale: 1.2,
            paragraph_spacing_scale: 1.5,
        }
    }
}

/// Defines the size and margins of a page.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct PageStyle {
    /// The class of this page.
    pub class: PaperClass,
    /// The width and height of the page.
    pub dimensions: Size,
    /// The amount of white space on each side. If a side is set to `None`, the
    /// default for the paper class is used.
    pub margins: Value4<Option<ScaleLength>>,
}

impl PageStyle {
    /// The default page style for the given paper.
    pub fn new(paper: Paper) -> PageStyle {
        PageStyle {
            class: paper.class,
            dimensions: paper.size(),
            margins: Value4::with_all(None),
        }
    }

    /// The absolute margins.
    pub fn margins(&self) -> Margins {
        let dims = self.dimensions;
        let default = self.class.default_margins();

        Margins {
            left: self.margins.left.unwrap_or(default.left).scaled(dims.x),
            top: self.margins.top.unwrap_or(default.top).scaled(dims.y),
            right: self.margins.right.unwrap_or(default.right).scaled(dims.x),
            bottom: self.margins.bottom.unwrap_or(default.bottom).scaled(dims.y),
        }
    }
}

impl Default for PageStyle {
    fn default() -> PageStyle {
        PageStyle::new(PAPER_A4)
    }
}