Skip to main content

pdf_min/
page.rs

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