oxihuman_export/
pdf_export.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq)]
9pub struct PdfPageSize {
10 pub width_pt: f32,
11 pub height_pt: f32,
12}
13
14impl PdfPageSize {
15 pub fn a4() -> Self {
17 Self {
18 width_pt: 595.28,
19 height_pt: 841.89,
20 }
21 }
22
23 pub fn letter() -> Self {
25 Self {
26 width_pt: 612.0,
27 height_pt: 792.0,
28 }
29 }
30
31 pub fn area(&self) -> f32 {
33 self.width_pt * self.height_pt
34 }
35}
36
37#[derive(Debug, Clone)]
39pub struct PdfPage {
40 pub size: PdfPageSize,
41 pub content: String,
42}
43
44impl PdfPage {
45 pub fn new(size: PdfPageSize) -> Self {
47 Self {
48 size,
49 content: String::new(),
50 }
51 }
52
53 pub fn append_text(&mut self, text: &str) {
55 self.content.push_str(text);
56 }
57
58 pub fn has_content(&self) -> bool {
60 !self.content.is_empty()
61 }
62}
63
64#[derive(Debug, Clone)]
66pub struct PdfExport {
67 pub title: String,
68 pub author: String,
69 pub pages: Vec<PdfPage>,
70}
71
72impl PdfExport {
73 pub fn new(title: &str) -> Self {
75 Self {
76 title: title.to_string(),
77 author: String::new(),
78 pages: Vec::new(),
79 }
80 }
81
82 pub fn add_page(&mut self, page: PdfPage) {
84 self.pages.push(page);
85 }
86
87 pub fn page_count(&self) -> usize {
89 self.pages.len()
90 }
91}
92
93pub fn pdf_header_bytes() -> Vec<u8> {
95 b"%PDF-1.7\n".to_vec()
96}
97
98pub fn estimate_pdf_bytes(doc: &PdfExport) -> usize {
100 let content_len: usize = doc.pages.iter().map(|p| p.content.len() + 256).sum();
101 512 + content_len
102}
103
104pub fn validate_pdf(doc: &PdfExport) -> bool {
106 !doc.title.is_empty() && !doc.pages.is_empty()
107}
108
109pub fn pdf_metadata_json(doc: &PdfExport) -> String {
111 format!(
112 "{{\"title\":\"{}\",\"author\":\"{}\",\"pages\":{}}}",
113 doc.title,
114 doc.author,
115 doc.page_count()
116 )
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 fn sample_doc() -> PdfExport {
124 let mut doc = PdfExport::new("Mesh Report");
125 let mut p = PdfPage::new(PdfPageSize::a4());
126 p.append_text("Hello, world!");
127 doc.add_page(p);
128 doc
129 }
130
131 #[test]
132 fn test_page_count() {
133 assert_eq!(sample_doc().page_count(), 1);
135 }
136
137 #[test]
138 fn test_validate_valid() {
139 assert!(validate_pdf(&sample_doc()));
141 }
142
143 #[test]
144 fn test_validate_empty_title() {
145 let doc = PdfExport::new("");
147 assert!(!validate_pdf(&doc));
148 }
149
150 #[test]
151 fn test_pdf_header_magic() {
152 let h = pdf_header_bytes();
154 assert_eq!(&h[..4], b"%PDF");
155 }
156
157 #[test]
158 fn test_estimate_bytes_positive() {
159 assert!(estimate_pdf_bytes(&sample_doc()) > 0);
161 }
162
163 #[test]
164 fn test_metadata_json_contains_title() {
165 let json = pdf_metadata_json(&sample_doc());
167 assert!(json.contains("Mesh Report"));
168 }
169
170 #[test]
171 fn test_page_has_content() {
172 let mut p = PdfPage::new(PdfPageSize::a4());
174 p.append_text("test");
175 assert!(p.has_content());
176 }
177
178 #[test]
179 fn test_a4_area() {
180 let a4 = PdfPageSize::a4();
182 assert!(a4.area() > 100_000.0);
183 }
184}