1pub mod elements;
2pub mod flex;
3pub mod fonts;
4pub mod image;
5pub mod serde_elements;
6pub mod test_utils;
7mod text;
8pub mod utils;
9
10use elements::padding::Padding;
11use fonts::Font;
12use pdf_writer::{Content, Name, Rect, Ref};
13use serde::{Deserialize, Serialize};
14
15pub use crate::text::TextPiecesCache;
16
17pub type Color = u32;
18
19#[derive(Copy, Clone, Serialize, Deserialize)]
24pub enum LineCapStyle {
25 Butt,
28
29 Round,
32
33 ProjectingSquare,
37}
38
39impl Into<pdf_writer::types::LineCapStyle> for LineCapStyle {
40 fn into(self) -> pdf_writer::types::LineCapStyle {
41 match self {
42 LineCapStyle::Butt => pdf_writer::types::LineCapStyle::ButtCap,
43 LineCapStyle::Round => pdf_writer::types::LineCapStyle::RoundCap,
44 LineCapStyle::ProjectingSquare => pdf_writer::types::LineCapStyle::ProjectingSquareCap,
45 }
46 }
47}
48
49#[derive(Copy, Clone, Serialize, Deserialize)]
54pub struct LineDashPattern {
55 pub offset: u16,
58
59 pub dashes: [u16; 2],
63}
64
65#[derive(Copy, Clone, Serialize, Deserialize)]
66pub struct LineStyle {
67 pub thickness: f32,
68 pub color: Color,
69 pub dash_pattern: Option<LineDashPattern>,
70 pub cap_style: LineCapStyle,
71}
72
73pub struct Layer {
74 pub content: Content,
75 pub graphics_state_restore_required: bool,
76}
77
78pub struct Page {
79 pub ext_g_states: Vec<Ref>, pub x_objects: Vec<Ref>,
81 pub layers: Vec<Layer>,
82 pub size: (f32, f32),
83}
84
85impl Page {
86 pub fn add_ext_g_state(&mut self, resource: Ref) -> usize {
87 self.ext_g_states.push(resource);
88 self.ext_g_states.len() - 1
89 }
90
91 pub fn add_x_object(&mut self, resource: Ref) -> String {
92 self.x_objects.push(resource);
93 (self.x_objects.len() - 1).to_string()
94 }
95}
96
97pub struct Pdf {
98 pub alloc: Ref,
99 pub pdf: pdf_writer::Pdf,
100 pub pages: Vec<Page>,
101 pub fonts: Vec<Ref>,
102 truetype_fonts: Vec<fonts::truetype::TruetypeFontState>,
103}
104
105impl Pdf {
106 pub fn new() -> Self {
107 let pdf = pdf_writer::Pdf::new();
108
109 Pdf {
110 alloc: pdf_writer::Ref::new(1),
111 pdf,
112 pages: Vec::new(),
113 fonts: Vec::new(),
114 truetype_fonts: Vec::new(),
115 }
116 }
117
118 pub fn alloc(&mut self) -> Ref {
119 self.alloc.bump()
120 }
121
122 pub fn add_page(&mut self, size: (f32, f32)) -> Location {
123 self.pages.push(Page {
124 ext_g_states: Vec::new(),
125 x_objects: Vec::new(),
126 layers: vec![Layer {
127 content: Content::new(),
128 graphics_state_restore_required: false,
129 }],
130 size,
131 });
132
133 Location {
134 page_idx: self.pages.len() - 1,
135 layer_idx: 0,
136 pos: (0., size.1),
137 scale_factor: 1.,
138 }
139 }
140
141 pub fn add_element(&mut self, page_size: (f32, f32), element: impl Element) {
144 let text_pieces_cache = TextPiecesCache::new();
145
146 self.add_element_with_text_pieces_cache(page_size, &text_pieces_cache, element);
147 }
148
149 pub fn add_element_with_text_pieces_cache(
152 &mut self,
153 page_size: (f32, f32),
154 text_pieces_cache: &TextPiecesCache,
155 element: impl Element,
156 ) {
157 let mut page_idx = self.pages.len() as u32;
158
159 let location = self.add_page((page_size.0, page_size.1));
160
161 let entry_page = page_idx;
162
163 let do_break = &mut |pdf: &mut Pdf, location_idx, _height| {
164 while page_idx <= entry_page + location_idx {
165 pdf.add_page((page_size.0, page_size.1));
166 page_idx += 1;
167 }
168
169 Location {
170 page_idx: (entry_page + location_idx + 1) as usize,
171 layer_idx: 0,
172 pos: (0., page_size.1),
173 scale_factor: 1.,
174 }
175 };
176
177 let ctx = DrawCtx {
178 pdf: self,
179 text_pieces_cache,
180 width: WidthConstraint {
181 max: page_size.0,
182 expand: true,
183 },
184 location,
185
186 first_height: page_size.1,
187 preferred_height: None,
188
189 breakable: Some(BreakableDraw {
190 full_height: page_size.1,
191 preferred_height_break_count: 0,
192 do_break,
193 }),
194 };
195
196 element.draw(ctx);
197 }
198
199 pub fn finish(mut self) -> Vec<u8> {
200 let catalog_ref = self.alloc();
201 let page_tree_ref = self.alloc();
202
203 self.pdf.catalog(catalog_ref).pages(page_tree_ref);
204
205 for mut truetype_font in self.truetype_fonts {
206 truetype_font.finish(&mut self.pdf, &mut self.alloc);
207 }
208
209 let pages = self
210 .pages
211 .iter()
212 .scan(self.alloc, |state, _| Some(state.bump()));
213
214 self.pdf
215 .pages(page_tree_ref)
216 .kids(pages)
217 .count(self.pages.len() as i32);
218
219 let mut page_alloc = self.alloc;
220 self.alloc = Ref::new(self.alloc.get() + self.pages.len() as i32);
221
222 for page in self.pages {
223 let mut page_writer = self.pdf.page(page_alloc.bump());
224
225 page_writer
226 .parent(page_tree_ref)
227 .media_box(Rect::new(
228 0.,
229 0.,
230 (page.size.0 * 72. / 25.4) as f32,
231 (page.size.1 * 72. / 25.4) as f32,
232 ))
233 .contents_array(
234 page.layers
235 .iter()
236 .scan(self.alloc, |state, _| Some(state.bump())),
237 );
238
239 let mut resources = page_writer.resources();
240
241 let mut ext_g_states = resources.ext_g_states();
242 for (i, ext_g_state) in page.ext_g_states.iter().enumerate() {
243 ext_g_states.pair(Name(format!("{i}").as_bytes()), ext_g_state);
244 }
245 drop(ext_g_states);
246
247 if !page.x_objects.is_empty() {
248 let mut x_objects = resources.x_objects();
249 for (i, x_object) in page.x_objects.iter().enumerate() {
250 x_objects.pair(Name(format!("{i}").as_bytes()), x_object);
251 }
252 }
253
254 let mut fonts = resources.fonts();
255
256 for (i, &font) in self.fonts.iter().enumerate() {
257 fonts.pair(Name(&format!("F{}", i).as_bytes()), font);
259 }
260
261 drop(fonts);
262 drop(resources);
263 drop(page_writer);
264
265 for mut layer in page.layers {
266 if layer.graphics_state_restore_required {
267 layer.content.restore_state();
268 }
269
270 self.pdf.stream(self.alloc.bump(), &layer.content.finish());
272 }
273 }
274
275 self.pdf.finish()
276 }
277}
278
279#[derive(Clone, Debug)]
285pub struct Location {
286 pub page_idx: usize,
287 pub layer_idx: usize,
288 pub pos: (f32, f32),
289 pub scale_factor: f32,
290}
291
292impl Location {
293 pub fn layer<'a>(&self, pdf: &'a mut Pdf) -> &'a mut Content {
294 &mut pdf.pages[self.page_idx].layers[self.layer_idx].content
295 }
296
297 pub fn next_layer(&self, pdf: &mut Pdf) -> Location {
298 let page = &mut pdf.pages[self.page_idx];
299
300 let mut content = Content::new();
301
302 let graphics_state_restore_required = if self.scale_factor != 1. {
303 content
304 .save_state()
305 .transform(utils::scale(self.scale_factor));
306 true
307 } else {
308 false
309 };
310
311 page.layers.push(Layer {
315 content,
316 graphics_state_restore_required,
317 });
318
319 Location {
320 layer_idx: page.layers.len() - 1,
321 ..*self
322 }
323 }
324}
325
326#[derive(Clone, Copy, Debug, PartialEq)]
327pub struct WidthConstraint {
328 pub max: f32,
329 pub expand: bool,
330}
331
332impl WidthConstraint {
333 pub fn constrain(&self, width: f32) -> f32 {
334 if self.expand {
335 self.max
336 } else {
337 width.min(self.max)
338 }
339 }
340
341 pub fn max(&self, width: f32) -> f32 {
342 if self.expand {
343 width.max(self.max)
344 } else {
345 width
346 }
347 }
348}
349
350pub type Pos = (f32, f32);
351pub type Size = (f32, f32);
352
353pub type Break<'a> = &'a mut dyn FnMut(&mut Pdf, u32, Option<f32>) -> Location;
362
363#[derive(Clone, Copy, PartialEq, Eq, Debug)]
364pub enum FirstLocationUsage {
365 NoneHeight,
369 WillUse,
370 WillSkip,
371}
372
373pub struct FirstLocationUsageCtx<'a> {
374 pub text_pieces_cache: &'a TextPiecesCache,
375 pub width: WidthConstraint,
376 pub first_height: f32,
377
378 pub full_height: f32,
385}
386
387impl<'a> FirstLocationUsageCtx<'a> {
388 pub fn break_appropriate_for_min_height(&self, height: f32) -> bool {
389 height > self.first_height && self.full_height > self.first_height
390 }
391}
392
393pub struct BreakableMeasure<'a> {
394 pub full_height: f32,
395 pub break_count: &'a mut u32,
396
397 pub extra_location_min_height: &'a mut Option<f32>,
408}
409
410pub struct MeasureCtx<'a> {
411 pub text_pieces_cache: &'a TextPiecesCache,
412 pub width: WidthConstraint,
413 pub first_height: f32,
414 pub breakable: Option<BreakableMeasure<'a>>,
415}
416
417impl<'a> MeasureCtx<'a> {
418 pub fn break_if_appropriate_for_min_height(&mut self, height: f32) -> bool {
419 if let Some(ref mut breakable) = self.breakable {
420 if height > self.first_height && breakable.full_height > self.first_height {
421 *breakable.break_count = 1;
422 return true;
423 }
424 }
425
426 false
427 }
428}
429
430pub struct BreakableDraw<'a> {
431 pub full_height: f32,
432 pub preferred_height_break_count: u32,
433 pub do_break: Break<'a>,
434}
435
436pub struct DrawCtx<'a, 'b> {
437 pub pdf: &'a mut Pdf,
438 pub text_pieces_cache: &'a TextPiecesCache,
439 pub location: Location,
440
441 pub width: WidthConstraint,
442 pub first_height: f32,
443
444 pub preferred_height: Option<f32>,
445
446 pub breakable: Option<BreakableDraw<'b>>,
447}
448
449impl<'a, 'b> DrawCtx<'a, 'b> {
450 pub fn break_if_appropriate_for_min_height(&mut self, height: f32) -> bool {
451 if let Some(ref mut breakable) = self.breakable {
452 if height > self.first_height && breakable.full_height > self.first_height {
453 self.location = (breakable.do_break)(self.pdf, 0, None);
456 return true;
457 }
458 }
459
460 false
461 }
462}
463
464#[derive(Copy, Clone, Debug, PartialEq)]
465pub struct ElementSize {
466 pub width: Option<f32>,
467
468 pub height: Option<f32>,
473}
474
475impl ElementSize {
476 pub fn new(width: Option<f32>, height: Option<f32>) -> Self {
477 ElementSize { width, height }
478 }
479}
480
481pub trait Element {
485 #[allow(unused_variables)]
486 fn first_location_usage(&self, ctx: FirstLocationUsageCtx) -> FirstLocationUsage {
487 FirstLocationUsage::WillUse
488 }
489
490 fn measure(&self, ctx: MeasureCtx) -> ElementSize;
491
492 fn draw(&self, ctx: DrawCtx) -> ElementSize;
493
494 fn with_padding_top(self, padding: f32) -> Padding<Self>
495 where
496 Self: Sized,
497 {
498 Padding {
499 left: 0.,
500 right: 0.,
501 top: padding,
502 bottom: 0.,
503 element: self,
504 }
505 }
506
507 fn with_vertical_padding(self, padding: f32) -> Padding<Self>
508 where
509 Self: Sized,
510 {
511 Padding {
512 left: 0.,
513 right: 0.,
514 top: padding,
515 bottom: padding,
516 element: self,
517 }
518 }
519
520 fn debug(self, color: u8) -> elements::debug::Debug<Self>
521 where
522 Self: Sized,
523 {
524 elements::debug::Debug {
525 element: self,
526 color,
527 show_max_width: false,
528 show_last_location_max_height: false,
529 }
530 }
531}
532
533pub trait CompositeElementCallback {
534 fn call(self, element: &impl Element);
535}
536
537pub trait CompositeElement {
538 fn element(&self, callback: impl CompositeElementCallback);
539}
540
541impl<C: CompositeElement> Element for C {
542 fn first_location_usage(&self, ctx: FirstLocationUsageCtx) -> FirstLocationUsage {
543 struct Callback<'a> {
544 ctx: FirstLocationUsageCtx<'a>,
545 ret: &'a mut FirstLocationUsage,
546 }
547
548 impl<'a> CompositeElementCallback for Callback<'a> {
549 fn call(self, element: &impl Element) {
550 *self.ret = element.first_location_usage(self.ctx);
551 }
552 }
553
554 let mut ret = FirstLocationUsage::NoneHeight;
555
556 self.element(Callback { ctx, ret: &mut ret });
557
558 ret
559 }
560
561 fn measure(&self, ctx: MeasureCtx) -> ElementSize {
562 struct Callback<'a> {
563 ctx: MeasureCtx<'a>,
564 ret: &'a mut ElementSize,
565 }
566
567 impl<'a> CompositeElementCallback for Callback<'a> {
568 fn call(self, element: &impl Element) {
569 *self.ret = element.measure(self.ctx);
570 }
571 }
572
573 let mut ret = ElementSize {
574 width: None,
575 height: None,
576 };
577
578 self.element(Callback { ctx, ret: &mut ret });
579
580 ret
581 }
582
583 fn draw(&self, ctx: DrawCtx) -> ElementSize {
584 struct Callback<'pdf, 'a, 'r> {
585 ctx: DrawCtx<'pdf, 'a>,
586 ret: &'r mut ElementSize,
587 }
588
589 impl<'pdf, 'a, 'r> CompositeElementCallback for Callback<'pdf, 'a, 'r> {
590 fn call(self, element: &impl Element) {
591 *self.ret = element.draw(self.ctx);
592 }
593 }
594
595 let mut ret = ElementSize {
596 width: None,
597 height: None,
598 };
599
600 self.element(Callback { ctx, ret: &mut ret });
601
602 ret
603 }
604}