1pub mod elements;
2pub mod flex;
3pub mod fonts;
4pub mod image;
5pub mod serde_elements;
6pub mod test_utils;
7pub mod text;
8pub mod utils;
9
10use elements::padding::Padding;
11use pdf_writer::{Content, Name, Rect, Ref};
12use fonts::Font;
14use serde::{Deserialize, Serialize};
16
17pub const EMPTY_FIELD: &str = "—";
18
19#[derive(Debug)]
20pub struct FontSet<'a, F: Font> {
21 pub regular: &'a F,
22 pub bold: &'a F,
23 pub italic: &'a F,
24 pub bold_italic: &'a F,
25}
26
27impl<'a, F: Font> Clone for FontSet<'a, F> {
28 fn clone(&self) -> Self {
29 *self
30 }
31}
32
33impl<'a, F: Font> Copy for FontSet<'a, F> {}
34
35#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
36pub enum VAlign {
37 Top,
38 Center,
39 Bottom,
40}
41
42pub type Color = u32;
43
44#[derive(Copy, Clone, Serialize, Deserialize)]
49pub enum LineCapStyle {
50 Butt,
53
54 Round,
57
58 ProjectingSquare,
62}
63
64impl Into<pdf_writer::types::LineCapStyle> for LineCapStyle {
65 fn into(self) -> pdf_writer::types::LineCapStyle {
66 match self {
67 LineCapStyle::Butt => pdf_writer::types::LineCapStyle::ButtCap,
68 LineCapStyle::Round => pdf_writer::types::LineCapStyle::RoundCap,
69 LineCapStyle::ProjectingSquare => pdf_writer::types::LineCapStyle::ProjectingSquareCap,
70 }
71 }
72}
73
74#[derive(Copy, Clone, Serialize, Deserialize)]
79pub struct LineDashPattern {
80 pub offset: u16,
83
84 pub dashes: [u16; 2],
88}
89
90#[derive(Copy, Clone, Serialize, Deserialize)]
91pub struct LineStyle {
92 pub thickness: f32,
93 pub color: Color,
94 pub dash_pattern: Option<LineDashPattern>,
95 pub cap_style: LineCapStyle,
96}
97
98pub struct Layer {
99 pub content: Content,
100 pub graphics_state_restore_required: bool,
101}
102
103pub struct Page {
104 pub ext_g_states: Vec<Ref>, pub x_objects: Vec<Ref>,
106 pub layers: Vec<Layer>,
107 pub size: (f32, f32),
108}
109
110impl Page {
111 pub fn add_ext_g_state(&mut self, resource: Ref) -> usize {
112 self.ext_g_states.push(resource);
113 self.ext_g_states.len() - 1
114 }
115
116 pub fn add_x_object(&mut self, resource: Ref) -> String {
117 self.x_objects.push(resource);
118 (self.x_objects.len() - 1).to_string()
119 }
120}
121
122pub struct Pdf {
123 pub alloc: Ref,
124 pub pdf: pdf_writer::Pdf,
125 pub pages: Vec<Page>,
126 pub fonts: Vec<Ref>,
127 truetype_fonts: Vec<fonts::truetype::TruetypeFontState>,
128}
129
130impl Pdf {
131 pub fn new() -> Self {
132 let pdf = pdf_writer::Pdf::new();
133
134 Pdf {
135 alloc: pdf_writer::Ref::new(1),
136 pdf,
137 pages: Vec::new(),
138 fonts: Vec::new(),
139 truetype_fonts: Vec::new(),
140 }
141 }
142
143 pub fn alloc(&mut self) -> Ref {
144 self.alloc.bump()
145 }
146
147 pub fn add_page(&mut self, size: (f32, f32)) -> Location {
148 self.pages.push(Page {
149 ext_g_states: Vec::new(),
150 x_objects: Vec::new(),
151 layers: vec![Layer {
152 content: Content::new(),
153 graphics_state_restore_required: false,
154 }],
155 size,
156 });
157
158 Location {
159 page_idx: self.pages.len() - 1,
160 layer_idx: 0,
161 pos: (0., size.1),
162 scale_factor: 1.,
163 }
164 }
165
166 pub fn finish(mut self) -> Vec<u8> {
167 let catalog_ref = self.alloc();
168 let page_tree_ref = self.alloc();
169
170 self.pdf.catalog(catalog_ref).pages(page_tree_ref);
171
172 for mut truetype_font in self.truetype_fonts {
173 truetype_font.finish(&mut self.pdf, &mut self.alloc);
174 }
175
176 let pages = self
177 .pages
178 .iter()
179 .scan(self.alloc, |state, _| Some(state.bump()));
180
181 self.pdf
182 .pages(page_tree_ref)
183 .kids(pages)
184 .count(self.pages.len() as i32);
185
186 let mut page_alloc = self.alloc;
187 self.alloc = Ref::new(self.alloc.get() + self.pages.len() as i32);
188
189 for page in self.pages {
190 let mut page_writer = self.pdf.page(page_alloc.bump());
191
192 page_writer
193 .parent(page_tree_ref)
194 .media_box(Rect::new(
195 0.,
196 0.,
197 (page.size.0 * 72. / 25.4) as f32,
198 (page.size.1 * 72. / 25.4) as f32,
199 ))
200 .contents_array(
201 page.layers
202 .iter()
203 .scan(self.alloc, |state, _| Some(state.bump())),
204 );
205
206 let mut resources = page_writer.resources();
207
208 let mut ext_g_states = resources.ext_g_states();
209 for (i, ext_g_state) in page.ext_g_states.iter().enumerate() {
210 ext_g_states.pair(Name(format!("{i}").as_bytes()), ext_g_state);
211 }
212 drop(ext_g_states);
213
214 if !page.x_objects.is_empty() {
215 let mut x_objects = resources.x_objects();
216 for (i, x_object) in page.x_objects.iter().enumerate() {
217 x_objects.pair(Name(format!("{i}").as_bytes()), x_object);
218 }
219 }
220
221 let mut fonts = resources.fonts();
222
223 for (i, &font) in self.fonts.iter().enumerate() {
224 fonts.pair(Name(&format!("F{}", i).as_bytes()), font);
226 }
227
228 drop(fonts);
229 drop(resources);
230 drop(page_writer);
231
232 for mut layer in page.layers {
233 if layer.graphics_state_restore_required {
234 layer.content.restore_state();
235 }
236
237 self.pdf.stream(self.alloc.bump(), &layer.content.finish());
239 }
240 }
241
242 self.pdf.finish()
243 }
244}
245
246#[derive(Clone, Debug)]
252pub struct Location {
253 pub page_idx: usize,
254 pub layer_idx: usize,
255 pub pos: (f32, f32),
256 pub scale_factor: f32,
257}
258
259impl Location {
260 pub fn layer<'a>(&self, pdf: &'a mut Pdf) -> &'a mut Content {
261 &mut pdf.pages[self.page_idx].layers[self.layer_idx].content
262 }
263
264 pub fn next_layer(&self, pdf: &mut Pdf) -> Location {
265 let page = &mut pdf.pages[self.page_idx];
266
267 let mut content = Content::new();
268
269 let graphics_state_restore_required = if self.scale_factor != 1. {
270 content
271 .save_state()
272 .transform(utils::scale(self.scale_factor));
273 true
274 } else {
275 false
276 };
277
278 page.layers.push(Layer {
282 content,
283 graphics_state_restore_required,
284 });
285
286 Location {
287 layer_idx: page.layers.len() - 1,
288 ..*self
289 }
290 }
291}
292
293#[derive(Clone, Copy, Debug, PartialEq)]
294pub struct WidthConstraint {
295 pub max: f32,
296 pub expand: bool,
297}
298
299impl WidthConstraint {
300 pub fn constrain(&self, width: f32) -> f32 {
301 if self.expand {
302 self.max
303 } else {
304 width.min(self.max)
305 }
306 }
307}
308
309pub type Pos = (f32, f32);
310pub type Size = (f32, f32);
311
312pub type Break<'a> = &'a mut dyn FnMut(&mut Pdf, u32, Option<f32>) -> Location;
321
322#[derive(Clone, Copy, PartialEq, Eq, Debug)]
323pub enum FirstLocationUsage {
324 NoneHeight,
328 WillUse,
329 WillSkip,
330}
331
332pub struct FirstLocationUsageCtx {
333 pub width: WidthConstraint,
334 pub first_height: f32,
335
336 pub full_height: f32,
343}
344
345impl FirstLocationUsageCtx {
346 pub fn break_appropriate_for_min_height(&self, height: f32) -> bool {
347 height > self.first_height && self.full_height > self.first_height
348 }
349}
350
351pub struct BreakableMeasure<'a> {
352 pub full_height: f32,
353 pub break_count: &'a mut u32,
354
355 pub extra_location_min_height: &'a mut Option<f32>,
366}
367
368pub struct MeasureCtx<'a> {
369 pub width: WidthConstraint,
370 pub first_height: f32,
371 pub breakable: Option<BreakableMeasure<'a>>,
372}
373
374impl<'a> MeasureCtx<'a> {
375 pub fn break_if_appropriate_for_min_height(&mut self, height: f32) -> bool {
376 if let Some(ref mut breakable) = self.breakable {
377 if height > self.first_height && breakable.full_height > self.first_height {
378 *breakable.break_count = 1;
379 return true;
380 }
381 }
382
383 false
384 }
385}
386
387pub struct BreakableDraw<'a> {
388 pub full_height: f32,
389 pub preferred_height_break_count: u32,
390 pub do_break: Break<'a>,
391}
392
393pub struct DrawCtx<'a, 'b> {
394 pub pdf: &'a mut Pdf,
395 pub location: Location,
396
397 pub width: WidthConstraint,
398 pub first_height: f32,
399
400 pub preferred_height: Option<f32>,
401
402 pub breakable: Option<BreakableDraw<'b>>,
403}
404
405impl<'a, 'b> DrawCtx<'a, 'b> {
406 pub fn break_if_appropriate_for_min_height(&mut self, height: f32) -> bool {
407 if let Some(ref mut breakable) = self.breakable {
408 if height > self.first_height && breakable.full_height > self.first_height {
409 self.location = (breakable.do_break)(self.pdf, 0, None);
412 return true;
413 }
414 }
415
416 false
417 }
418}
419
420#[derive(Copy, Clone, Debug, PartialEq)]
421pub struct ElementSize {
422 pub width: Option<f32>,
423
424 pub height: Option<f32>,
429}
430
431impl ElementSize {
432 pub fn new(width: Option<f32>, height: Option<f32>) -> Self {
433 ElementSize { width, height }
434 }
435}
436
437pub trait Element {
441 #[allow(unused_variables)]
442 fn first_location_usage(&self, ctx: FirstLocationUsageCtx) -> FirstLocationUsage {
443 FirstLocationUsage::WillUse
444 }
445
446 fn measure(&self, ctx: MeasureCtx) -> ElementSize;
447
448 fn draw(&self, ctx: DrawCtx) -> ElementSize;
449
450 fn with_padding_top(self, padding: f32) -> Padding<Self>
451 where
452 Self: Sized,
453 {
454 Padding {
455 left: 0.,
456 right: 0.,
457 top: padding,
458 bottom: 0.,
459 element: self,
460 }
461 }
462
463 fn with_vertical_padding(self, padding: f32) -> Padding<Self>
464 where
465 Self: Sized,
466 {
467 Padding {
468 left: 0.,
469 right: 0.,
470 top: padding,
471 bottom: padding,
472 element: self,
473 }
474 }
475
476 fn debug(self, color: u8) -> elements::debug::Debug<Self>
477 where
478 Self: Sized,
479 {
480 elements::debug::Debug {
481 element: self,
482 color,
483 show_max_width: false,
484 show_last_location_max_height: false,
485 }
486 }
487}
488
489pub trait CompositeElementCallback {
490 fn call(self, element: &impl Element);
491}
492
493pub trait CompositeElement {
494 fn element(&self, callback: impl CompositeElementCallback);
495}
496
497impl<C: CompositeElement> Element for C {
498 fn first_location_usage(&self, ctx: FirstLocationUsageCtx) -> FirstLocationUsage {
499 struct Callback<'a> {
500 ctx: FirstLocationUsageCtx,
501 ret: &'a mut FirstLocationUsage,
502 }
503
504 impl<'a> CompositeElementCallback for Callback<'a> {
505 fn call(self, element: &impl Element) {
506 *self.ret = element.first_location_usage(self.ctx);
507 }
508 }
509
510 let mut ret = FirstLocationUsage::NoneHeight;
511
512 self.element(Callback { ctx, ret: &mut ret });
513
514 ret
515 }
516
517 fn measure(&self, ctx: MeasureCtx) -> ElementSize {
518 struct Callback<'a> {
519 ctx: MeasureCtx<'a>,
520 ret: &'a mut ElementSize,
521 }
522
523 impl<'a> CompositeElementCallback for Callback<'a> {
524 fn call(self, element: &impl Element) {
525 *self.ret = element.measure(self.ctx);
526 }
527 }
528
529 let mut ret = ElementSize {
530 width: None,
531 height: None,
532 };
533
534 self.element(Callback { ctx, ret: &mut ret });
535
536 ret
537 }
538
539 fn draw(&self, ctx: DrawCtx) -> ElementSize {
540 struct Callback<'pdf, 'a, 'r> {
541 ctx: DrawCtx<'pdf, 'a>,
542 ret: &'r mut ElementSize,
543 }
544
545 impl<'pdf, 'a, 'r> CompositeElementCallback for Callback<'pdf, 'a, 'r> {
546 fn call(self, element: &impl Element) {
547 *self.ret = element.draw(self.ctx);
548 }
549 }
550
551 let mut ret = ElementSize {
552 width: None,
553 height: None,
554 };
555
556 self.element(Callback { ctx, ret: &mut ret });
557
558 ret
559 }
560}
561
562