1pub mod content;
25pub mod error;
26pub mod font;
27pub mod graphics;
28pub mod image;
29pub mod parser;
30pub mod rasterizer;
31pub mod text;
32
33pub use error::{PdfRenderError, Result};
34pub use rasterizer::RasterPage;
35
36pub struct PdfRenderer {
40 doc: parser::PdfDocument,
41}
42
43impl PdfRenderer {
44 pub fn from_bytes(data: &[u8]) -> Result<Self> {
46 let doc = parser::PdfDocument::from_bytes(data)?;
47 Ok(Self { doc })
48 }
49
50 pub fn page_count(&self) -> usize {
52 self.doc.page_count()
53 }
54
55 pub fn render_page(&self, page_index: usize, dpi: f32) -> Result<RasterPage> {
59 let page = self.doc.get_page(page_index)?;
60 let mut rasterizer = rasterizer::PageRasterizer::new(&self.doc);
61 rasterizer.render(&page, dpi)
62 }
63
64 pub fn save_as_png(&self, page_index: usize, path: &str, dpi: f32) -> Result<()> {
66 let page = self.render_page(page_index, dpi)?;
67 page.save_png(path)
68 }
69
70 pub fn render_all_pages(&self, dpi: f32) -> Result<Vec<Vec<u8>>> {
72 let mut pages = Vec::new();
73 for i in 0..self.page_count() {
74 let page = self.render_page(i, dpi)?;
75 pages.push(page.to_png()?);
76 }
77 Ok(pages)
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn test_empty_pdf_returns_error() {
87 let result = PdfRenderer::from_bytes(b"not a pdf");
88 assert!(result.is_err());
89 }
90
91 #[test]
92 fn test_minimal_pdf() {
93 let pdf = b"%PDF-1.4\n\
95 1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n\
96 2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n\
97 3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>\nendobj\n\
98 xref\n0 4\n0000000000 65535 f \n0000000009 00000 n \n0000000058 00000 n \n0000000115 00000 n \n\
99 trailer\n<< /Size 4 /Root 1 0 R >>\nstartxref\n200\n%%EOF\n";
100
101 let renderer = PdfRenderer::from_bytes(pdf);
102 if let Ok(r) = renderer {
104 assert_eq!(r.page_count(), 1);
105 }
106 }
107}