Skip to main content

graphitepdf_renderer/
lib.rs

1pub mod error {
2    pub use graphitepdf_render::error::*;
3}
4
5pub use graphitepdf_render::*;
6
7#[cfg(test)]
8mod tests {
9    use super::*;
10
11    use graphitepdf_font::{FontDescriptor, FontSource, StandardFont};
12    use graphitepdf_layout::LayoutMetadata;
13    use graphitepdf_layout::{Document, LayoutStyle, Node, Page};
14    use graphitepdf_primitives::{Bounds, Color, Pt, Size};
15    use std::io::Cursor;
16
17    fn simple_render_document(label: &str) -> RenderDocument {
18        RenderDocument {
19            metadata: LayoutMetadata {
20                title: Some(String::from(label)),
21                ..LayoutMetadata::default()
22            },
23            pages: vec![RenderPage {
24                size: Size::new(240.0, 180.0),
25                source_page_index: 0,
26                commands: vec![
27                    RenderCommand::FillRect(FillRectOp {
28                        context: graphitepdf_render::RenderContext {
29                            page_index: 0,
30                            source_page_index: 0,
31                            path: vec![0],
32                            node_kind: graphitepdf_render::RenderNodeKind::Box,
33                            z_index: 0,
34                            frame: Bounds::from_origin_size(12.0, 18.0, 120.0, 48.0),
35                            content_frame: Bounds::from_origin_size(12.0, 18.0, 120.0, 48.0),
36                        },
37                        bounds: Bounds::from_origin_size(12.0, 18.0, 120.0, 48.0),
38                        color: Color::rgb(0x22, 0x66, 0xaa),
39                        role: graphitepdf_render::PaintRole::Background,
40                    }),
41                    RenderCommand::DrawText(TextRenderOp {
42                        context: graphitepdf_render::RenderContext {
43                            page_index: 0,
44                            source_page_index: 0,
45                            path: vec![1],
46                            node_kind: graphitepdf_render::RenderNodeKind::Text,
47                            z_index: 1,
48                            frame: Bounds::from_origin_size(18.0, 26.0, 180.0, 24.0),
49                            content_frame: Bounds::from_origin_size(18.0, 26.0, 180.0, 24.0),
50                        },
51                        text: label.to_string(),
52                        color: Color::BLACK,
53                        font: Some(FontDescriptor::new("Helvetica")),
54                        font_source: Some(FontSource::standard(StandardFont::Helvetica)),
55                        font_size: Pt::new(12.0),
56                        line_height: Some(Pt::new(14.0)),
57                        block: None,
58                        layout: None,
59                    }),
60                ],
61            }],
62        }
63    }
64
65    #[test]
66    fn renders_render_documents_to_pdf_bytes() {
67        let document = simple_render_document("Hello renderer");
68        let bytes = render_to_bytes(&document).expect("render document should serialize");
69
70        assert!(bytes.starts_with(b"%PDF-1.7"));
71        assert!(
72            bytes
73                .windows(b"/Type /Page".len())
74                .any(|window| window == b"/Type /Page")
75        );
76        assert!(
77            bytes
78                .windows(b"%%EOF".len())
79                .any(|window| window == b"%%EOF")
80        );
81    }
82
83    #[test]
84    fn session_tracks_revisions_and_refreshes_after_updates() {
85        let document = Document::new().with_page(
86            Page::new([Node::box_node().with_style(
87                LayoutStyle::new().with_background_color(Color::rgb(0xdd, 0xee, 0xff)),
88            )])
89            .with_size(Size::new(200.0, 120.0)),
90        );
91        let mut session = RendererSession::new(document);
92
93        let first_revision = session
94            .render_snapshot()
95            .expect("initial render should succeed")
96            .revision();
97        let first_bytes = session
98            .to_bytes()
99            .expect("session should produce pdf bytes");
100
101        assert_eq!(first_revision, 0);
102        assert_eq!(
103            session
104                .render_snapshot()
105                .expect("cached render should still be available")
106                .revision(),
107            0
108        );
109
110        let updated_revision = session.update_document(|document| {
111            document.add_page(Page::new([Node::box_node()]).with_size(Size::new(200.0, 120.0)));
112        });
113        let (updated_snapshot_revision, updated_page_count) = {
114            let updated_snapshot = session
115                .render_snapshot()
116                .expect("updated render should succeed");
117            (
118                updated_snapshot.revision(),
119                updated_snapshot.document().pages.len(),
120            )
121        };
122        let updated_bytes = session
123            .to_bytes()
124            .expect("updated session should serialize");
125
126        assert_eq!(updated_revision, 1);
127        assert_eq!(updated_snapshot_revision, 1);
128        assert_eq!(updated_page_count, 2);
129        assert_ne!(first_bytes, updated_bytes);
130    }
131
132    #[test]
133    fn writer_and_file_helpers_emit_pdf_output() {
134        let document = simple_render_document("Writer helper");
135        let mut buffer = Cursor::new(Vec::new());
136
137        render_to_writer(&document, &mut buffer).expect("writer helper should succeed");
138        assert!(buffer.get_ref().starts_with(b"%PDF-1.7"));
139
140        let path = std::env::temp_dir().join(format!(
141            "graphitepdf-renderer-test-{}-{}.pdf",
142            std::process::id(),
143            std::thread::current().name().unwrap_or("unnamed")
144        ));
145        render_to_file(&document, &path).expect("file helper should succeed");
146        let saved = std::fs::read(&path).expect("saved PDF should be readable");
147        let _ = std::fs::remove_file(&path);
148
149        assert!(saved.starts_with(b"%PDF-1.7"));
150    }
151}