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)]
192#[serde(rename_all = "camelCase")]
193pub struct PageConfig {
194 #[serde(default = "PageSize::default")]
196 pub size: PageSize,
197
198 #[serde(default)]
200 pub margin: Edges,
201
202 #[serde(default = "default_true")]
204 pub wrap: bool,
205
206 #[serde(default, skip_serializing_if = "Option::is_none")]
209 pub background_image: Option<String>,
210
211 #[serde(default, skip_serializing_if = "Option::is_none")]
213 pub background_opacity: Option<f64>,
214
215 #[serde(default, skip_serializing_if = "Option::is_none")]
217 pub background_size: Option<BackgroundSize>,
218
219 #[serde(default, skip_serializing_if = "Option::is_none")]
221 pub background_position: Option<BackgroundPosition>,
222}
223
224#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
226#[serde(rename_all = "kebab-case")]
227pub enum BackgroundSize {
228 #[default]
230 Fill,
231 Cover,
233 Contain,
235}
236
237#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
240#[serde(rename_all = "kebab-case")]
241pub enum BackgroundPosition {
242 Center,
243 #[default]
244 TopLeft,
245 TopRight,
246 BottomLeft,
247 BottomRight,
248}
249
250impl Default for PageConfig {
251 fn default() -> Self {
252 Self {
253 size: PageSize::A4,
254 margin: Edges::uniform(54.0), wrap: true,
256 background_image: None,
257 background_opacity: None,
258 background_size: None,
259 background_position: None,
260 }
261 }
262}
263
264fn default_true() -> bool {
265 true
266}
267
268#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
270pub enum PageSize {
271 #[default]
272 A4,
273 A3,
274 A5,
275 Letter,
276 Legal,
277 Tabloid,
278 Custom {
279 width: f64,
280 height: f64,
281 },
282}
283
284impl PageSize {
285 pub fn dimensions(&self) -> (f64, f64) {
287 match self {
288 PageSize::A4 => (595.28, 841.89),
289 PageSize::A3 => (841.89, 1190.55),
290 PageSize::A5 => (419.53, 595.28),
291 PageSize::Letter => (612.0, 792.0),
292 PageSize::Legal => (612.0, 1008.0),
293 PageSize::Tabloid => (792.0, 1224.0),
294 PageSize::Custom { width, height } => (*width, *height),
295 }
296 }
297}
298
299#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
301pub struct Edges {
302 pub top: f64,
303 pub right: f64,
304 pub bottom: f64,
305 pub left: f64,
306}
307
308#[derive(Debug, Clone, Copy, Serialize)]
310pub enum EdgeValue {
311 Pt(f64),
312 Auto,
313}
314
315impl Default for EdgeValue {
316 fn default() -> Self {
317 EdgeValue::Pt(0.0)
318 }
319}
320
321impl EdgeValue {
322 pub fn resolve(&self) -> f64 {
324 match self {
325 EdgeValue::Pt(v) => *v,
326 EdgeValue::Auto => 0.0,
327 }
328 }
329
330 pub fn is_auto(&self) -> bool {
332 matches!(self, EdgeValue::Auto)
333 }
334}
335
336impl<'de> Deserialize<'de> for EdgeValue {
337 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
338 where
339 D: Deserializer<'de>,
340 {
341 use serde::de;
342
343 struct EdgeValueVisitor;
344
345 impl<'de> de::Visitor<'de> for EdgeValueVisitor {
346 type Value = EdgeValue;
347
348 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
349 f.write_str("a number or the string \"auto\"")
350 }
351
352 fn visit_f64<E: de::Error>(self, v: f64) -> Result<EdgeValue, E> {
353 Ok(EdgeValue::Pt(v))
354 }
355
356 fn visit_i64<E: de::Error>(self, v: i64) -> Result<EdgeValue, E> {
357 Ok(EdgeValue::Pt(v as f64))
358 }
359
360 fn visit_u64<E: de::Error>(self, v: u64) -> Result<EdgeValue, E> {
361 Ok(EdgeValue::Pt(v as f64))
362 }
363
364 fn visit_str<E: de::Error>(self, v: &str) -> Result<EdgeValue, E> {
365 if v == "auto" {
366 Ok(EdgeValue::Auto)
367 } else {
368 Err(de::Error::invalid_value(de::Unexpected::Str(v), &self))
369 }
370 }
371 }
372
373 deserializer.deserialize_any(EdgeValueVisitor)
374 }
375}
376
377#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
379pub struct MarginEdges {
380 pub top: EdgeValue,
381 pub right: EdgeValue,
382 pub bottom: EdgeValue,
383 pub left: EdgeValue,
384}
385
386impl MarginEdges {
387 pub fn horizontal(&self) -> f64 {
389 self.left.resolve() + self.right.resolve()
390 }
391
392 pub fn vertical(&self) -> f64 {
394 self.top.resolve() + self.bottom.resolve()
395 }
396
397 pub fn has_auto_horizontal(&self) -> bool {
399 self.left.is_auto() || self.right.is_auto()
400 }
401
402 pub fn has_auto_vertical(&self) -> bool {
404 self.top.is_auto() || self.bottom.is_auto()
405 }
406
407 pub fn from_edges(e: Edges) -> Self {
409 MarginEdges {
410 top: EdgeValue::Pt(e.top),
411 right: EdgeValue::Pt(e.right),
412 bottom: EdgeValue::Pt(e.bottom),
413 left: EdgeValue::Pt(e.left),
414 }
415 }
416
417 pub fn to_edges(&self) -> Edges {
419 Edges {
420 top: self.top.resolve(),
421 right: self.right.resolve(),
422 bottom: self.bottom.resolve(),
423 left: self.left.resolve(),
424 }
425 }
426}
427
428impl Edges {
429 pub fn uniform(v: f64) -> Self {
430 Self {
431 top: v,
432 right: v,
433 bottom: v,
434 left: v,
435 }
436 }
437
438 pub fn symmetric(vertical: f64, horizontal: f64) -> Self {
439 Self {
440 top: vertical,
441 right: horizontal,
442 bottom: vertical,
443 left: horizontal,
444 }
445 }
446
447 pub fn horizontal(&self) -> f64 {
448 self.left + self.right
449 }
450
451 pub fn vertical(&self) -> f64 {
452 self.top + self.bottom
453 }
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
458#[serde(rename_all = "camelCase")]
459pub struct Node {
460 pub kind: NodeKind,
462
463 #[serde(default)]
465 pub style: Style,
466
467 #[serde(default)]
469 pub children: Vec<Node>,
470
471 #[serde(default)]
473 pub id: Option<String>,
474
475 #[serde(default, skip_serializing_if = "Option::is_none")]
477 pub source_location: Option<SourceLocation>,
478
479 #[serde(default, skip_serializing_if = "Option::is_none")]
481 pub bookmark: Option<String>,
482
483 #[serde(default, skip_serializing_if = "Option::is_none")]
485 pub href: Option<String>,
486
487 #[serde(default, skip_serializing_if = "Option::is_none")]
489 pub alt: Option<String>,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize)]
494#[serde(tag = "type")]
495pub enum NodeKind {
496 Page {
498 #[serde(default)]
499 config: PageConfig,
500 },
501
502 View,
504
505 Text {
507 content: String,
508 #[serde(default, skip_serializing_if = "Option::is_none")]
510 href: Option<String>,
511 #[serde(default, skip_serializing_if = "Vec::is_empty")]
513 runs: Vec<TextRun>,
514 },
515
516 Heading {
521 level: u8,
522 content: String,
523 #[serde(default, skip_serializing_if = "Option::is_none")]
524 href: Option<String>,
525 #[serde(default, skip_serializing_if = "Vec::is_empty")]
526 runs: Vec<TextRun>,
527 },
528
529 List {
532 ordered: bool,
534 marker_type: ListMarkerType,
536 #[serde(default = "default_list_start")]
539 start: u32,
540 },
541
542 ListItem,
544
545 Image {
547 src: String,
549 width: Option<f64>,
551 height: Option<f64>,
553 },
554
555 Table {
557 #[serde(default)]
559 columns: Vec<ColumnDef>,
560 },
561
562 TableRow {
564 #[serde(default)]
567 is_header: bool,
568 },
569
570 TableCell {
572 #[serde(default = "default_one")]
574 col_span: u32,
575 #[serde(default = "default_one")]
577 row_span: u32,
578 },
579
580 Fixed {
582 position: FixedPosition,
584 },
585
586 PageBreak,
588
589 Svg {
591 width: f64,
593 height: f64,
595 #[serde(default, skip_serializing_if = "Option::is_none")]
597 view_box: Option<String>,
598 content: String,
600 },
601
602 Canvas {
604 width: f64,
606 height: f64,
608 operations: Vec<CanvasOp>,
610 },
611
612 Barcode {
614 data: String,
616 #[serde(default)]
618 format: crate::barcode::BarcodeFormat,
619 #[serde(default, skip_serializing_if = "Option::is_none")]
621 width: Option<f64>,
622 #[serde(default = "default_barcode_height")]
624 height: f64,
625 },
626
627 QrCode {
629 data: String,
631 #[serde(default, skip_serializing_if = "Option::is_none")]
634 size: Option<f64>,
635 },
636
637 BarChart {
639 data: Vec<ChartDataPoint>,
641 width: f64,
643 height: f64,
645 #[serde(default, skip_serializing_if = "Option::is_none")]
647 color: Option<String>,
648 #[serde(default = "default_true")]
650 show_labels: bool,
651 #[serde(default)]
653 show_values: bool,
654 #[serde(default)]
656 show_grid: bool,
657 #[serde(default, skip_serializing_if = "Option::is_none")]
659 title: Option<String>,
660 },
661
662 LineChart {
664 series: Vec<ChartSeries>,
666 labels: Vec<String>,
668 width: f64,
670 height: f64,
672 #[serde(default)]
674 show_points: bool,
675 #[serde(default)]
677 show_grid: bool,
678 #[serde(default, skip_serializing_if = "Option::is_none")]
680 title: Option<String>,
681 },
682
683 PieChart {
685 data: Vec<ChartDataPoint>,
687 width: f64,
689 height: f64,
691 #[serde(default)]
693 donut: bool,
694 #[serde(default)]
696 show_legend: bool,
697 #[serde(default, skip_serializing_if = "Option::is_none")]
699 title: Option<String>,
700 },
701
702 AreaChart {
704 series: Vec<ChartSeries>,
706 labels: Vec<String>,
708 width: f64,
710 height: f64,
712 #[serde(default)]
714 show_grid: bool,
715 #[serde(default, skip_serializing_if = "Option::is_none")]
717 title: Option<String>,
718 },
719
720 DotPlot {
722 groups: Vec<DotPlotGroup>,
724 width: f64,
726 height: f64,
728 #[serde(default, skip_serializing_if = "Option::is_none")]
730 x_min: Option<f64>,
731 #[serde(default, skip_serializing_if = "Option::is_none")]
733 x_max: Option<f64>,
734 #[serde(default, skip_serializing_if = "Option::is_none")]
736 y_min: Option<f64>,
737 #[serde(default, skip_serializing_if = "Option::is_none")]
739 y_max: Option<f64>,
740 #[serde(default, skip_serializing_if = "Option::is_none")]
742 x_label: Option<String>,
743 #[serde(default, skip_serializing_if = "Option::is_none")]
745 y_label: Option<String>,
746 #[serde(default)]
748 show_legend: bool,
749 #[serde(default = "default_dot_size")]
751 dot_size: f64,
752 },
753
754 Watermark {
756 text: String,
758 #[serde(default = "default_watermark_font_size")]
760 font_size: f64,
761 #[serde(default = "default_watermark_angle")]
763 angle: f64,
764 },
765
766 TextField {
768 name: String,
770 #[serde(default, skip_serializing_if = "Option::is_none")]
772 value: Option<String>,
773 #[serde(default, skip_serializing_if = "Option::is_none")]
775 placeholder: Option<String>,
776 width: f64,
778 #[serde(default = "default_form_field_height")]
780 height: f64,
781 #[serde(default)]
783 multiline: bool,
784 #[serde(default)]
786 password: bool,
787 #[serde(default)]
789 read_only: bool,
790 #[serde(default, skip_serializing_if = "Option::is_none")]
792 max_length: Option<u32>,
793 #[serde(default = "default_form_font_size")]
795 font_size: f64,
796 },
797
798 Checkbox {
800 name: String,
802 #[serde(default)]
804 checked: bool,
805 #[serde(default = "default_checkbox_size")]
807 width: f64,
808 #[serde(default = "default_checkbox_size")]
810 height: f64,
811 #[serde(default)]
813 read_only: bool,
814 },
815
816 Dropdown {
818 name: String,
820 options: Vec<String>,
822 #[serde(default, skip_serializing_if = "Option::is_none")]
824 value: Option<String>,
825 width: f64,
827 #[serde(default = "default_form_field_height")]
829 height: f64,
830 #[serde(default)]
832 read_only: bool,
833 #[serde(default = "default_form_font_size")]
835 font_size: f64,
836 },
837
838 RadioButton {
841 name: String,
843 value: String,
845 #[serde(default)]
847 checked: bool,
848 #[serde(default = "default_checkbox_size")]
850 width: f64,
851 #[serde(default = "default_checkbox_size")]
853 height: f64,
854 #[serde(default)]
856 read_only: bool,
857 },
858}
859
860#[derive(Debug, Clone, Serialize, Deserialize)]
862pub struct ChartDataPoint {
863 pub label: String,
864 pub value: f64,
865 #[serde(default, skip_serializing_if = "Option::is_none")]
866 pub color: Option<String>,
867}
868
869#[derive(Debug, Clone, Serialize, Deserialize)]
871pub struct ChartSeries {
872 pub name: String,
873 pub data: Vec<f64>,
874 #[serde(default, skip_serializing_if = "Option::is_none")]
875 pub color: Option<String>,
876}
877
878#[derive(Debug, Clone, Serialize, Deserialize)]
880pub struct DotPlotGroup {
881 pub name: String,
882 #[serde(default, skip_serializing_if = "Option::is_none")]
883 pub color: Option<String>,
884 pub data: Vec<(f64, f64)>,
885}
886
887#[derive(Debug, Clone, Serialize, Deserialize)]
889#[serde(tag = "op")]
890pub enum CanvasOp {
891 MoveTo {
892 x: f64,
893 y: f64,
894 },
895 LineTo {
896 x: f64,
897 y: f64,
898 },
899 BezierCurveTo {
900 cp1x: f64,
901 cp1y: f64,
902 cp2x: f64,
903 cp2y: f64,
904 x: f64,
905 y: f64,
906 },
907 QuadraticCurveTo {
908 cpx: f64,
909 cpy: f64,
910 x: f64,
911 y: f64,
912 },
913 ClosePath,
914 Rect {
915 x: f64,
916 y: f64,
917 width: f64,
918 height: f64,
919 },
920 Circle {
921 cx: f64,
922 cy: f64,
923 r: f64,
924 },
925 Ellipse {
926 cx: f64,
927 cy: f64,
928 rx: f64,
929 ry: f64,
930 },
931 Arc {
932 cx: f64,
933 cy: f64,
934 r: f64,
935 start_angle: f64,
936 end_angle: f64,
937 #[serde(default)]
938 counterclockwise: bool,
939 },
940 Stroke,
941 Fill,
942 FillAndStroke,
943 SetFillColor {
944 r: f64,
945 g: f64,
946 b: f64,
947 },
948 SetStrokeColor {
949 r: f64,
950 g: f64,
951 b: f64,
952 },
953 SetLineWidth {
954 width: f64,
955 },
956 SetLineCap {
957 cap: u32,
958 },
959 SetLineJoin {
960 join: u32,
961 },
962 Save,
963 Restore,
964}
965
966#[derive(Debug, Clone, Serialize, Deserialize)]
968#[serde(rename_all = "camelCase")]
969pub struct TextRun {
970 pub content: String,
971 #[serde(default)]
972 pub style: crate::style::Style,
973 #[serde(default, skip_serializing_if = "Option::is_none")]
974 pub href: Option<String>,
975}
976
977#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
979pub enum Position {
980 #[default]
981 Relative,
982 Absolute,
983}
984
985fn default_one() -> u32 {
986 1
987}
988
989fn default_barcode_height() -> f64 {
990 60.0
991}
992
993fn default_dot_size() -> f64 {
994 4.0
995}
996
997fn default_watermark_font_size() -> f64 {
998 60.0
999}
1000
1001fn default_watermark_angle() -> f64 {
1002 -45.0
1003}
1004
1005fn default_form_field_height() -> f64 {
1006 24.0
1007}
1008
1009fn default_form_font_size() -> f64 {
1010 12.0
1011}
1012
1013fn default_checkbox_size() -> f64 {
1014 14.0
1015}
1016
1017#[derive(Debug, Clone, Serialize, Deserialize)]
1019pub struct ColumnDef {
1020 pub width: ColumnWidth,
1022}
1023
1024#[derive(Debug, Clone, Serialize, Deserialize)]
1025pub enum ColumnWidth {
1026 Fraction(f64),
1028 Fixed(f64),
1030 Auto,
1032}
1033
1034#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1039#[serde(rename_all = "camelCase")]
1040pub enum ListMarkerType {
1041 Disc,
1042 Circle,
1043 Square,
1044 None,
1045 Decimal,
1046 LowerAlpha,
1047 UpperAlpha,
1048 LowerRoman,
1049 UpperRoman,
1050}
1051
1052fn default_list_start() -> u32 {
1053 1
1054}
1055
1056#[derive(Debug, Clone, Serialize, Deserialize)]
1058pub enum FixedPosition {
1059 Header,
1061 Footer,
1063}
1064
1065#[derive(Debug, Clone, Serialize, Deserialize)]
1067#[serde(rename_all = "camelCase")]
1068pub struct SourceLocation {
1069 pub file: String,
1070 pub line: u32,
1071 pub column: u32,
1072}
1073
1074impl Node {
1075 pub fn view(style: Style, children: Vec<Node>) -> Self {
1077 Self {
1078 kind: NodeKind::View,
1079 style,
1080 children,
1081 id: None,
1082 source_location: None,
1083 bookmark: None,
1084 href: None,
1085 alt: None,
1086 }
1087 }
1088
1089 pub fn text(content: &str, style: Style) -> Self {
1091 Self {
1092 kind: NodeKind::Text {
1093 content: content.to_string(),
1094 href: None,
1095 runs: vec![],
1096 },
1097 style,
1098 children: vec![],
1099 id: None,
1100 source_location: None,
1101 bookmark: None,
1102 href: None,
1103 alt: None,
1104 }
1105 }
1106
1107 pub fn page(config: PageConfig, style: Style, children: Vec<Node>) -> Self {
1109 Self {
1110 kind: NodeKind::Page { config },
1111 style,
1112 children,
1113 id: None,
1114 source_location: None,
1115 bookmark: None,
1116 href: None,
1117 alt: None,
1118 }
1119 }
1120
1121 pub fn is_breakable(&self) -> bool {
1123 match &self.kind {
1124 NodeKind::View
1125 | NodeKind::Table { .. }
1126 | NodeKind::Text { .. }
1127 | NodeKind::Heading { .. }
1128 | NodeKind::List { .. }
1129 | NodeKind::ListItem => self.style.wrap.unwrap_or(true),
1130 NodeKind::TableRow { .. } => true,
1131 NodeKind::Image { .. } => false,
1132 NodeKind::Svg { .. } => false,
1133 NodeKind::Canvas { .. } => false,
1134 NodeKind::Barcode { .. } => false,
1135 NodeKind::QrCode { .. } => false,
1136 NodeKind::BarChart { .. } => false,
1137 NodeKind::LineChart { .. } => false,
1138 NodeKind::PieChart { .. } => false,
1139 NodeKind::AreaChart { .. } => false,
1140 NodeKind::DotPlot { .. } => false,
1141 NodeKind::Watermark { .. } => false,
1142 NodeKind::TextField { .. } => false,
1143 NodeKind::Checkbox { .. } => false,
1144 NodeKind::Dropdown { .. } => false,
1145 NodeKind::RadioButton { .. } => false,
1146 NodeKind::PageBreak => false,
1147 NodeKind::Fixed { .. } => false,
1148 NodeKind::Page { .. } => true,
1149 NodeKind::TableCell { .. } => true,
1150 }
1151 }
1152}