1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use crate::font::Font;
use format_bytes::write_bytes as wb;
use std::collections::BTreeSet;

///
#[derive(Default)]
pub struct Page {
    /// Page width.
    pub width: i16,

    /// Page height.
    pub height: i16,

    /// Output buffer.
    pub os: Vec<u8>,

    /// Text stream.
    pub ts: Vec<u8>,

    /// Current text buffer.
    pub text: Vec<u8>,

    /// Current line position ( from left of page ).
    pub x: i16,

    /// Current line position ( from bottom of page ).
    pub y: i16,

    /// Current font.
    font_obj: usize,

    /// Current font size.
    font_size: i16,

    /// Current super
    pub sup: i16,

    /// For checking whether font has changed.
    last_font_obj: usize,

    /// For checking whether font size has changed.
    last_font_size: i16,

    /// Set of font obj numbers used by page.
    pub fonts: BTreeSet<usize>,

    /// Set of other obj numbers used by page.
    pub xobjs: BTreeSet<usize>,
}

impl Page {
    /// Start a new line ( absolute position ).
    pub fn goto(&mut self, x: i16, y: i16) {
        self.td(x - self.x, y - self.y);
    }

    /// Start a new line ( relative to previous line ).
    pub fn td(&mut self, x: i16, y: i16) {
        self.flush_text();
        let _ = wb!(&mut self.ts, b"\n{} {} Td ", x, y);
        self.x += x;
        self.y += y;
    }

    /// Append text ( encoded with font ).
    pub fn text(&mut self, font: &dyn Font, size: i16, s: &str) {
        if size != self.font_size || font.obj() != self.font_obj {
            self.flush_text();
            self.font_obj = font.obj();
            self.font_size = size;
        }
        font.encode(s, &mut self.text);
    }

    /// Flush text using tj.
    fn flush_text(&mut self) {
        if self.text.is_empty() {
            return;
        }
        if self.font_obj != self.last_font_obj || self.font_size != self.last_font_size {
            self.fonts.insert(self.font_obj);
            let obj = self.font_obj;
            let size = self.font_size;
            let _ = wb!(&mut self.ts, b"/F{} {} Tf", obj, size);
            self.last_font_obj = obj;
            self.last_font_size = size;
        }
        let mut hex = false;
        for b in &self.text {
            if *b < 32 || *b >= 128 {
                hex = true;
                break;
            }
        }
        if hex {
            self.ts.push(b'<');
            for b in &self.text {
                let x = *b >> 4;
                self.ts.push(x + if x < 10 { 48 } else { 55 });
                let x = *b & 15;
                self.ts.push(x + if x < 10 { 48 } else { 55 });
            }
            self.ts.extend_from_slice(b"> Tj");
        } else {
            self.ts.push(b'(');
            for b in &self.text {
                let b = *b;
                if b == b'(' || b == b')' || b == b'\\' {
                    self.ts.push(b'\\');
                }
                self.ts.push(b);
            }
            self.ts.extend_from_slice(b") Tj");
        }
        self.text.clear();
    }

    /// Finish page by appending self.ts to self.os enclosed by "BT" and "ET".
    pub fn finish(&mut self) {
        self.flush_text();
        self.os.extend_from_slice(b"\nBT");
        self.os.extend_from_slice(&self.ts);
        self.ts.clear();
        self.os.extend_from_slice(b"\nET");
    }

    // Graphics operations

    /// Draw a line from (x0,y0) to (x1,y1)
    pub fn line(&mut self, x0: f64, y0: f64, x1: f64, y1: f64) {
        let _ = wb!(&mut self.os, b"\n{} {} m {} {} l S", x0, y0, x1, y1);
    }

    /// Draw a rectangle with corners (x0,y0) to (x1,y1)
    pub fn rect(&mut self, x0: f64, y0: f64, x1: f64, y1: f64) {
        let _ = wb!(&mut self.os, b"\n{} {} m {} {} re S", x0, y0, x1, y1);
    }

    /// Set level of text on line.
    pub fn set_sup(&mut self, sup: i16) {
        if self.sup != sup {
            self.flush_text();
            self.sup = sup;
            let _ = wb!(&mut self.ts, b" {} Ts", sup);
        }
    }
}