1use crate::style::Style;
13use serde::{Deserialize, Deserializer, Serialize};
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub struct Document {
19 pub children: Vec<Node>,
22
23 #[serde(default)]
25 pub metadata: Metadata,
26
27 #[serde(default)]
30 pub default_page: PageConfig,
31
32 #[serde(default)]
35 pub fonts: Vec<FontEntry>,
36
37 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub default_style: Option<crate::style::Style>,
41
42 #[serde(default)]
44 pub tagged: bool,
45
46 #[serde(default)]
48 pub pdfa: Option<PdfAConformance>,
49
50 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub embedded_data: Option<String>,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
58pub enum PdfAConformance {
59 #[serde(rename = "2a")]
61 A2a,
62 #[serde(rename = "2b")]
64 A2b,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct FontEntry {
70 pub family: String,
72 pub src: String,
74 #[serde(default = "default_weight")]
76 pub weight: u32,
77 #[serde(default)]
79 pub italic: bool,
80}
81
82fn default_weight() -> u32 {
83 400
84}
85
86#[derive(Debug, Clone, Default, Serialize, Deserialize)]
88pub struct Metadata {
89 pub title: Option<String>,
90 pub author: Option<String>,
91 pub subject: Option<String>,
92 pub creator: Option<String>,
93 #[serde(default, skip_serializing_if = "Option::is_none")]
95 pub lang: Option<String>,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct PageConfig {
101 #[serde(default = "PageSize::default")]
103 pub size: PageSize,
104
105 #[serde(default)]
107 pub margin: Edges,
108
109 #[serde(default = "default_true")]
111 pub wrap: bool,
112}
113
114impl Default for PageConfig {
115 fn default() -> Self {
116 Self {
117 size: PageSize::A4,
118 margin: Edges::uniform(54.0), wrap: true,
120 }
121 }
122}
123
124fn default_true() -> bool {
125 true
126}
127
128#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
130pub enum PageSize {
131 #[default]
132 A4,
133 A3,
134 A5,
135 Letter,
136 Legal,
137 Tabloid,
138 Custom {
139 width: f64,
140 height: f64,
141 },
142}
143
144impl PageSize {
145 pub fn dimensions(&self) -> (f64, f64) {
147 match self {
148 PageSize::A4 => (595.28, 841.89),
149 PageSize::A3 => (841.89, 1190.55),
150 PageSize::A5 => (419.53, 595.28),
151 PageSize::Letter => (612.0, 792.0),
152 PageSize::Legal => (612.0, 1008.0),
153 PageSize::Tabloid => (792.0, 1224.0),
154 PageSize::Custom { width, height } => (*width, *height),
155 }
156 }
157}
158
159#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
161pub struct Edges {
162 pub top: f64,
163 pub right: f64,
164 pub bottom: f64,
165 pub left: f64,
166}
167
168#[derive(Debug, Clone, Copy, Serialize)]
170pub enum EdgeValue {
171 Pt(f64),
172 Auto,
173}
174
175impl Default for EdgeValue {
176 fn default() -> Self {
177 EdgeValue::Pt(0.0)
178 }
179}
180
181impl EdgeValue {
182 pub fn resolve(&self) -> f64 {
184 match self {
185 EdgeValue::Pt(v) => *v,
186 EdgeValue::Auto => 0.0,
187 }
188 }
189
190 pub fn is_auto(&self) -> bool {
192 matches!(self, EdgeValue::Auto)
193 }
194}
195
196impl<'de> Deserialize<'de> for EdgeValue {
197 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
198 where
199 D: Deserializer<'de>,
200 {
201 use serde::de;
202
203 struct EdgeValueVisitor;
204
205 impl<'de> de::Visitor<'de> for EdgeValueVisitor {
206 type Value = EdgeValue;
207
208 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
209 f.write_str("a number or the string \"auto\"")
210 }
211
212 fn visit_f64<E: de::Error>(self, v: f64) -> Result<EdgeValue, E> {
213 Ok(EdgeValue::Pt(v))
214 }
215
216 fn visit_i64<E: de::Error>(self, v: i64) -> Result<EdgeValue, E> {
217 Ok(EdgeValue::Pt(v as f64))
218 }
219
220 fn visit_u64<E: de::Error>(self, v: u64) -> Result<EdgeValue, E> {
221 Ok(EdgeValue::Pt(v as f64))
222 }
223
224 fn visit_str<E: de::Error>(self, v: &str) -> Result<EdgeValue, E> {
225 if v == "auto" {
226 Ok(EdgeValue::Auto)
227 } else {
228 Err(de::Error::invalid_value(de::Unexpected::Str(v), &self))
229 }
230 }
231 }
232
233 deserializer.deserialize_any(EdgeValueVisitor)
234 }
235}
236
237#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
239pub struct MarginEdges {
240 pub top: EdgeValue,
241 pub right: EdgeValue,
242 pub bottom: EdgeValue,
243 pub left: EdgeValue,
244}
245
246impl MarginEdges {
247 pub fn horizontal(&self) -> f64 {
249 self.left.resolve() + self.right.resolve()
250 }
251
252 pub fn vertical(&self) -> f64 {
254 self.top.resolve() + self.bottom.resolve()
255 }
256
257 pub fn has_auto_horizontal(&self) -> bool {
259 self.left.is_auto() || self.right.is_auto()
260 }
261
262 pub fn has_auto_vertical(&self) -> bool {
264 self.top.is_auto() || self.bottom.is_auto()
265 }
266
267 pub fn from_edges(e: Edges) -> Self {
269 MarginEdges {
270 top: EdgeValue::Pt(e.top),
271 right: EdgeValue::Pt(e.right),
272 bottom: EdgeValue::Pt(e.bottom),
273 left: EdgeValue::Pt(e.left),
274 }
275 }
276
277 pub fn to_edges(&self) -> Edges {
279 Edges {
280 top: self.top.resolve(),
281 right: self.right.resolve(),
282 bottom: self.bottom.resolve(),
283 left: self.left.resolve(),
284 }
285 }
286}
287
288impl Edges {
289 pub fn uniform(v: f64) -> Self {
290 Self {
291 top: v,
292 right: v,
293 bottom: v,
294 left: v,
295 }
296 }
297
298 pub fn symmetric(vertical: f64, horizontal: f64) -> Self {
299 Self {
300 top: vertical,
301 right: horizontal,
302 bottom: vertical,
303 left: horizontal,
304 }
305 }
306
307 pub fn horizontal(&self) -> f64 {
308 self.left + self.right
309 }
310
311 pub fn vertical(&self) -> f64 {
312 self.top + self.bottom
313 }
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
318#[serde(rename_all = "camelCase")]
319pub struct Node {
320 pub kind: NodeKind,
322
323 #[serde(default)]
325 pub style: Style,
326
327 #[serde(default)]
329 pub children: Vec<Node>,
330
331 #[serde(default)]
333 pub id: Option<String>,
334
335 #[serde(default, skip_serializing_if = "Option::is_none")]
337 pub source_location: Option<SourceLocation>,
338
339 #[serde(default, skip_serializing_if = "Option::is_none")]
341 pub bookmark: Option<String>,
342
343 #[serde(default, skip_serializing_if = "Option::is_none")]
345 pub href: Option<String>,
346
347 #[serde(default, skip_serializing_if = "Option::is_none")]
349 pub alt: Option<String>,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize)]
354#[serde(tag = "type")]
355pub enum NodeKind {
356 Page {
358 #[serde(default)]
359 config: PageConfig,
360 },
361
362 View,
364
365 Text {
367 content: String,
368 #[serde(default, skip_serializing_if = "Option::is_none")]
370 href: Option<String>,
371 #[serde(default, skip_serializing_if = "Vec::is_empty")]
373 runs: Vec<TextRun>,
374 },
375
376 Image {
378 src: String,
380 width: Option<f64>,
382 height: Option<f64>,
384 },
385
386 Table {
388 #[serde(default)]
390 columns: Vec<ColumnDef>,
391 },
392
393 TableRow {
395 #[serde(default)]
398 is_header: bool,
399 },
400
401 TableCell {
403 #[serde(default = "default_one")]
405 col_span: u32,
406 #[serde(default = "default_one")]
408 row_span: u32,
409 },
410
411 Fixed {
413 position: FixedPosition,
415 },
416
417 PageBreak,
419
420 Svg {
422 width: f64,
424 height: f64,
426 #[serde(default, skip_serializing_if = "Option::is_none")]
428 view_box: Option<String>,
429 content: String,
431 },
432
433 Canvas {
435 width: f64,
437 height: f64,
439 operations: Vec<CanvasOp>,
441 },
442
443 Barcode {
445 data: String,
447 #[serde(default)]
449 format: crate::barcode::BarcodeFormat,
450 #[serde(default, skip_serializing_if = "Option::is_none")]
452 width: Option<f64>,
453 #[serde(default = "default_barcode_height")]
455 height: f64,
456 },
457
458 QrCode {
460 data: String,
462 #[serde(default, skip_serializing_if = "Option::is_none")]
465 size: Option<f64>,
466 },
467
468 BarChart {
470 data: Vec<ChartDataPoint>,
472 width: f64,
474 height: f64,
476 #[serde(default, skip_serializing_if = "Option::is_none")]
478 color: Option<String>,
479 #[serde(default = "default_true")]
481 show_labels: bool,
482 #[serde(default)]
484 show_values: bool,
485 #[serde(default)]
487 show_grid: bool,
488 #[serde(default, skip_serializing_if = "Option::is_none")]
490 title: Option<String>,
491 },
492
493 LineChart {
495 series: Vec<ChartSeries>,
497 labels: Vec<String>,
499 width: f64,
501 height: f64,
503 #[serde(default)]
505 show_points: bool,
506 #[serde(default)]
508 show_grid: bool,
509 #[serde(default, skip_serializing_if = "Option::is_none")]
511 title: Option<String>,
512 },
513
514 PieChart {
516 data: Vec<ChartDataPoint>,
518 width: f64,
520 height: f64,
522 #[serde(default)]
524 donut: bool,
525 #[serde(default)]
527 show_legend: bool,
528 #[serde(default, skip_serializing_if = "Option::is_none")]
530 title: Option<String>,
531 },
532
533 AreaChart {
535 series: Vec<ChartSeries>,
537 labels: Vec<String>,
539 width: f64,
541 height: f64,
543 #[serde(default)]
545 show_grid: bool,
546 #[serde(default, skip_serializing_if = "Option::is_none")]
548 title: Option<String>,
549 },
550
551 DotPlot {
553 groups: Vec<DotPlotGroup>,
555 width: f64,
557 height: f64,
559 #[serde(default, skip_serializing_if = "Option::is_none")]
561 x_min: Option<f64>,
562 #[serde(default, skip_serializing_if = "Option::is_none")]
564 x_max: Option<f64>,
565 #[serde(default, skip_serializing_if = "Option::is_none")]
567 y_min: Option<f64>,
568 #[serde(default, skip_serializing_if = "Option::is_none")]
570 y_max: Option<f64>,
571 #[serde(default, skip_serializing_if = "Option::is_none")]
573 x_label: Option<String>,
574 #[serde(default, skip_serializing_if = "Option::is_none")]
576 y_label: Option<String>,
577 #[serde(default)]
579 show_legend: bool,
580 #[serde(default = "default_dot_size")]
582 dot_size: f64,
583 },
584
585 Watermark {
587 text: String,
589 #[serde(default = "default_watermark_font_size")]
591 font_size: f64,
592 #[serde(default = "default_watermark_angle")]
594 angle: f64,
595 },
596}
597
598#[derive(Debug, Clone, Serialize, Deserialize)]
600pub struct ChartDataPoint {
601 pub label: String,
602 pub value: f64,
603 #[serde(default, skip_serializing_if = "Option::is_none")]
604 pub color: Option<String>,
605}
606
607#[derive(Debug, Clone, Serialize, Deserialize)]
609pub struct ChartSeries {
610 pub name: String,
611 pub data: Vec<f64>,
612 #[serde(default, skip_serializing_if = "Option::is_none")]
613 pub color: Option<String>,
614}
615
616#[derive(Debug, Clone, Serialize, Deserialize)]
618pub struct DotPlotGroup {
619 pub name: String,
620 #[serde(default, skip_serializing_if = "Option::is_none")]
621 pub color: Option<String>,
622 pub data: Vec<(f64, f64)>,
623}
624
625#[derive(Debug, Clone, Serialize, Deserialize)]
627#[serde(tag = "op")]
628pub enum CanvasOp {
629 MoveTo {
630 x: f64,
631 y: f64,
632 },
633 LineTo {
634 x: f64,
635 y: f64,
636 },
637 BezierCurveTo {
638 cp1x: f64,
639 cp1y: f64,
640 cp2x: f64,
641 cp2y: f64,
642 x: f64,
643 y: f64,
644 },
645 QuadraticCurveTo {
646 cpx: f64,
647 cpy: f64,
648 x: f64,
649 y: f64,
650 },
651 ClosePath,
652 Rect {
653 x: f64,
654 y: f64,
655 width: f64,
656 height: f64,
657 },
658 Circle {
659 cx: f64,
660 cy: f64,
661 r: f64,
662 },
663 Ellipse {
664 cx: f64,
665 cy: f64,
666 rx: f64,
667 ry: f64,
668 },
669 Arc {
670 cx: f64,
671 cy: f64,
672 r: f64,
673 start_angle: f64,
674 end_angle: f64,
675 #[serde(default)]
676 counterclockwise: bool,
677 },
678 Stroke,
679 Fill,
680 FillAndStroke,
681 SetFillColor {
682 r: f64,
683 g: f64,
684 b: f64,
685 },
686 SetStrokeColor {
687 r: f64,
688 g: f64,
689 b: f64,
690 },
691 SetLineWidth {
692 width: f64,
693 },
694 SetLineCap {
695 cap: u32,
696 },
697 SetLineJoin {
698 join: u32,
699 },
700 Save,
701 Restore,
702}
703
704#[derive(Debug, Clone, Serialize, Deserialize)]
706#[serde(rename_all = "camelCase")]
707pub struct TextRun {
708 pub content: String,
709 #[serde(default)]
710 pub style: crate::style::Style,
711 #[serde(default, skip_serializing_if = "Option::is_none")]
712 pub href: Option<String>,
713}
714
715#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
717pub enum Position {
718 #[default]
719 Relative,
720 Absolute,
721}
722
723fn default_one() -> u32 {
724 1
725}
726
727fn default_barcode_height() -> f64 {
728 60.0
729}
730
731fn default_dot_size() -> f64 {
732 4.0
733}
734
735fn default_watermark_font_size() -> f64 {
736 60.0
737}
738
739fn default_watermark_angle() -> f64 {
740 -45.0
741}
742
743#[derive(Debug, Clone, Serialize, Deserialize)]
745pub struct ColumnDef {
746 pub width: ColumnWidth,
748}
749
750#[derive(Debug, Clone, Serialize, Deserialize)]
751pub enum ColumnWidth {
752 Fraction(f64),
754 Fixed(f64),
756 Auto,
758}
759
760#[derive(Debug, Clone, Serialize, Deserialize)]
762pub enum FixedPosition {
763 Header,
765 Footer,
767}
768
769#[derive(Debug, Clone, Serialize, Deserialize)]
771#[serde(rename_all = "camelCase")]
772pub struct SourceLocation {
773 pub file: String,
774 pub line: u32,
775 pub column: u32,
776}
777
778impl Node {
779 pub fn view(style: Style, children: Vec<Node>) -> Self {
781 Self {
782 kind: NodeKind::View,
783 style,
784 children,
785 id: None,
786 source_location: None,
787 bookmark: None,
788 href: None,
789 alt: None,
790 }
791 }
792
793 pub fn text(content: &str, style: Style) -> Self {
795 Self {
796 kind: NodeKind::Text {
797 content: content.to_string(),
798 href: None,
799 runs: vec![],
800 },
801 style,
802 children: vec![],
803 id: None,
804 source_location: None,
805 bookmark: None,
806 href: None,
807 alt: None,
808 }
809 }
810
811 pub fn page(config: PageConfig, style: Style, children: Vec<Node>) -> Self {
813 Self {
814 kind: NodeKind::Page { config },
815 style,
816 children,
817 id: None,
818 source_location: None,
819 bookmark: None,
820 href: None,
821 alt: None,
822 }
823 }
824
825 pub fn is_breakable(&self) -> bool {
827 match &self.kind {
828 NodeKind::View | NodeKind::Table { .. } | NodeKind::Text { .. } => {
829 self.style.wrap.unwrap_or(true)
830 }
831 NodeKind::TableRow { .. } => true,
832 NodeKind::Image { .. } => false,
833 NodeKind::Svg { .. } => false,
834 NodeKind::Canvas { .. } => false,
835 NodeKind::Barcode { .. } => false,
836 NodeKind::QrCode { .. } => false,
837 NodeKind::BarChart { .. } => false,
838 NodeKind::LineChart { .. } => false,
839 NodeKind::PieChart { .. } => false,
840 NodeKind::AreaChart { .. } => false,
841 NodeKind::DotPlot { .. } => false,
842 NodeKind::Watermark { .. } => false,
843 NodeKind::PageBreak => false,
844 NodeKind::Fixed { .. } => false,
845 NodeKind::Page { .. } => true,
846 NodeKind::TableCell { .. } => true,
847 }
848 }
849}