use std::io::Read;
use std::sync::OnceLock;
use flate2::read::GzDecoder;
const DEJAVU_SANS_GZ: &[u8] = include_bytes!("../assets/fonts/DejaVuSans.ttf.gz");
#[cfg(any(feature = "png", feature = "pdf", feature = "embed_font"))]
const DEJAVU_SANS_BOLD_GZ: &[u8] = include_bytes!("../assets/fonts/DejaVuSans-Bold.ttf.gz");
#[cfg(any(feature = "png", feature = "pdf", feature = "embed_font"))]
const DEJAVU_SANS_OBLIQUE_GZ: &[u8] = include_bytes!("../assets/fonts/DejaVuSans-Oblique.ttf.gz");
#[cfg(any(feature = "png", feature = "pdf", feature = "embed_font"))]
const DEJAVU_SANS_MONO_GZ: &[u8] = include_bytes!("../assets/fonts/DejaVuSansMono.ttf.gz");
fn inflate(gz: &[u8], capacity: usize, label: &str) -> Vec<u8> {
let mut out = Vec::with_capacity(capacity);
GzDecoder::new(gz)
.read_to_end(&mut out)
.unwrap_or_else(|_| panic!("bundled {label} gzip stream is corrupt"));
out
}
pub(crate) fn dejavu_sans() -> &'static [u8] {
static BYTES: OnceLock<Vec<u8>> = OnceLock::new();
BYTES.get_or_init(|| inflate(DEJAVU_SANS_GZ, 800_000, "DejaVu Sans"))
}
#[cfg(any(feature = "png", feature = "pdf", feature = "embed_font"))]
pub(crate) fn dejavu_sans_bold() -> &'static [u8] {
static BYTES: OnceLock<Vec<u8>> = OnceLock::new();
BYTES.get_or_init(|| inflate(DEJAVU_SANS_BOLD_GZ, 750_000, "DejaVu Sans Bold"))
}
#[cfg(any(feature = "png", feature = "pdf", feature = "embed_font"))]
pub(crate) fn dejavu_sans_oblique() -> &'static [u8] {
static BYTES: OnceLock<Vec<u8>> = OnceLock::new();
BYTES.get_or_init(|| inflate(DEJAVU_SANS_OBLIQUE_GZ, 680_000, "DejaVu Sans Oblique"))
}
#[cfg(any(feature = "png", feature = "pdf", feature = "embed_font"))]
pub(crate) fn dejavu_sans_mono() -> &'static [u8] {
static BYTES: OnceLock<Vec<u8>> = OnceLock::new();
BYTES.get_or_init(|| inflate(DEJAVU_SANS_MONO_GZ, 380_000, "DejaVu Sans Mono"))
}
fn base64_encode(data: &[u8]) -> String {
const TABLE: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut out = String::with_capacity(data.len().div_ceil(3) * 4);
for chunk in data.chunks(3) {
let b0 = chunk[0] as usize;
let b1 = if chunk.len() > 1 {
chunk[1] as usize
} else {
0
};
let b2 = if chunk.len() > 2 {
chunk[2] as usize
} else {
0
};
let n = (b0 << 16) | (b1 << 8) | b2;
out.push(TABLE[(n >> 18) & 0x3f] as char);
out.push(TABLE[(n >> 12) & 0x3f] as char);
out.push(if chunk.len() > 1 {
TABLE[(n >> 6) & 0x3f] as char
} else {
'='
});
out.push(if chunk.len() > 2 {
TABLE[n & 0x3f] as char
} else {
'='
});
}
out
}
pub(crate) fn dejavu_sans_style_block() -> &'static str {
static BLOCK: OnceLock<String> = OnceLock::new();
BLOCK.get_or_init(|| {
let reg = base64_encode(dejavu_sans());
let bold = base64_encode(dejavu_sans_bold());
let oblique = base64_encode(dejavu_sans_oblique());
let mono = base64_encode(dejavu_sans_mono());
let url = "data:font/truetype;base64,";
format!(
"<style>\
@font-face{{font-family:'DejaVu Sans';src:url('{url}{reg}') format('truetype');font-weight:normal;font-style:normal;}}\
@font-face{{font-family:'DejaVu Sans';src:url('{url}{bold}') format('truetype');font-weight:bold;font-style:normal;}}\
@font-face{{font-family:'DejaVu Sans';src:url('{url}{oblique}') format('truetype');font-weight:normal;font-style:italic;}}\
@font-face{{font-family:'DejaVu Sans Mono';src:url('{url}{mono}') format('truetype');font-weight:normal;font-style:normal;}}\
</style>"
)
})
}