pdf_min/
page.rs

1use crate::font::Font;
2use format_bytes::write_bytes as wb;
3use std::collections::BTreeSet;
4
5///
6#[derive(Default)]
7pub struct Page {
8    /// Page width.
9    pub width: i16,
10
11    /// Page height.
12    pub height: i16,
13
14    /// Output buffer.
15    pub os: Vec<u8>,
16
17    /// Text stream.
18    pub ts: Vec<u8>,
19
20    /// Current text buffer.
21    pub text: Vec<u8>,
22
23    /// Current line position ( from left of page ).
24    pub x: i16,
25
26    /// Current line position ( from bottom of page ).
27    pub y: i16,
28
29    /// Current font.
30    font_obj: usize,
31
32    /// Current font size.
33    font_size: i16,
34
35    /// Current super
36    pub sup: i16,
37
38    /// For checking whether font has changed.
39    last_font_obj: usize,
40
41    /// For checking whether font size has changed.
42    last_font_size: i16,
43
44    /// Set of font obj numbers used by page.
45    pub fonts: BTreeSet<usize>,
46
47    /// Set of other obj numbers used by page.
48    pub xobjs: BTreeSet<usize>,
49}
50
51impl Page {
52    /// Start a new line ( absolute position ).
53    pub fn goto(&mut self, x: i16, y: i16) {
54        self.td(x - self.x, y - self.y);
55    }
56
57    /// Start a new line ( relative to previous line ).
58    pub fn td(&mut self, x: i16, y: i16) {
59        self.flush_text();
60        let _ = wb!(&mut self.ts, b"\n{} {} Td ", x, y);
61        self.x += x;
62        self.y += y;
63    }
64
65    /// Append text ( encoded with font ).
66    pub fn text(&mut self, font: &dyn Font, size: i16, s: &str) {
67        if size != self.font_size || font.obj() != self.font_obj {
68            self.flush_text();
69            self.font_obj = font.obj();
70            self.font_size = size;
71        }
72        font.encode(s, &mut self.text);
73    }
74
75    /// Flush text using tj.
76    fn flush_text(&mut self) {
77        if self.text.is_empty() {
78            return;
79        }
80        if self.font_obj != self.last_font_obj || self.font_size != self.last_font_size {
81            self.fonts.insert(self.font_obj);
82            let obj = self.font_obj;
83            let size = self.font_size;
84            let _ = wb!(&mut self.ts, b"/F{} {} Tf", obj, size);
85            self.last_font_obj = obj;
86            self.last_font_size = size;
87        }
88        let mut hex = false;
89        for b in &self.text {
90            if *b < 32 || *b >= 128 {
91                hex = true;
92                break;
93            }
94        }
95        if hex {
96            self.ts.push(b'<');
97            for b in &self.text {
98                let x = *b >> 4;
99                self.ts.push(x + if x < 10 { 48 } else { 55 });
100                let x = *b & 15;
101                self.ts.push(x + if x < 10 { 48 } else { 55 });
102            }
103            self.ts.extend_from_slice(b"> Tj");
104        } else {
105            self.ts.push(b'(');
106            for b in &self.text {
107                let b = *b;
108                if b == b'(' || b == b')' || b == b'\\' {
109                    self.ts.push(b'\\');
110                }
111                self.ts.push(b);
112            }
113            self.ts.extend_from_slice(b") Tj");
114        }
115        self.text.clear();
116    }
117
118    /// Finish page by appending self.ts to self.os enclosed by "BT" and "ET".
119    pub fn finish(&mut self) {
120        self.flush_text();
121        self.os.extend_from_slice(b"\nBT");
122        self.os.extend_from_slice(&self.ts);
123        self.ts.clear();
124        self.os.extend_from_slice(b"\nET");
125    }
126
127    // Graphics operations
128
129    /// Draw a line from (x0,y0) to (x1,y1)
130    pub fn line(&mut self, x0: f64, y0: f64, x1: f64, y1: f64) {
131        let _ = wb!(&mut self.os, b"\n{} {} m {} {} l S", x0, y0, x1, y1);
132    }
133
134    /// Draw a rectangle with corners (x0,y0) to (x1,y1)
135    pub fn rect(&mut self, x0: f64, y0: f64, x1: f64, y1: f64) {
136        let _ = wb!(&mut self.os, b"\n{} {} m {} {} re S", x0, y0, x1, y1);
137    }
138
139    /// Set level of text on line.
140    pub fn set_sup(&mut self, sup: i16) {
141        if self.sup != sup {
142            self.flush_text();
143            self.sup = sup;
144            let _ = wb!(&mut self.ts, b" {} Ts", sup);
145        }
146    }
147}