pdf2image_alt/
render_options.rs

1/// Options for rendering PDFs
2#[derive(derive_builder::Builder)]
3pub struct RenderOptions {
4    #[builder(default = "DPI::Uniform(150)")]
5    /// Resolution in dots per inch
6    pub resolution: DPI,
7    #[builder(setter(into, strip_option), default)]
8    /// Scale pages to a certain number of pixels
9    pub scale: Option<Scale>,
10    #[builder(default)]
11    /// Render pages in grayscale
12    pub greyscale: bool,
13    #[builder(setter(into, strip_option), default)]
14    /// Crop a specific section of the page
15    pub crop: Option<Crop>,
16    #[builder(setter(into, strip_option), default)]
17    /// Password to unlock encrypted PDFs
18    pub password: Option<Password>,
19    /// Use pdftocairo instead of pdftoppm
20    #[builder(default)]
21    pub pdftocairo: bool,
22}
23
24impl Default for RenderOptions {
25    fn default() -> Self {
26        Self {
27            resolution: DPI::Uniform(150),
28            scale: None,
29            greyscale: false,
30            crop: None,
31            password: None,
32            pdftocairo: false,
33        }
34    }
35}
36
37impl RenderOptions {
38    pub fn to_cli_args(&self) -> Vec<String> {
39        let mut args = vec![];
40
41        match self.resolution {
42            DPI::Uniform(dpi) => {
43                args.push("-r".to_string());
44                args.push(dpi.to_string());
45            }
46            DPI::XY(dpi_x, dpi_y) => {
47                args.push("-rx".to_string());
48                args.push(dpi_x.to_string());
49                args.push("-ry".to_string());
50                args.push(dpi_y.to_string());
51            }
52        }
53
54        if let Some(scale) = &self.scale {
55            match scale {
56                Scale::Uniform(scale) => {
57                    args.push("-scale-to".to_string());
58                    args.push(scale.to_string());
59                }
60                Scale::X(scale_x) => {
61                    args.push("-scale-to-x".to_string());
62                    args.push(scale_x.to_string());
63                }
64                Scale::Y(scale_y) => {
65                    args.push("-scale-to-y".to_string());
66                    args.push(scale_y.to_string());
67                }
68                Scale::XY(scale_x, scale_y) => {
69                    args.push("-scale-to-x".to_string());
70                    args.push(scale_x.to_string());
71                    args.push("-scale-to-y".to_string());
72                    args.push(scale_y.to_string());
73                }
74            }
75        }
76
77        if self.greyscale {
78            args.push("-gray".to_string());
79        }
80
81        if let Some(crop) = &self.crop {
82            args.push("-cropbox".to_string());
83            let (x, y) = (crop.inner.x, crop.inner.y);
84            let (width, height) = (crop.inner.width, crop.inner.height);
85            args.push("-x".to_string());
86            args.push(x.to_string());
87            args.push("-y".to_string());
88            args.push(y.to_string());
89            args.push("-W".to_string());
90            args.push(width.to_string());
91            args.push("-H".to_string());
92            args.push(height.to_string());
93        }
94
95        if let Some(password) = &self.password {
96            match password {
97                Password::User(password) => {
98                    args.push("-upw".to_string());
99                    args.push(password.clone());
100                }
101                Password::Owner(password) => {
102                    args.push("-opw".to_string());
103                    args.push(password.clone());
104                }
105            }
106        }
107
108        args
109    }
110}
111
112/// Password to unlock encrypted PDFs
113#[derive(Debug, Clone)]
114pub enum Password {
115    User(String),
116    Owner(String),
117}
118
119/// Specifies resolution in terms of dots per inch
120#[derive(Debug, Clone)]
121pub enum DPI {
122    /// DPI for both axes
123    Uniform(u32),
124    /// DPI for x and y axis
125    XY(u32, u32),
126}
127
128/// Scales pages to a certain number of pixels
129#[derive(Debug, Clone)]
130pub enum Scale {
131    /// scales each page to fit within scale-to*scale-to pixel box
132    Uniform(u32),
133    /// scales each page horizontally to fit in scale-to-x pixels
134    X(u32),
135    /// scales each page vertically to fit in scale-to-y pixels
136    Y(u32),
137    ///  scales each page to fit within scale-to-x*scale-to-y pixel box
138    XY(u32, u32),
139}
140
141/// Crop a specific section of the page
142#[derive(Debug, Clone)]
143pub struct Crop {
144    inner: image::math::Rect,
145}
146
147impl Crop {
148    pub fn new(x1: u32, y1: u32, x2: u32, y2: u32) -> Self {
149        let (min_x, max_x) = match x1 < x2 {
150            true => (x1, x2),
151            false => (x2, x1),
152        };
153
154        let (min_y, max_y) = match y1 < y2 {
155            true => (y1, y2),
156            false => (y2, y1),
157        };
158
159        Self {
160            inner: image::math::Rect {
161                x: min_x,
162                y: min_y,
163                width: max_x - min_x,
164                height: max_y - min_y,
165            },
166        }
167    }
168
169    pub fn from_top_left(width: u32, height: u32, top_left: (u32, u32)) -> Self {
170        Self {
171            inner: image::math::Rect {
172                x: top_left.0,
173                y: top_left.1,
174                width,
175                height,
176            },
177        }
178    }
179
180    pub fn square(size: u32, top_left: (u32, u32)) -> Self {
181        Self {
182            inner: image::math::Rect {
183                x: top_left.0,
184                y: top_left.1,
185                width: size,
186                height: size,
187            },
188        }
189    }
190}