1use crate::*;
2use pstd::collections::BTreeMap;
3
4pub struct Writer {
6 pub b: BasicPdfWriter,
8 pub p: Page,
10 pub fonts: FontFamily,
12 pub cur_font: usize,
14 pub font_size: Px,
16 pub sup: Px,
18 pub mode: Mode,
20 pub title: String,
22 pub pages: Vec<Page>,
24 pub new_page: bool,
26 pub line_pad: Px,
28 pub margin_left: Px,
30 pub margin_right: Px,
32 pub margin_top: Px,
34 pub margin_bottom: Px,
36 pub page_width: Px,
38 pub page_height: Px,
40 pub line_used: MPx,
42 pub line: Vec<Item>,
44 pub max_font_size: Px,
46 pub center: bool,
48 pub fetcher: Option<Box<dyn Fetcher>>,
50 pub image_cache: BTreeMap<String,Image>,
52}
53
54impl Default for Writer {
55 fn default() -> Self {
56 Self {
57 mode: Mode::Normal,
58 title: String::new(),
59 b: BasicPdfWriter::default(),
60 fonts: helvetica(),
61 cur_font: 0,
62 font_size: 10,
63 sup: 0,
64 p: Page::default(),
65 pages: Vec::new(),
66 new_page: true,
67
68 page_width: 600,
69 page_height: 800,
70 line_pad: 4,
71 margin_left: 20,
72 margin_right: 20,
73 margin_top: 20,
74 margin_bottom: 20,
75 line_used: 0,
76 line: Vec::new(),
77 max_font_size: 0,
78 center: false,
79 fetcher: None,
80 image_cache: BTreeMap::new(),
81 }
82 }
83}
84
85impl Writer {
86 fn init_page(&mut self) {
87 self.p.width = self.page_width;
88 self.p.height = self.page_height;
89 self.p.goto(
90 self.margin_left,
91 self.p.height - self.font_size - self.margin_top,
92 );
93 if self.sup != 0 {
94 self.p.set_sup(self.sup);
95 }
96 self.new_page = false;
97 }
98
99 pub fn save_page(&mut self) {
101 let p = std::mem::take(&mut self.p);
102 self.pages.push(p);
103 self.new_page = true;
104 }
105
106 fn init_font(&mut self, x: usize) {
107 let f = &mut self.fonts[x];
108 f.init(&mut self.b);
109 }
110
111 fn width(&self, c: char) -> MPx {
112 let f = &self.fonts[self.cur_font];
113 f.width(c) * self.font_size as MPx
114 }
115
116 fn line_len(&self) -> MPx {
117 ((self.page_width - self.margin_left - self.margin_right) as MPx) * 1000
118 }
119
120 fn wrap_init(&mut self) {
121 if self.new_page {
122 self.init_page();
123 }
124 }
125
126 fn wrap_text(&mut self, s: &str) {
127 self.wrap_init();
128
129 let mut width: MPx = 0;
130 for c in s.chars() {
131 width += self.width(c); }
133
134 if self.line_used + width > self.line_len() {
135 self.output_line();
136 if s == " " {
137 return;
138 }
139 }
140 self.line_used += width;
141
142 self.init_font(self.cur_font);
143 if self.font_size > self.max_font_size {
144 self.max_font_size = self.font_size;
145 }
146
147 self.line.push(Item::Text(
148 s.to_string(),
149 self.cur_font,
150 self.font_size,
151 width,
152 ));
153 }
154
155 fn wrap_image(&mut self, im: Image, width: Px, scale: f32) {
156 self.wrap_init();
157
158 let width = (width as MPx) * 1000; if self.line_used + width > self.line_len() {
161 self.output_line();
162 }
163
164 self.line_used += width;
165 self.line.push(Item::Img(im, width, scale));
166 }
167
168 pub fn output_line(&mut self) {
170 if self.new_page {
171 self.init_page();
172 } else {
173 let cx = if self.center {
174 ((self.line_len() - self.line_used) / 2000) as Px
175 } else {
176 0
177 };
178 let h = self.max_font_size + self.line_pad;
179 if self.p.y >= h + self.margin_bottom {
180 self.p.td(self.margin_left + cx - self.p.x, -h);
181 } else {
182 self.save_page();
183 self.init_page();
184 }
185 }
186 let mut cx: MPx = 0;
187 for item in &self.line {
188 match item {
189 Item::Text(s, f, x, w) => {
190 let fp = &*self.fonts[*f];
191 self.p.text(fp, *x, s);
192 cx += w;
193 }
194 Item::Sup(x) => {
195 self.p.set_sup(*x);
196 }
197 Item::Img(im, width, scale) => {
198 self.p.flush_text();
199 let x: f32 = (self.p.x as f32) + (cx as f32 / 1000.0);
200 let y = self.p.y as f32;
201 im.draw(&mut self.p, x, y, *scale);
202 cx += width;
203 self.p.space(*width);
204 }
205 }
206 }
207 self.line.clear();
208 self.line_used = 0;
209 self.max_font_size = 0;
210 }
211
212 pub fn text(&mut self, s: &str) {
214 match self.mode {
215 Mode::Normal => {
216 self.wrap_text(s);
217 }
218 Mode::Title => {
219 self.title += s;
220 }
221 Mode::Head => {}
222 }
223 }
224
225 fn fetch_image(&mut self, src: &str) -> Option<Image>
226 {
227 let mut result = None;
228 if let Some(im) = self.image_cache.get(src)
229 {
230 result = Some(im.clone());
231 }
232 else
233 {
234 let mut bf = std::mem::take(&mut self.fetcher);
235 if let Some(f) = &mut bf {
236 let im = f.image(self, src);
237 self.image_cache.insert( src.to_owned(), im.clone() );
238 result = Some(im);
239 }
240 self.fetcher = bf;
241 }
242 result
243 }
244
245 pub fn image(&mut self, src: &str, awidth: Option<Px>, aheight: Option<Px>) {
247 if let Some(im) = self.fetch_image( src ) {
248 let mut width: Px = im.width;
249 let mut scale: f32 = 1.0;
250 if let Some(awidth) = awidth {
251 scale = awidth as f32 / width as f32;
252 width = awidth;
253 } else if let Some(aheight) = aheight {
254 scale = aheight as f32 / im.height as f32;
255 width = (width as f32 * scale) as Px;
256 }
257 self.wrap_image(im, width, scale);
258 } else {
259 self.text("error : no fetcher in pdf-min::Writer");
260 }
261 }
262
263 pub fn space(&mut self) {
265 self.text(" ");
266 }
267
268 pub fn set_sup(&mut self, sup: Px) {
270 self.line.push(Item::Sup(sup));
271 self.sup = sup;
272 }
273
274 pub fn finish(&mut self) -> &[u8] {
276 self.output_line();
277 self.init_font(0);
278 self.save_page();
279 let n = self.pages.len();
280 let mut pnum = 1;
281 let font_size = 8;
282 #[allow(clippy::explicit_counter_loop)]
283 for p in &mut self.pages {
284 p.goto(self.margin_left, self.line_pad);
285 p.text(
286 &*self.fonts[0],
287 font_size,
288 &format!("Page {} of {}", pnum, n),
289 );
290 p.finish();
291 pnum += 1;
292 }
293 self.b.finish(&self.pages, self.title.as_bytes());
294 &self.b.b
295 }
296}
297
298#[derive(Clone, Copy)]
300pub enum Mode {
301 Normal,
303 Head,
305 Title,
307}
308
309pub enum Item {
311 Text(String, usize, Px, MPx),
313 Sup(Px),
315 Img(Image, MPx, f32),
317}
318
319pub trait Fetcher {
321 fn image(&mut self, _w: &mut Writer, _name: &str) -> Image {
323 todo!()
324 }
325 fn font(&mut self, _w: &mut Writer, _name: &str) -> Box<dyn Font> {
327 todo!()
328 }
329}