use std::io::Write;
pub fn build_pdf(contents: &[&str]) -> Vec<u8> {
let n = contents.len() as u32;
let mut objs: Vec<Vec<u8>> = Vec::new();
objs.push(b"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n".to_vec());
let kids: String = (3..3 + n)
.map(|i| format!("{i} 0 R"))
.collect::<Vec<_>>()
.join(" ");
objs.push(
format!("2 0 obj\n<< /Type /Pages /Kids [{kids}] /Count {n} >>\nendobj\n").into_bytes(),
);
let font_id = 3 + n;
for (i, _) in contents.iter().enumerate() {
let page_id = 3 + i as u32;
let content_id = font_id + 1 + i as u32;
objs.push(
format!(
"{page_id} 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] \
/Resources << /Font << /F1 {font_id} 0 R >> >> /Contents {content_id} 0 R >>\nendobj\n"
)
.into_bytes(),
);
}
objs.push(
format!(
"{font_id} 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n"
)
.into_bytes(),
);
for (i, content) in contents.iter().enumerate() {
let content_id = font_id + 1 + i as u32;
objs.push(
format!(
"{content_id} 0 obj\n<< /Length {} >>\nstream\n{content}\nendstream\nendobj\n",
content.len()
)
.into_bytes(),
);
}
let mut out = Vec::new();
out.extend_from_slice(b"%PDF-1.4\n");
let mut offsets = Vec::with_capacity(objs.len());
for obj in &objs {
offsets.push(out.len());
out.extend_from_slice(obj);
}
let xref_pos = out.len();
let mut xref = format!("xref\n0 {}\n", objs.len() + 1);
xref.push_str("0000000000 65535 f \n");
for off in &offsets {
xref.push_str(&format!("{off:010} 00000 n \n"));
}
out.extend_from_slice(xref.as_bytes());
out.extend_from_slice(
format!(
"trailer\n<< /Size {} /Root 1 0 R >>\nstartxref\n{xref_pos}\n%%EOF\n",
objs.len() + 1
)
.as_bytes(),
);
out
}
pub fn minimal_text_pdf() -> Vec<u8> {
build_pdf(&["BT /F1 24 Tf 72 720 Td (Hello World) Tj ET"])
}
pub fn image_only_pdf() -> Vec<u8> {
let mut objs: Vec<Vec<u8>> = Vec::new();
objs.push(b"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n".to_vec());
objs.push(b"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n".to_vec());
objs.push(
b"3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] \
/Resources << /XObject << /Im0 5 0 R >> >> /Contents 4 0 R >>\nendobj\n"
.to_vec(),
);
let content = b"q 72 72 468 648 re W n /Im0 Do Q";
objs.push(
format!(
"4 0 obj\n<< /Length {} >>\nstream\n{}\nendstream\nendobj\n",
content.len(),
String::from_utf8_lossy(content)
)
.into_bytes(),
);
objs.push(
b"5 0 obj\n<< /Type /XObject /Subtype /Image /Width 1 /Height 1 \
/ColorSpace /DeviceGray /BitsPerComponent 8 /Length 1 >>\nstream\n\x00\nendstream\nendobj\n"
.to_vec(),
);
let mut out = Vec::new();
out.extend_from_slice(b"%PDF-1.4\n");
let mut offsets = Vec::with_capacity(objs.len());
for obj in &objs {
offsets.push(out.len());
out.extend_from_slice(obj);
}
let xref_pos = out.len();
let mut xref = format!("xref\n0 {}\n", objs.len() + 1);
xref.push_str("0000000000 65535 f \n");
for off in &offsets {
xref.push_str(&format!("{off:010} 00000 n \n"));
}
out.extend_from_slice(xref.as_bytes());
out.extend_from_slice(
format!(
"trailer\n<< /Size {} /Root 1 0 R >>\nstartxref\n{xref_pos}\n%%EOF\n",
objs.len() + 1
)
.as_bytes(),
);
out
}
pub fn write_temp(bytes: &[u8]) -> tempfile::NamedTempFile {
let mut file = tempfile::NamedTempFile::new().unwrap();
file.write_all(bytes).unwrap();
file
}
#[allow(dead_code)]
pub fn nested_array_poc_pdf(depth: usize) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(b"%PDF-1.4\n");
let mut catalog = String::from("1 0 obj\n<< /Type /Catalog /X ");
for _ in 0..depth {
catalog.push('[');
}
for _ in 0..depth {
catalog.push(']');
}
catalog.push_str(" >>\nendobj\n");
let catalog_off = out.len();
out.extend_from_slice(catalog.as_bytes());
let pages_off = out.len();
out.extend_from_slice(b"2 0 obj\n<< /Type /Pages /Kids [] /Count 0 >>\nendobj\n");
let xref_pos = out.len();
let trailer = format!(
"xref\n0 3\n0000000000 65535 f \n{catalog_off:010} 00000 n \n{pages_off:010} 00000 n \ntrailer\n<< /Size 3 /Root 1 0 R >>\nstartxref\n{xref_pos}\n%%EOF\n"
);
out.extend_from_slice(trailer.as_bytes());
out
}