1use crate::*;
2
3pub struct Writer {
5 pub b: BasicPdfWriter,
7 pub p: Page,
9 pub fonts: FontFamily,
11 pub cur_font: usize,
13 pub font_size: Px,
15 pub sup: Px,
17 pub mode: Mode,
19 pub title: String,
21 pub pages: Vec<Page>,
23 pub new_page: bool,
25 pub line_pad: Px,
27 pub margin_left: Px,
29 pub margin_right: Px,
31 pub margin_top: Px,
33 pub margin_bottom: Px,
35 pub page_width: Px,
37 pub page_height: Px,
39 pub line_used: MPx,
41 pub line: Vec<Item>,
43 pub max_font_size: Px,
45 pub center: bool,
47 pub fetcher: Option<Box<dyn Fetcher>>,
49}
50
51impl Default for Writer {
52 fn default() -> Self {
53 Self {
54 mode: Mode::Normal,
55 title: String::new(),
56 b: BasicPdfWriter::default(),
57 fonts: helvetica(),
58 cur_font: 0,
59 font_size: 10,
60 sup: 0,
61 p: Page::default(),
62 pages: Vec::new(),
63 new_page: true,
64
65 page_width: 600,
66 page_height: 800,
67 line_pad: 4,
68 margin_left: 20,
69 margin_right: 20,
70 margin_top: 20,
71 margin_bottom: 20,
72 line_used: 0,
73 line: Vec::new(),
74 max_font_size: 0,
75 center: false,
76 fetcher: None,
77 }
78 }
79}
80
81impl Writer {
82 fn init_page(&mut self) {
83 self.p.width = self.page_width;
84 self.p.height = self.page_height;
85 self.p.goto(
86 self.margin_left,
87 self.p.height - self.font_size - self.margin_top,
88 );
89 if self.sup != 0 {
90 self.p.set_sup(self.sup);
91 }
92 self.new_page = false;
93 }
94
95 pub fn save_page(&mut self) {
97 let p = std::mem::take(&mut self.p);
98 self.pages.push(p);
99 self.new_page = true;
100 }
101
102 fn init_font(&mut self, x: usize) {
103 let f = &mut self.fonts[x];
104 f.init(&mut self.b);
105 }
106
107 fn width(&self, c: char) -> MPx {
108 let f = &self.fonts[self.cur_font];
109 f.width(c) * self.font_size as MPx
110 }
111
112 fn line_len(&self) -> MPx {
113 ((self.page_width - self.margin_left - self.margin_right) as MPx) * 1000
114 }
115
116 fn wrap_init(&mut self) {
117 if self.new_page {
118 self.init_page();
119 }
120 }
121
122 fn wrap_text(&mut self, s: &str) {
123 self.wrap_init();
124
125 let mut width: MPx = 0;
126 for c in s.chars() {
127 width += self.width(c); }
129
130 if self.line_used + width > self.line_len() {
131 self.output_line();
132 if s == " " {
133 return;
134 }
135 }
136 self.line_used += width;
137
138 self.init_font(self.cur_font);
139 if self.font_size > self.max_font_size {
140 self.max_font_size = self.font_size;
141 }
142
143 self.line.push(Item::Text(
144 s.to_string(),
145 self.cur_font,
146 self.font_size,
147 width,
148 ));
149 }
150
151 fn wrap_image(&mut self, im: Image, width: Px, scale: f32) {
152 self.wrap_init();
153
154 let width = (width as MPx) * 1000; if self.line_used + width > self.line_len() {
157 self.output_line();
158 }
159
160 self.line_used += width;
161 self.line.push(Item::Img(im, width, scale));
162 }
163
164 pub fn output_line(&mut self) {
166 if self.new_page {
167 self.init_page();
168 } else {
169 let cx = if self.center {
170 ((self.line_len() - self.line_used) / 2000) as Px
171 } else {
172 0
173 };
174 let h = self.max_font_size + self.line_pad;
175 if self.p.y >= h + self.margin_bottom {
176 self.p.td(self.margin_left + cx - self.p.x, -h);
177 } else {
178 self.save_page();
179 self.init_page();
180 }
181 }
182 let mut cx: MPx = 0;
183 for item in &self.line {
184 match item {
185 Item::Text(s, f, x, w) => {
186 let fp = &*self.fonts[*f];
187 self.p.text(fp, *x, s);
188 cx += w;
189 }
190 Item::Sup(x) => {
191 self.p.set_sup(*x);
192 }
193 Item::Img(im, width, scale) => {
194 self.p.flush_text();
195 let x: f32 = (self.p.x as f32) + (cx as f32 / 1000.0);
196 let y = self.p.y as f32;
197 im.draw(&mut self.p, x, y, *scale);
198 cx += width;
199 self.p.space(*width);
200 }
201 }
202 }
203 self.line.clear();
204 self.line_used = 0;
205 self.max_font_size = 0;
206 }
207
208 pub fn text(&mut self, s: &str) {
210 match self.mode {
211 Mode::Normal => {
212 self.wrap_text(s);
213 }
214 Mode::Title => {
215 self.title += s;
216 }
217 Mode::Head => {}
218 }
219 }
220
221 pub fn image(&mut self, src: &str, awidth: Option<Px>, aheight: Option<Px>) {
223 let mut bf = std::mem::take(&mut self.fetcher);
224 if let Some(f) = &mut bf {
225 let im = f.image(self, src);
226 let mut width: Px = im.width;
227 let mut scale: f32 = 1.0;
228 if let Some(awidth) = awidth {
229 scale = awidth as f32 / width as f32;
230 width = awidth;
231 } else if let Some(aheight) = aheight {
232 scale = aheight as f32 / im.height as f32;
233 width = (width as f32 * scale) as Px;
234 }
235 self.wrap_image(im, width, scale);
236 } else {
237 self.text("error : no fetcher in pdf-min::Writer");
238 }
239 self.fetcher = bf;
240 }
241
242 pub fn space(&mut self) {
244 self.text(" ");
245 }
246
247 pub fn set_sup(&mut self, sup: Px) {
249 self.line.push(Item::Sup(sup));
250 self.sup = sup;
251 }
252
253 pub fn finish(&mut self) -> &[u8] {
255 self.output_line();
256 self.init_font(0);
257 self.save_page();
258 let n = self.pages.len();
259 let mut pnum = 1;
260 let font_size = 8;
261 #[allow(clippy::explicit_counter_loop)]
262 for p in &mut self.pages {
263 p.goto(self.margin_left, self.line_pad);
264 p.text(
265 &*self.fonts[0],
266 font_size,
267 &format!("Page {} of {}", pnum, n),
268 );
269 p.finish();
270 pnum += 1;
271 }
272 self.b.finish(&self.pages, self.title.as_bytes());
273 &self.b.b
274 }
275}
276
277#[derive(Clone, Copy)]
279pub enum Mode {
280 Normal,
282 Head,
284 Title,
286}
287
288pub enum Item {
290 Text(String, usize, Px, MPx),
292 Sup(Px),
294 Img(Image, MPx, f32),
296}
297
298pub trait Fetcher {
300 fn image(&mut self, _w: &mut Writer, _name: &str) -> Image {
302 todo!()
303 }
304 fn font(&mut self, _w: &mut Writer, _name: &str) -> Box<dyn Font> {
306 todo!()
307 }
308}