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)]
52 pub pdf_ua: bool,
53
54 #[serde(default, skip_serializing_if = "Option::is_none")]
57 pub embedded_data: Option<String>,
58
59 #[serde(default)]
63 pub flatten_forms: bool,
64
65 #[serde(default, skip_serializing_if = "Option::is_none", alias = "signature")]
68 pub certification: Option<CertificationConfig>,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73pub enum PdfAConformance {
74 #[serde(rename = "2a")]
76 A2a,
77 #[serde(rename = "2b")]
79 A2b,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct RedactionRegion {
85 pub page: usize,
87 pub x: f64,
89 pub y: f64,
92 pub width: f64,
94 pub height: f64,
96 #[serde(default)]
98 pub color: Option<String>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103pub enum PatternType {
104 Literal,
106 Regex,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct RedactionPattern {
113 pub pattern: String,
115 pub pattern_type: PatternType,
117 #[serde(default)]
119 pub page: Option<usize>,
120 #[serde(default)]
122 pub color: Option<String>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127#[serde(rename_all = "camelCase")]
128pub struct CertificationConfig {
129 pub certificate_pem: String,
131 pub private_key_pem: String,
133 #[serde(default)]
135 pub reason: Option<String>,
136 #[serde(default)]
138 pub location: Option<String>,
139 #[serde(default)]
141 pub contact: Option<String>,
142 #[serde(default)]
144 pub visible: bool,
145 #[serde(default)]
147 pub x: Option<f64>,
148 #[serde(default)]
150 pub y: Option<f64>,
151 #[serde(default)]
153 pub width: Option<f64>,
154 #[serde(default)]
156 pub height: Option<f64>,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct FontEntry {
162 pub family: String,
164 pub src: String,
166 #[serde(default = "default_weight")]
168 pub weight: u32,
169 #[serde(default)]
171 pub italic: bool,
172}
173
174fn default_weight() -> u32 {
175 400
176}
177
178#[derive(Debug, Clone, Default, Serialize, Deserialize)]
180pub struct Metadata {
181 pub title: Option<String>,
182 pub author: Option<String>,
183 pub subject: Option<String>,
184 pub creator: Option<String>,
185 #[serde(default, skip_serializing_if = "Option::is_none")]
187 pub lang: Option<String>,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct PageConfig {
193 #[serde(default = "PageSize::default")]
195 pub size: PageSize,
196
197 #[serde(default)]
199 pub margin: Edges,
200
201 #[serde(default = "default_true")]
203 pub wrap: bool,
204}
205
206impl Default for PageConfig {
207 fn default() -> Self {
208 Self {
209 size: PageSize::A4,
210 margin: Edges::uniform(54.0), wrap: true,
212 }
213 }
214}
215
216fn default_true() -> bool {
217 true
218}
219
220#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
222pub enum PageSize {
223 #[default]
224 A4,
225 A3,
226 A5,
227 Letter,
228 Legal,
229 Tabloid,
230 Custom {
231 width: f64,
232 height: f64,
233 },
234}
235
236impl PageSize {
237 pub fn dimensions(&self) -> (f64, f64) {
239 match self {
240 PageSize::A4 => (595.28, 841.89),
241 PageSize::A3 => (841.89, 1190.55),
242 PageSize::A5 => (419.53, 595.28),
243 PageSize::Letter => (612.0, 792.0),
244 PageSize::Legal => (612.0, 1008.0),
245 PageSize::Tabloid => (792.0, 1224.0),
246 PageSize::Custom { width, height } => (*width, *height),
247 }
248 }
249}
250
251#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
253pub struct Edges {
254 pub top: f64,
255 pub right: f64,
256 pub bottom: f64,
257 pub left: f64,
258}
259
260#[derive(Debug, Clone, Copy, Serialize)]
262pub enum EdgeValue {
263 Pt(f64),
264 Auto,
265}
266
267impl Default for EdgeValue {
268 fn default() -> Self {
269 EdgeValue::Pt(0.0)
270 }
271}
272
273impl EdgeValue {
274 pub fn resolve(&self) -> f64 {
276 match self {
277 EdgeValue::Pt(v) => *v,
278 EdgeValue::Auto => 0.0,
279 }
280 }
281
282 pub fn is_auto(&self) -> bool {
284 matches!(self, EdgeValue::Auto)
285 }
286}
287
288impl<'de> Deserialize<'de> for EdgeValue {
289 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
290 where
291 D: Deserializer<'de>,
292 {
293 use serde::de;
294
295 struct EdgeValueVisitor;
296
297 impl<'de> de::Visitor<'de> for EdgeValueVisitor {
298 type Value = EdgeValue;
299
300 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
301 f.write_str("a number or the string \"auto\"")
302 }
303
304 fn visit_f64<E: de::Error>(self, v: f64) -> Result<EdgeValue, E> {
305 Ok(EdgeValue::Pt(v))
306 }
307
308 fn visit_i64<E: de::Error>(self, v: i64) -> Result<EdgeValue, E> {
309 Ok(EdgeValue::Pt(v as f64))
310 }
311
312 fn visit_u64<E: de::Error>(self, v: u64) -> Result<EdgeValue, E> {
313 Ok(EdgeValue::Pt(v as f64))
314 }
315
316 fn visit_str<E: de::Error>(self, v: &str) -> Result<EdgeValue, E> {
317 if v == "auto" {
318 Ok(EdgeValue::Auto)
319 } else {
320 Err(de::Error::invalid_value(de::Unexpected::Str(v), &self))
321 }
322 }
323 }
324
325 deserializer.deserialize_any(EdgeValueVisitor)
326 }
327}
328
329#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
331pub struct MarginEdges {
332 pub top: EdgeValue,
333 pub right: EdgeValue,
334 pub bottom: EdgeValue,
335 pub left: EdgeValue,
336}
337
338impl MarginEdges {
339 pub fn horizontal(&self) -> f64 {
341 self.left.resolve() + self.right.resolve()
342 }
343
344 pub fn vertical(&self) -> f64 {
346 self.top.resolve() + self.bottom.resolve()
347 }
348
349 pub fn has_auto_horizontal(&self) -> bool {
351 self.left.is_auto() || self.right.is_auto()
352 }
353
354 pub fn has_auto_vertical(&self) -> bool {
356 self.top.is_auto() || self.bottom.is_auto()
357 }
358
359 pub fn from_edges(e: Edges) -> Self {
361 MarginEdges {
362 top: EdgeValue::Pt(e.top),
363 right: EdgeValue::Pt(e.right),
364 bottom: EdgeValue::Pt(e.bottom),
365 left: EdgeValue::Pt(e.left),
366 }
367 }
368
369 pub fn to_edges(&self) -> Edges {
371 Edges {
372 top: self.top.resolve(),
373 right: self.right.resolve(),
374 bottom: self.bottom.resolve(),
375 left: self.left.resolve(),
376 }
377 }
378}
379
380impl Edges {
381 pub fn uniform(v: f64) -> Self {
382 Self {
383 top: v,
384 right: v,
385 bottom: v,
386 left: v,
387 }
388 }
389
390 pub fn symmetric(vertical: f64, horizontal: f64) -> Self {
391 Self {
392 top: vertical,
393 right: horizontal,
394 bottom: vertical,
395 left: horizontal,
396 }
397 }
398
399 pub fn horizontal(&self) -> f64 {
400 self.left + self.right
401 }
402
403 pub fn vertical(&self) -> f64 {
404 self.top + self.bottom
405 }
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
410#[serde(rename_all = "camelCase")]
411pub struct Node {
412 pub kind: NodeKind,
414
415 #[serde(default)]
417 pub style: Style,
418
419 #[serde(default)]
421 pub children: Vec<Node>,
422
423 #[serde(default)]
425 pub id: Option<String>,
426
427 #[serde(default, skip_serializing_if = "Option::is_none")]
429 pub source_location: Option<SourceLocation>,
430
431 #[serde(default, skip_serializing_if = "Option::is_none")]
433 pub bookmark: Option<String>,
434
435 #[serde(default, skip_serializing_if = "Option::is_none")]
437 pub href: Option<String>,
438
439 #[serde(default, skip_serializing_if = "Option::is_none")]
441 pub alt: Option<String>,
442}
443
444#[derive(Debug, Clone, Serialize, Deserialize)]
446#[serde(tag = "type")]
447pub enum NodeKind {
448 Page {
450 #[serde(default)]
451 config: PageConfig,
452 },
453
454 View,
456
457 Text {
459 content: String,
460 #[serde(default, skip_serializing_if = "Option::is_none")]
462 href: Option<String>,
463 #[serde(default, skip_serializing_if = "Vec::is_empty")]
465 runs: Vec<TextRun>,
466 },
467
468 Image {
470 src: String,
472 width: Option<f64>,
474 height: Option<f64>,
476 },
477
478 Table {
480 #[serde(default)]
482 columns: Vec<ColumnDef>,
483 },
484
485 TableRow {
487 #[serde(default)]
490 is_header: bool,
491 },
492
493 TableCell {
495 #[serde(default = "default_one")]
497 col_span: u32,
498 #[serde(default = "default_one")]
500 row_span: u32,
501 },
502
503 Fixed {
505 position: FixedPosition,
507 },
508
509 PageBreak,
511
512 Svg {
514 width: f64,
516 height: f64,
518 #[serde(default, skip_serializing_if = "Option::is_none")]
520 view_box: Option<String>,
521 content: String,
523 },
524
525 Canvas {
527 width: f64,
529 height: f64,
531 operations: Vec<CanvasOp>,
533 },
534
535 Barcode {
537 data: String,
539 #[serde(default)]
541 format: crate::barcode::BarcodeFormat,
542 #[serde(default, skip_serializing_if = "Option::is_none")]
544 width: Option<f64>,
545 #[serde(default = "default_barcode_height")]
547 height: f64,
548 },
549
550 QrCode {
552 data: String,
554 #[serde(default, skip_serializing_if = "Option::is_none")]
557 size: Option<f64>,
558 },
559
560 BarChart {
562 data: Vec<ChartDataPoint>,
564 width: f64,
566 height: f64,
568 #[serde(default, skip_serializing_if = "Option::is_none")]
570 color: Option<String>,
571 #[serde(default = "default_true")]
573 show_labels: bool,
574 #[serde(default)]
576 show_values: bool,
577 #[serde(default)]
579 show_grid: bool,
580 #[serde(default, skip_serializing_if = "Option::is_none")]
582 title: Option<String>,
583 },
584
585 LineChart {
587 series: Vec<ChartSeries>,
589 labels: Vec<String>,
591 width: f64,
593 height: f64,
595 #[serde(default)]
597 show_points: bool,
598 #[serde(default)]
600 show_grid: bool,
601 #[serde(default, skip_serializing_if = "Option::is_none")]
603 title: Option<String>,
604 },
605
606 PieChart {
608 data: Vec<ChartDataPoint>,
610 width: f64,
612 height: f64,
614 #[serde(default)]
616 donut: bool,
617 #[serde(default)]
619 show_legend: bool,
620 #[serde(default, skip_serializing_if = "Option::is_none")]
622 title: Option<String>,
623 },
624
625 AreaChart {
627 series: Vec<ChartSeries>,
629 labels: Vec<String>,
631 width: f64,
633 height: f64,
635 #[serde(default)]
637 show_grid: bool,
638 #[serde(default, skip_serializing_if = "Option::is_none")]
640 title: Option<String>,
641 },
642
643 DotPlot {
645 groups: Vec<DotPlotGroup>,
647 width: f64,
649 height: f64,
651 #[serde(default, skip_serializing_if = "Option::is_none")]
653 x_min: Option<f64>,
654 #[serde(default, skip_serializing_if = "Option::is_none")]
656 x_max: Option<f64>,
657 #[serde(default, skip_serializing_if = "Option::is_none")]
659 y_min: Option<f64>,
660 #[serde(default, skip_serializing_if = "Option::is_none")]
662 y_max: Option<f64>,
663 #[serde(default, skip_serializing_if = "Option::is_none")]
665 x_label: Option<String>,
666 #[serde(default, skip_serializing_if = "Option::is_none")]
668 y_label: Option<String>,
669 #[serde(default)]
671 show_legend: bool,
672 #[serde(default = "default_dot_size")]
674 dot_size: f64,
675 },
676
677 Watermark {
679 text: String,
681 #[serde(default = "default_watermark_font_size")]
683 font_size: f64,
684 #[serde(default = "default_watermark_angle")]
686 angle: f64,
687 },
688
689 TextField {
691 name: String,
693 #[serde(default, skip_serializing_if = "Option::is_none")]
695 value: Option<String>,
696 #[serde(default, skip_serializing_if = "Option::is_none")]
698 placeholder: Option<String>,
699 width: f64,
701 #[serde(default = "default_form_field_height")]
703 height: f64,
704 #[serde(default)]
706 multiline: bool,
707 #[serde(default)]
709 password: bool,
710 #[serde(default)]
712 read_only: bool,
713 #[serde(default, skip_serializing_if = "Option::is_none")]
715 max_length: Option<u32>,
716 #[serde(default = "default_form_font_size")]
718 font_size: f64,
719 },
720
721 Checkbox {
723 name: String,
725 #[serde(default)]
727 checked: bool,
728 #[serde(default = "default_checkbox_size")]
730 width: f64,
731 #[serde(default = "default_checkbox_size")]
733 height: f64,
734 #[serde(default)]
736 read_only: bool,
737 },
738
739 Dropdown {
741 name: String,
743 options: Vec<String>,
745 #[serde(default, skip_serializing_if = "Option::is_none")]
747 value: Option<String>,
748 width: f64,
750 #[serde(default = "default_form_field_height")]
752 height: f64,
753 #[serde(default)]
755 read_only: bool,
756 #[serde(default = "default_form_font_size")]
758 font_size: f64,
759 },
760
761 RadioButton {
764 name: String,
766 value: String,
768 #[serde(default)]
770 checked: bool,
771 #[serde(default = "default_checkbox_size")]
773 width: f64,
774 #[serde(default = "default_checkbox_size")]
776 height: f64,
777 #[serde(default)]
779 read_only: bool,
780 },
781}
782
783#[derive(Debug, Clone, Serialize, Deserialize)]
785pub struct ChartDataPoint {
786 pub label: String,
787 pub value: f64,
788 #[serde(default, skip_serializing_if = "Option::is_none")]
789 pub color: Option<String>,
790}
791
792#[derive(Debug, Clone, Serialize, Deserialize)]
794pub struct ChartSeries {
795 pub name: String,
796 pub data: Vec<f64>,
797 #[serde(default, skip_serializing_if = "Option::is_none")]
798 pub color: Option<String>,
799}
800
801#[derive(Debug, Clone, Serialize, Deserialize)]
803pub struct DotPlotGroup {
804 pub name: String,
805 #[serde(default, skip_serializing_if = "Option::is_none")]
806 pub color: Option<String>,
807 pub data: Vec<(f64, f64)>,
808}
809
810#[derive(Debug, Clone, Serialize, Deserialize)]
812#[serde(tag = "op")]
813pub enum CanvasOp {
814 MoveTo {
815 x: f64,
816 y: f64,
817 },
818 LineTo {
819 x: f64,
820 y: f64,
821 },
822 BezierCurveTo {
823 cp1x: f64,
824 cp1y: f64,
825 cp2x: f64,
826 cp2y: f64,
827 x: f64,
828 y: f64,
829 },
830 QuadraticCurveTo {
831 cpx: f64,
832 cpy: f64,
833 x: f64,
834 y: f64,
835 },
836 ClosePath,
837 Rect {
838 x: f64,
839 y: f64,
840 width: f64,
841 height: f64,
842 },
843 Circle {
844 cx: f64,
845 cy: f64,
846 r: f64,
847 },
848 Ellipse {
849 cx: f64,
850 cy: f64,
851 rx: f64,
852 ry: f64,
853 },
854 Arc {
855 cx: f64,
856 cy: f64,
857 r: f64,
858 start_angle: f64,
859 end_angle: f64,
860 #[serde(default)]
861 counterclockwise: bool,
862 },
863 Stroke,
864 Fill,
865 FillAndStroke,
866 SetFillColor {
867 r: f64,
868 g: f64,
869 b: f64,
870 },
871 SetStrokeColor {
872 r: f64,
873 g: f64,
874 b: f64,
875 },
876 SetLineWidth {
877 width: f64,
878 },
879 SetLineCap {
880 cap: u32,
881 },
882 SetLineJoin {
883 join: u32,
884 },
885 Save,
886 Restore,
887}
888
889#[derive(Debug, Clone, Serialize, Deserialize)]
891#[serde(rename_all = "camelCase")]
892pub struct TextRun {
893 pub content: String,
894 #[serde(default)]
895 pub style: crate::style::Style,
896 #[serde(default, skip_serializing_if = "Option::is_none")]
897 pub href: Option<String>,
898}
899
900#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
902pub enum Position {
903 #[default]
904 Relative,
905 Absolute,
906}
907
908fn default_one() -> u32 {
909 1
910}
911
912fn default_barcode_height() -> f64 {
913 60.0
914}
915
916fn default_dot_size() -> f64 {
917 4.0
918}
919
920fn default_watermark_font_size() -> f64 {
921 60.0
922}
923
924fn default_watermark_angle() -> f64 {
925 -45.0
926}
927
928fn default_form_field_height() -> f64 {
929 24.0
930}
931
932fn default_form_font_size() -> f64 {
933 12.0
934}
935
936fn default_checkbox_size() -> f64 {
937 14.0
938}
939
940#[derive(Debug, Clone, Serialize, Deserialize)]
942pub struct ColumnDef {
943 pub width: ColumnWidth,
945}
946
947#[derive(Debug, Clone, Serialize, Deserialize)]
948pub enum ColumnWidth {
949 Fraction(f64),
951 Fixed(f64),
953 Auto,
955}
956
957#[derive(Debug, Clone, Serialize, Deserialize)]
959pub enum FixedPosition {
960 Header,
962 Footer,
964}
965
966#[derive(Debug, Clone, Serialize, Deserialize)]
968#[serde(rename_all = "camelCase")]
969pub struct SourceLocation {
970 pub file: String,
971 pub line: u32,
972 pub column: u32,
973}
974
975impl Node {
976 pub fn view(style: Style, children: Vec<Node>) -> Self {
978 Self {
979 kind: NodeKind::View,
980 style,
981 children,
982 id: None,
983 source_location: None,
984 bookmark: None,
985 href: None,
986 alt: None,
987 }
988 }
989
990 pub fn text(content: &str, style: Style) -> Self {
992 Self {
993 kind: NodeKind::Text {
994 content: content.to_string(),
995 href: None,
996 runs: vec![],
997 },
998 style,
999 children: vec![],
1000 id: None,
1001 source_location: None,
1002 bookmark: None,
1003 href: None,
1004 alt: None,
1005 }
1006 }
1007
1008 pub fn page(config: PageConfig, style: Style, children: Vec<Node>) -> Self {
1010 Self {
1011 kind: NodeKind::Page { config },
1012 style,
1013 children,
1014 id: None,
1015 source_location: None,
1016 bookmark: None,
1017 href: None,
1018 alt: None,
1019 }
1020 }
1021
1022 pub fn is_breakable(&self) -> bool {
1024 match &self.kind {
1025 NodeKind::View | NodeKind::Table { .. } | NodeKind::Text { .. } => {
1026 self.style.wrap.unwrap_or(true)
1027 }
1028 NodeKind::TableRow { .. } => true,
1029 NodeKind::Image { .. } => false,
1030 NodeKind::Svg { .. } => false,
1031 NodeKind::Canvas { .. } => false,
1032 NodeKind::Barcode { .. } => false,
1033 NodeKind::QrCode { .. } => false,
1034 NodeKind::BarChart { .. } => false,
1035 NodeKind::LineChart { .. } => false,
1036 NodeKind::PieChart { .. } => false,
1037 NodeKind::AreaChart { .. } => false,
1038 NodeKind::DotPlot { .. } => false,
1039 NodeKind::Watermark { .. } => false,
1040 NodeKind::TextField { .. } => false,
1041 NodeKind::Checkbox { .. } => false,
1042 NodeKind::Dropdown { .. } => false,
1043 NodeKind::RadioButton { .. } => false,
1044 NodeKind::PageBreak => false,
1045 NodeKind::Fixed { .. } => false,
1046 NodeKind::Page { .. } => true,
1047 NodeKind::TableCell { .. } => true,
1048 }
1049 }
1050}