Skip to main content

printpdf/
utils.rs

1use std::{
2    io::Read,
3    sync::atomic::{AtomicUsize, Ordering},
4};
5
6use crate::date::OffsetDateTime;
7
8/// Since the random number generator doesn't have to be cryptographically secure
9/// it doesn't make sense to import the entire rand library, so this is just a
10/// xorshift pseudo-random function
11static RAND_SEED: AtomicUsize = AtomicUsize::new(2100);
12
13/// Xorshift-based random number generator. Impure function
14pub(crate) fn random_number() -> usize {
15    let mut x = RAND_SEED.fetch_add(21, Ordering::SeqCst);
16    #[cfg(target_pointer_width = "64")]
17    {
18        x ^= x << 21;
19        x ^= x >> 35;
20        x ^= x << 4;
21        x
22    }
23
24    #[cfg(target_pointer_width = "32")]
25    {
26        x ^= x << 13;
27        x ^= x >> 17;
28        x ^= x << 5;
29        x
30    }
31}
32
33/// Returns a string with 32 random characters
34pub(crate) fn random_character_string_32() -> String {
35    const MAX_CHARS: usize = 32;
36    let mut final_string = String::with_capacity(MAX_CHARS);
37    let mut char_pos = 0;
38
39    'outer: while char_pos < MAX_CHARS {
40        let rand = format!("{}", crate::utils::random_number());
41        for ch in rand.chars() {
42            if char_pos < MAX_CHARS {
43                final_string.push(u8_to_char(ch.to_digit(10).unwrap() as u8));
44                char_pos += 1;
45            } else {
46                break 'outer;
47            }
48        }
49    }
50
51    final_string
52}
53
54// D:20170505150224+02'00'
55#[cfg(target_family = "wasm")]
56pub(crate) fn to_pdf_time_stamp_metadata(_date: &OffsetDateTime) -> String {
57    "D:19700101000000+00'00'".to_string()
58}
59
60#[cfg(not(target_family = "wasm"))]
61pub(crate) fn to_pdf_time_stamp_metadata(date: &OffsetDateTime) -> String {
62    format!(
63        "D:{:04}{:02}{:02}{:02}{:02}{:02}+00'00'",
64        date.year(),
65        u8::from(date.month()),
66        date.day(),
67        date.hour(),
68        date.minute(),
69        date.second(),
70    )
71}
72
73#[cfg(target_family = "wasm")]
74pub(crate) fn to_pdf_xmp_date(_date: &OffsetDateTime) -> String {
75    "D:1970-01-01T00:00:00+00'00'".to_string()
76}
77
78// D:2018-09-19T10:05:05+00'00'
79#[cfg(not(target_family = "wasm"))]
80pub(crate) fn to_pdf_xmp_date(date: &OffsetDateTime) -> String {
81    // Since the time is in UTC, we know that the time zone
82    // difference to UTC is 0 min, 0 sec, hence the 00'00
83    format!(
84        "D:{:04}-{:02}-{:02}T{:02}:{:02}:{:02}+00'00'",
85        date.year(),
86        date.month(),
87        date.day(),
88        date.hour(),
89        date.minute(),
90        date.second(),
91    )
92}
93
94/// `0 => A`, `1 => B`, and so on
95#[inline(always)]
96fn u8_to_char(input: u8) -> char {
97    (b'A' + input) as char
98}
99
100#[allow(dead_code)]
101pub(crate) fn compress(bytes: &[u8]) -> Vec<u8> {
102    use std::io::prelude::*;
103
104    use flate2::{write::GzEncoder, Compression};
105    let mut encoder = GzEncoder::new(Vec::new(), Compression::best());
106    let _ = encoder.write_all(bytes);
107    encoder.finish().unwrap_or_default()
108}
109
110pub(crate) fn uncompress(bytes: &[u8]) -> Vec<u8> {
111    use flate2::read::GzDecoder;
112    let mut gz = GzDecoder::new(bytes);
113    let mut s = Vec::<u8>::new();
114    let _ = gz.read_to_end(&mut s);
115    s
116}