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 Image {
518 src: String,
520 width: Option<f64>,
522 height: Option<f64>,
524 },
525
526 Table {
528 #[serde(default)]
530 columns: Vec<ColumnDef>,
531 },
532
533 TableRow {
535 #[serde(default)]
538 is_header: bool,
539 },
540
541 TableCell {
543 #[serde(default = "default_one")]
545 col_span: u32,
546 #[serde(default = "default_one")]
548 row_span: u32,
549 },
550
551 Fixed {
553 position: FixedPosition,
555 },
556
557 PageBreak,
559
560 Svg {
562 width: f64,
564 height: f64,
566 #[serde(default, skip_serializing_if = "Option::is_none")]
568 view_box: Option<String>,
569 content: String,
571 },
572
573 Canvas {
575 width: f64,
577 height: f64,
579 operations: Vec<CanvasOp>,
581 },
582
583 Barcode {
585 data: String,
587 #[serde(default)]
589 format: crate::barcode::BarcodeFormat,
590 #[serde(default, skip_serializing_if = "Option::is_none")]
592 width: Option<f64>,
593 #[serde(default = "default_barcode_height")]
595 height: f64,
596 },
597
598 QrCode {
600 data: String,
602 #[serde(default, skip_serializing_if = "Option::is_none")]
605 size: Option<f64>,
606 },
607
608 BarChart {
610 data: Vec<ChartDataPoint>,
612 width: f64,
614 height: f64,
616 #[serde(default, skip_serializing_if = "Option::is_none")]
618 color: Option<String>,
619 #[serde(default = "default_true")]
621 show_labels: bool,
622 #[serde(default)]
624 show_values: bool,
625 #[serde(default)]
627 show_grid: bool,
628 #[serde(default, skip_serializing_if = "Option::is_none")]
630 title: Option<String>,
631 },
632
633 LineChart {
635 series: Vec<ChartSeries>,
637 labels: Vec<String>,
639 width: f64,
641 height: f64,
643 #[serde(default)]
645 show_points: bool,
646 #[serde(default)]
648 show_grid: bool,
649 #[serde(default, skip_serializing_if = "Option::is_none")]
651 title: Option<String>,
652 },
653
654 PieChart {
656 data: Vec<ChartDataPoint>,
658 width: f64,
660 height: f64,
662 #[serde(default)]
664 donut: bool,
665 #[serde(default)]
667 show_legend: bool,
668 #[serde(default, skip_serializing_if = "Option::is_none")]
670 title: Option<String>,
671 },
672
673 AreaChart {
675 series: Vec<ChartSeries>,
677 labels: Vec<String>,
679 width: f64,
681 height: f64,
683 #[serde(default)]
685 show_grid: bool,
686 #[serde(default, skip_serializing_if = "Option::is_none")]
688 title: Option<String>,
689 },
690
691 DotPlot {
693 groups: Vec<DotPlotGroup>,
695 width: f64,
697 height: f64,
699 #[serde(default, skip_serializing_if = "Option::is_none")]
701 x_min: Option<f64>,
702 #[serde(default, skip_serializing_if = "Option::is_none")]
704 x_max: Option<f64>,
705 #[serde(default, skip_serializing_if = "Option::is_none")]
707 y_min: Option<f64>,
708 #[serde(default, skip_serializing_if = "Option::is_none")]
710 y_max: Option<f64>,
711 #[serde(default, skip_serializing_if = "Option::is_none")]
713 x_label: Option<String>,
714 #[serde(default, skip_serializing_if = "Option::is_none")]
716 y_label: Option<String>,
717 #[serde(default)]
719 show_legend: bool,
720 #[serde(default = "default_dot_size")]
722 dot_size: f64,
723 },
724
725 Watermark {
727 text: String,
729 #[serde(default = "default_watermark_font_size")]
731 font_size: f64,
732 #[serde(default = "default_watermark_angle")]
734 angle: f64,
735 },
736
737 TextField {
739 name: String,
741 #[serde(default, skip_serializing_if = "Option::is_none")]
743 value: Option<String>,
744 #[serde(default, skip_serializing_if = "Option::is_none")]
746 placeholder: Option<String>,
747 width: f64,
749 #[serde(default = "default_form_field_height")]
751 height: f64,
752 #[serde(default)]
754 multiline: bool,
755 #[serde(default)]
757 password: bool,
758 #[serde(default)]
760 read_only: bool,
761 #[serde(default, skip_serializing_if = "Option::is_none")]
763 max_length: Option<u32>,
764 #[serde(default = "default_form_font_size")]
766 font_size: f64,
767 },
768
769 Checkbox {
771 name: String,
773 #[serde(default)]
775 checked: bool,
776 #[serde(default = "default_checkbox_size")]
778 width: f64,
779 #[serde(default = "default_checkbox_size")]
781 height: f64,
782 #[serde(default)]
784 read_only: bool,
785 },
786
787 Dropdown {
789 name: String,
791 options: Vec<String>,
793 #[serde(default, skip_serializing_if = "Option::is_none")]
795 value: Option<String>,
796 width: f64,
798 #[serde(default = "default_form_field_height")]
800 height: f64,
801 #[serde(default)]
803 read_only: bool,
804 #[serde(default = "default_form_font_size")]
806 font_size: f64,
807 },
808
809 RadioButton {
812 name: String,
814 value: String,
816 #[serde(default)]
818 checked: bool,
819 #[serde(default = "default_checkbox_size")]
821 width: f64,
822 #[serde(default = "default_checkbox_size")]
824 height: f64,
825 #[serde(default)]
827 read_only: bool,
828 },
829}
830
831#[derive(Debug, Clone, Serialize, Deserialize)]
833pub struct ChartDataPoint {
834 pub label: String,
835 pub value: f64,
836 #[serde(default, skip_serializing_if = "Option::is_none")]
837 pub color: Option<String>,
838}
839
840#[derive(Debug, Clone, Serialize, Deserialize)]
842pub struct ChartSeries {
843 pub name: String,
844 pub data: Vec<f64>,
845 #[serde(default, skip_serializing_if = "Option::is_none")]
846 pub color: Option<String>,
847}
848
849#[derive(Debug, Clone, Serialize, Deserialize)]
851pub struct DotPlotGroup {
852 pub name: String,
853 #[serde(default, skip_serializing_if = "Option::is_none")]
854 pub color: Option<String>,
855 pub data: Vec<(f64, f64)>,
856}
857
858#[derive(Debug, Clone, Serialize, Deserialize)]
860#[serde(tag = "op")]
861pub enum CanvasOp {
862 MoveTo {
863 x: f64,
864 y: f64,
865 },
866 LineTo {
867 x: f64,
868 y: f64,
869 },
870 BezierCurveTo {
871 cp1x: f64,
872 cp1y: f64,
873 cp2x: f64,
874 cp2y: f64,
875 x: f64,
876 y: f64,
877 },
878 QuadraticCurveTo {
879 cpx: f64,
880 cpy: f64,
881 x: f64,
882 y: f64,
883 },
884 ClosePath,
885 Rect {
886 x: f64,
887 y: f64,
888 width: f64,
889 height: f64,
890 },
891 Circle {
892 cx: f64,
893 cy: f64,
894 r: f64,
895 },
896 Ellipse {
897 cx: f64,
898 cy: f64,
899 rx: f64,
900 ry: f64,
901 },
902 Arc {
903 cx: f64,
904 cy: f64,
905 r: f64,
906 start_angle: f64,
907 end_angle: f64,
908 #[serde(default)]
909 counterclockwise: bool,
910 },
911 Stroke,
912 Fill,
913 FillAndStroke,
914 SetFillColor {
915 r: f64,
916 g: f64,
917 b: f64,
918 },
919 SetStrokeColor {
920 r: f64,
921 g: f64,
922 b: f64,
923 },
924 SetLineWidth {
925 width: f64,
926 },
927 SetLineCap {
928 cap: u32,
929 },
930 SetLineJoin {
931 join: u32,
932 },
933 Save,
934 Restore,
935}
936
937#[derive(Debug, Clone, Serialize, Deserialize)]
939#[serde(rename_all = "camelCase")]
940pub struct TextRun {
941 pub content: String,
942 #[serde(default)]
943 pub style: crate::style::Style,
944 #[serde(default, skip_serializing_if = "Option::is_none")]
945 pub href: Option<String>,
946}
947
948#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
950pub enum Position {
951 #[default]
952 Relative,
953 Absolute,
954}
955
956fn default_one() -> u32 {
957 1
958}
959
960fn default_barcode_height() -> f64 {
961 60.0
962}
963
964fn default_dot_size() -> f64 {
965 4.0
966}
967
968fn default_watermark_font_size() -> f64 {
969 60.0
970}
971
972fn default_watermark_angle() -> f64 {
973 -45.0
974}
975
976fn default_form_field_height() -> f64 {
977 24.0
978}
979
980fn default_form_font_size() -> f64 {
981 12.0
982}
983
984fn default_checkbox_size() -> f64 {
985 14.0
986}
987
988#[derive(Debug, Clone, Serialize, Deserialize)]
990pub struct ColumnDef {
991 pub width: ColumnWidth,
993}
994
995#[derive(Debug, Clone, Serialize, Deserialize)]
996pub enum ColumnWidth {
997 Fraction(f64),
999 Fixed(f64),
1001 Auto,
1003}
1004
1005#[derive(Debug, Clone, Serialize, Deserialize)]
1007pub enum FixedPosition {
1008 Header,
1010 Footer,
1012}
1013
1014#[derive(Debug, Clone, Serialize, Deserialize)]
1016#[serde(rename_all = "camelCase")]
1017pub struct SourceLocation {
1018 pub file: String,
1019 pub line: u32,
1020 pub column: u32,
1021}
1022
1023impl Node {
1024 pub fn view(style: Style, children: Vec<Node>) -> Self {
1026 Self {
1027 kind: NodeKind::View,
1028 style,
1029 children,
1030 id: None,
1031 source_location: None,
1032 bookmark: None,
1033 href: None,
1034 alt: None,
1035 }
1036 }
1037
1038 pub fn text(content: &str, style: Style) -> Self {
1040 Self {
1041 kind: NodeKind::Text {
1042 content: content.to_string(),
1043 href: None,
1044 runs: vec![],
1045 },
1046 style,
1047 children: vec![],
1048 id: None,
1049 source_location: None,
1050 bookmark: None,
1051 href: None,
1052 alt: None,
1053 }
1054 }
1055
1056 pub fn page(config: PageConfig, style: Style, children: Vec<Node>) -> Self {
1058 Self {
1059 kind: NodeKind::Page { config },
1060 style,
1061 children,
1062 id: None,
1063 source_location: None,
1064 bookmark: None,
1065 href: None,
1066 alt: None,
1067 }
1068 }
1069
1070 pub fn is_breakable(&self) -> bool {
1072 match &self.kind {
1073 NodeKind::View | NodeKind::Table { .. } | NodeKind::Text { .. } => {
1074 self.style.wrap.unwrap_or(true)
1075 }
1076 NodeKind::TableRow { .. } => true,
1077 NodeKind::Image { .. } => false,
1078 NodeKind::Svg { .. } => false,
1079 NodeKind::Canvas { .. } => false,
1080 NodeKind::Barcode { .. } => false,
1081 NodeKind::QrCode { .. } => false,
1082 NodeKind::BarChart { .. } => false,
1083 NodeKind::LineChart { .. } => false,
1084 NodeKind::PieChart { .. } => false,
1085 NodeKind::AreaChart { .. } => false,
1086 NodeKind::DotPlot { .. } => false,
1087 NodeKind::Watermark { .. } => false,
1088 NodeKind::TextField { .. } => false,
1089 NodeKind::Checkbox { .. } => false,
1090 NodeKind::Dropdown { .. } => false,
1091 NodeKind::RadioButton { .. } => false,
1092 NodeKind::PageBreak => false,
1093 NodeKind::Fixed { .. } => false,
1094 NodeKind::Page { .. } => true,
1095 NodeKind::TableCell { .. } => true,
1096 }
1097 }
1098}