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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
//! Utility / conveniece functions for commonly use graphical shapes

use crate::scale::Pt;
use crate::Point;

// PDF doesn't understand what a "circle" is, so we have to
// approximate it.
const C: f32 = 0.551915024494;

/// Calculates and returns the points for an approximated circle, given a radius and an
/// offset into the centre of circle (starting from bottom left corner of page).
#[inline]
pub fn calculate_points_for_circle<P: Into<Pt>>(
    radius: P,
    offset_x: P,
    offset_y: P,
) -> Vec<(Point, bool)> {
    let (radius, offset_x, offset_y) = (radius.into(), offset_x.into(), offset_y.into());
    let radius = radius.0;

    let p10 = Point {
        x: Pt(0.0 * radius),
        y: Pt(1.0 * radius),
    };
    let p11 = Point {
        x: Pt(C * radius),
        y: Pt(1.0 * radius),
    };
    let p12 = Point {
        x: Pt(1.0 * radius),
        y: Pt(C * radius),
    };
    let p13 = Point {
        x: Pt(1.0 * radius),
        y: Pt(0.0 * radius),
    };

    let p20 = Point {
        x: Pt(1.0 * radius),
        y: Pt(0.0 * radius),
    };
    let p21 = Point {
        x: Pt(1.0 * radius),
        y: Pt(-C * radius),
    };
    let p22 = Point {
        x: Pt(C * radius),
        y: Pt(-1.0 * radius),
    };
    let p23 = Point {
        x: Pt(0.0 * radius),
        y: Pt(-1.0 * radius),
    };

    let p30 = Point {
        x: Pt(0.0 * radius),
        y: Pt(-1.0 * radius),
    };
    let p31 = Point {
        x: Pt(-C * radius),
        y: Pt(-1.0 * radius),
    };
    let p32 = Point {
        x: Pt(-1.0 * radius),
        y: Pt(-C * radius),
    };
    let p33 = Point {
        x: Pt(-1.0 * radius),
        y: Pt(0.0 * radius),
    };

    let p40 = Point {
        x: Pt(-1.0 * radius),
        y: Pt(0.0 * radius),
    };
    let p41 = Point {
        x: Pt(-1.0 * radius),
        y: Pt(C * radius),
    };
    let p42 = Point {
        x: Pt(-C * radius),
        y: Pt(1.0 * radius),
    };
    let p43 = Point {
        x: Pt(0.0 * radius),
        y: Pt(1.0 * radius),
    };

    let mut pts = vec![
        (p10, true),
        (p11, true),
        (p12, true),
        (p13, false),
        (p20, true),
        (p21, true),
        (p22, true),
        (p23, false),
        (p30, true),
        (p31, true),
        (p32, true),
        (p33, false),
        (p40, true),
        (p41, true),
        (p42, true),
        (p43, false),
    ];

    for &mut (ref mut p, _) in pts.iter_mut() {
        p.x.0 += offset_x.0;
        p.y.0 += offset_y.0;
    }

    pts
}

/// Calculates and returns the points for a rectangle, given a horizontal and vertical scale.
/// and an offset into the centre of rectangle (starting from bottom left corner of page).
#[inline]
pub fn calculate_points_for_rect<P: Into<Pt>>(
    scale_x: P,
    scale_y: P,
    offset_x: P,
    offset_y: P,
) -> Vec<(Point, bool)> {
    let (scale_x, scale_y, offset_x, offset_y) = (
        scale_x.into(),
        scale_y.into(),
        offset_x.into(),
        offset_y.into(),
    );
    let top = Pt(offset_y.0 + (scale_y.0 / 2.0));
    let bottom = Pt(offset_y.0 - (scale_y.0 / 2.0));
    let left = Pt(offset_x.0 - (scale_x.0 / 2.0));
    let right = Pt(offset_x.0 + (scale_x.0 / 2.0));

    let top_left_pt = Point { x: left, y: top };
    let top_right_pt = Point { x: right, y: top };
    let bottom_right_pt = Point {
        x: right,
        y: bottom,
    };
    let bottom_left_pt = Point { x: left, y: bottom };

    vec![
        (top_left_pt, false),
        (top_right_pt, false),
        (bottom_right_pt, false),
        (bottom_left_pt, false),
    ]
}

use std::sync::atomic::{AtomicUsize, Ordering};

/// Since the random number generator doesn't have to be cryptographically secure
/// it doesn't make sense to import the entire rand library, so this is just a
/// xorshift pseudo-random function
static RAND_SEED: AtomicUsize = AtomicUsize::new(2100);

/// Xorshift-based random number generator. Impure function
pub(crate) fn rand() -> usize {
    let mut x = RAND_SEED.fetch_add(21, Ordering::SeqCst);
    #[cfg(target_pointer_width = "64")]
    {
        x ^= x << 21;
        x ^= x >> 35;
        x ^= x << 4;
        x
    }

    #[cfg(target_pointer_width = "32")]
    {
        x ^= x << 13;
        x ^= x >> 17;
        x ^= x << 5;
        x
    }
}

/// Returns a string with 32 random characters
pub(crate) fn random_character_string_32() -> String {
    const MAX_CHARS: usize = 32;
    let mut final_string = String::with_capacity(MAX_CHARS);
    let mut char_pos = 0;

    'outer: while char_pos < MAX_CHARS {
        let rand = format!("{}", rand());
        for ch in rand.chars() {
            if char_pos < MAX_CHARS {
                final_string.push(u8_to_char(ch.to_digit(10).unwrap() as u8));
                char_pos += 1;
            } else {
                break 'outer;
            }
        }
    }

    final_string
}

#[inline(always)]
fn u8_to_char(input: u8) -> char {
    (b'A' + input) as char
}

/// Takes a Vec<u8> of RGBA data and returns two Vec<u8> of RGB and alpha data
#[cfg(feature = "embedded_images")]
pub(crate) fn rgba_to_rgb(data: Vec<u8>) -> (Vec<u8>, Vec<u8>) {
    let mut rgb = Vec::with_capacity(data.len() / 4 * 3);
    let mut alpha = Vec::with_capacity(data.len() / 4);
    for i in (0..data.len()).step_by(4) {
        rgb.push(data[i]);
        rgb.push(data[i + 1]);
        rgb.push(data[i + 2]);
        alpha.push(data[i + 3]);
    }

    (rgb, alpha)
}