graphitepdf_renderer/
lib.rs1pub 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}