1use crate::*;
2
3pub struct Writer {
5 pub b: BasicPdfWriter,
7 pub p: Page,
9 pub fonts: Vec<Box<dyn Font>>,
11 pub cur_font: usize, pub font_size: i16,
15 pub sup: i16,
17 pub mode: Mode,
19 pub title: String,
21 pub pages: Vec<Page>,
23 pub new_page: bool,
25
26 pub line_pad: i16,
28 pub margin_left: i16,
30 pub margin_right: i16,
32 pub margin_top: i16,
34 pub margin_bottom: i16,
36 pub page_width: i16,
38 pub page_height: i16,
40 pub line_used: i16,
42 pub line: Vec<Item>,
44 pub max_font_size: i16,
46 pub center: i16,
48}
49
50impl Default for Writer {
51 fn default() -> Self {
52 let mut x = Self {
53 mode: Mode::Normal,
54 title: String::new(),
55 b: BasicPdfWriter::default(),
56 fonts: Vec::new(),
57 cur_font: 0,
58 font_size: 10,
59 sup: 0,
60 p: Page::default(),
61 pages: Vec::new(),
62 new_page: true,
63
64 page_width: 400,
65 page_height: 600,
66 line_pad: 4,
67 margin_left: 20,
68 margin_right: 20,
69 margin_top: 20,
70 margin_bottom: 20,
71 line_used: 0,
72 line: Vec::new(),
73 max_font_size: 0,
74 center: 0,
75 };
76 for _ in 0..4 {
77 x.fonts.push(Box::<StandardFont>::default());
78 }
79 x
80 }
81}
82
83impl Writer {
84 fn init_page(&mut self) {
86 self.p.width = self.page_width;
87 self.p.height = self.page_height;
88 self.p.goto(
89 self.margin_left,
90 self.p.height - self.font_size - self.margin_top,
91 );
92 if self.sup != 0 {
93 self.p.set_sup(self.sup);
94 }
95 self.new_page = false;
96 }
97
98 pub fn save_page(&mut self) {
100 let p = std::mem::take(&mut self.p);
101 self.pages.push(p);
102 self.new_page = true;
103 }
104
105 fn init_font(&mut self, x: usize) {
107 let f = &mut self.fonts[x];
108 f.init(&mut self.b, HELVETICA[x]);
109 }
110
111 fn width(&self, _c: char) -> u64 {
113 if (self.cur_font & 1) == 1 {
116 550
117 } else {
118 500
119 }
120 }
121
122 fn line_len(&self) -> i16 {
124 self.page_width - self.margin_left - self.margin_right
125 }
126
127 fn wrap_text(&mut self, s: &str) {
129 if self.new_page {
130 self.init_page();
131 }
132
133 let mut w = 0;
134 for c in s.chars() {
135 w += self.width(c);
136 }
137 let w = (w * self.font_size as u64 / 1000) as i16;
138
139 if self.line_used + w > self.line_len() {
140 self.output_line();
141 if s == " " {
142 return;
143 }
144 }
145 self.line_used += w;
146 self.init_font(self.cur_font);
147 if self.font_size > self.max_font_size {
148 self.max_font_size = self.font_size;
149 }
150 self.line
151 .push(Item::Text(s.to_string(), self.cur_font, self.font_size));
152 }
153
154 pub fn output_line(&mut self) {
156 if self.new_page {
157 self.init_page();
158 } else {
159 let cx = if self.center == 1 {
160 (self.line_len() - self.line_used) / 2
161 } else {
162 0
163 };
164 let h = self.max_font_size + self.line_pad;
165 if self.p.y >= h + self.margin_bottom {
166 self.p.td(self.margin_left + cx - self.p.x, -h);
167 } else {
168 self.save_page();
169 self.init_page();
170 }
171 }
172 for item in &self.line {
173 match item {
174 Item::Text(s, f, x) => {
175 let fp = &*self.fonts[*f];
176 self.p.text(fp, *x, s);
177 }
178 Item::Sup(x) => {
179 self.p.set_sup(*x);
180 }
181 }
182 }
183 self.line.clear();
184 self.line_used = 0;
185 self.max_font_size = 0;
186 }
187
188 pub fn text(&mut self, s: &str) {
190 match self.mode {
191 Mode::Normal => {
192 self.wrap_text(s);
193 }
194 Mode::Title => {
195 self.title += s;
196 }
197 Mode::Head => {}
198 }
199 }
200
201 pub fn space(&mut self) {
203 self.text(" ");
204 }
205
206 pub fn set_sup(&mut self, sup: i16) {
208 self.line.push(Item::Sup(sup));
209 self.sup = sup;
210 }
211
212 pub fn finish(&mut self) {
214 self.output_line();
215 self.init_font(0);
216 self.save_page();
217 let n = self.pages.len();
218 let mut pnum = 1;
219 let font_size = 8;
220 for p in &mut self.pages {
221 p.goto(self.margin_left, self.line_pad);
222 p.text(
223 &*self.fonts[0],
224 font_size,
225 &format!("Page {} of {}", pnum, n),
226 );
227 p.finish();
228 pnum += 1;
229 }
230 self.b.finish(&self.pages, self.title.as_bytes());
231 }
232}
233
234#[derive(Clone, Copy)]
236pub enum Mode {
237 Normal,
239 Head,
241 Title,
243}
244
245pub enum Item {
247 Text(String, usize, i16),
249 Sup(i16),
251}
252
253fn _tos(s: &[u8]) -> String {
255 std::str::from_utf8(s).unwrap().to_string()
256}
257
258fn tosl(s: &[u8]) -> &str {
260 std::str::from_utf8(s).unwrap()
261}
262
263pub fn html(w: &mut Writer, source: &[u8]) {
265 let mut p = Parser::new(source);
266 p.read_token();
267 html_inner(w, &mut p, b"");
268}
269
270#[derive(Debug)]
271enum Token {
272 Text,
273 Tag,
274 WhiteSpace,
275 Eof,
276}
277
278struct Parser<'a> {
279 source: &'a [u8],
280 position: usize,
281 token_start: usize,
282 token_end: usize,
283 end_tag: bool,
284 token: Token,
285}
286
287impl<'a> Parser<'a> {
288 fn new(source: &'a [u8]) -> Self {
289 Self {
290 source,
291 position: 0,
292 token_start: 0,
293 token_end: 0,
294 end_tag: false,
295 token: Token::Eof,
296 }
297 }
298
299 fn tvalue(&self) -> &'a [u8] {
300 &self.source[self.token_start..self.token_end]
301 }
302
303 fn next(&mut self) -> u8 {
304 if self.position == self.source.len() {
305 0
306 } else {
307 let c = self.source[self.position];
308 self.position += 1;
309 c
310 }
311 }
312
313 fn read_token(&mut self) {
314 let c = self.next();
315 if c == 0 {
316 self.token = Token::Eof;
317 } else if c == b' ' || c == b'\n' {
318 self.token = Token::WhiteSpace;
319 loop {
320 let c = self.next();
321 if c != b' ' || c != b'\n' {
322 if c != 0 {
323 self.position -= 1;
324 }
325 break;
326 }
327 }
328 } else if c == b'<' {
329 self.token = Token::Tag;
333 self.token_start = self.position;
334 self.end_tag = false;
335 let mut c = self.next();
336 if c == b'/' {
337 self.end_tag = true;
338 self.token_start = self.position;
339 c = self.next();
340 }
341 let mut got_end = false;
342 loop {
343 if c == b' ' {
344 if !got_end {
345 self.token_end = self.position;
346 got_end = true;
347 }
348 } else if c == b'>' {
349 if !got_end {
350 self.token_end = self.position - 1;
351 break;
352 }
353 } else if c == 0 {
354 self.token = Token::Eof;
355 break;
356 }
357 c = self.next();
358 }
359 } else {
360 self.token = Token::Text;
361 self.token_start = self.position - 1;
362 let mut c = self.next();
363 loop {
364 if c == b'<' || c == b' ' || c == b'\n' {
365 self.position -= 1;
366 self.token_end = self.position;
367 break;
368 } else if c == 0 {
369 self.token_end = self.position;
370 break;
371 }
372 c = self.next();
373 }
374 }
375 }
376}
377
378fn html_inner(w: &mut Writer, p: &mut Parser, endtag: &[u8]) {
379 loop {
380 match p.token {
381 Token::Eof => {
382 return;
383 }
384 Token::WhiteSpace => {
385 w.space();
386 p.read_token();
387 }
388 Token::Text => {
389 let s = tosl(p.tvalue());
390 let s = &html_escape::decode_html_entities(s);
391 w.text(s);
392 p.read_token();
393 }
394 Token::Tag => {
395 let tag = p.tvalue();
396 if p.end_tag {
397 if tag == endtag {
398 p.read_token();
399 }
400 return;
401 } else if tag == b"p" && tag == endtag {
402 return;
403 }
404 p.read_token();
405 if tag == b"br" || tag == b"br/" {
406 w.output_line();
407 } else {
408 let save_mode = w.mode;
409 let save_font = w.cur_font;
410 let save_font_size = w.font_size;
411 let mut save: i16 = 0;
412 match tag {
413 b"p" => w.output_line(),
414 b"h1" => {
415 w.font_size = 14;
416 w.output_line();
417 save = w.center;
418 w.center = 1;
419 }
420 b"b" => w.cur_font |= 1,
421 b"i" => w.cur_font |= 2,
422 b"title" => w.mode = Mode::Title,
423 b"html" | b"head" => w.mode = Mode::Head,
424 b"body" => w.mode = Mode::Normal,
425 b"sup" => {
426 save = w.sup;
427 w.set_sup(w.font_size / 2);
428 }
429 b"sub" => {
430 save = w.sup;
431 w.set_sup(-w.font_size / 2);
432 }
433 _ => {}
434 }
435 html_inner(w, p, tag);
436 w.mode = save_mode;
437 w.font_size = save_font_size;
438 w.cur_font = save_font;
439 match tag {
440 b"sup" | b"sub" => w.set_sup(save),
441 b"h1" => {
442 w.output_line();
443 w.center = save;
444 }
445 _ => {}
446 }
447 }
448 }
449 }
450 }
451}