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, skip_serializing_if = "Option::is_none")]
37 pub first_page: Option<PageConfig>,
38
39 #[serde(default)]
42 pub fonts: Vec<FontEntry>,
43
44 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub default_style: Option<crate::style::Style>,
48
49 #[serde(default = "default_true")]
56 pub tagged: bool,
57
58 #[serde(default)]
60 pub pdfa: Option<PdfAConformance>,
61
62 #[serde(default)]
64 pub pdf_ua: bool,
65
66 #[serde(default, skip_serializing_if = "Option::is_none")]
69 pub embedded_data: Option<String>,
70
71 #[serde(default)]
75 pub flatten_forms: bool,
76
77 #[serde(default, skip_serializing_if = "Option::is_none", alias = "signature")]
80 pub certification: Option<CertificationConfig>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85pub enum PdfAConformance {
86 #[serde(rename = "2a")]
88 A2a,
89 #[serde(rename = "2b")]
91 A2b,
92 #[serde(rename = "2u")]
94 A2u,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct RedactionRegion {
100 pub page: usize,
102 pub x: f64,
104 pub y: f64,
107 pub width: f64,
109 pub height: f64,
111 #[serde(default)]
113 pub color: Option<String>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub enum PatternType {
119 Literal,
121 Regex,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct RedactionPattern {
128 pub pattern: String,
130 pub pattern_type: PatternType,
132 #[serde(default)]
134 pub page: Option<usize>,
135 #[serde(default)]
137 pub color: Option<String>,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
142#[serde(rename_all = "camelCase")]
143pub struct CertificationConfig {
144 pub certificate_pem: String,
146 pub private_key_pem: String,
148 #[serde(default)]
150 pub reason: Option<String>,
151 #[serde(default)]
153 pub location: Option<String>,
154 #[serde(default)]
156 pub contact: Option<String>,
157 #[serde(default)]
159 pub visible: bool,
160 #[serde(default)]
162 pub x: Option<f64>,
163 #[serde(default)]
165 pub y: Option<f64>,
166 #[serde(default)]
168 pub width: Option<f64>,
169 #[serde(default)]
171 pub height: Option<f64>,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct FontEntry {
177 pub family: String,
179 pub src: String,
181 #[serde(default = "default_weight")]
183 pub weight: u32,
184 #[serde(default)]
186 pub italic: bool,
187}
188
189fn default_weight() -> u32 {
190 400
191}
192
193#[derive(Debug, Clone, Default, Serialize, Deserialize)]
195pub struct Metadata {
196 pub title: Option<String>,
197 pub author: Option<String>,
198 pub subject: Option<String>,
199 pub creator: Option<String>,
200 #[serde(default, skip_serializing_if = "Option::is_none")]
202 pub lang: Option<String>,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
207#[serde(rename_all = "camelCase")]
208pub struct PageConfig {
209 #[serde(default = "PageSize::default")]
211 pub size: PageSize,
212
213 #[serde(default)]
215 pub margin: Edges,
216
217 #[serde(default = "default_true")]
219 pub wrap: bool,
220
221 #[serde(default, skip_serializing_if = "Option::is_none")]
224 pub background_image: Option<String>,
225
226 #[serde(default, skip_serializing_if = "Option::is_none")]
228 pub background_opacity: Option<f64>,
229
230 #[serde(default, skip_serializing_if = "Option::is_none")]
232 pub background_size: Option<BackgroundSize>,
233
234 #[serde(default, skip_serializing_if = "Option::is_none")]
236 pub background_position: Option<BackgroundPosition>,
237}
238
239#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
241#[serde(rename_all = "kebab-case")]
242pub enum BackgroundSize {
243 #[default]
245 Fill,
246 Cover,
248 Contain,
250}
251
252#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
255#[serde(rename_all = "kebab-case")]
256pub enum BackgroundPosition {
257 Center,
258 #[default]
259 TopLeft,
260 TopRight,
261 BottomLeft,
262 BottomRight,
263}
264
265impl Default for PageConfig {
266 fn default() -> Self {
267 Self {
268 size: PageSize::A4,
269 margin: Edges::uniform(54.0), wrap: true,
271 background_image: None,
272 background_opacity: None,
273 background_size: None,
274 background_position: None,
275 }
276 }
277}
278
279fn default_true() -> bool {
280 true
281}
282
283#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
285pub enum PageSize {
286 #[default]
287 A4,
288 A3,
289 A5,
290 Letter,
291 Legal,
292 Tabloid,
293 Custom {
294 width: f64,
295 height: f64,
296 },
297}
298
299impl PageSize {
300 pub fn dimensions(&self) -> (f64, f64) {
302 match self {
303 PageSize::A4 => (595.28, 841.89),
304 PageSize::A3 => (841.89, 1190.55),
305 PageSize::A5 => (419.53, 595.28),
306 PageSize::Letter => (612.0, 792.0),
307 PageSize::Legal => (612.0, 1008.0),
308 PageSize::Tabloid => (792.0, 1224.0),
309 PageSize::Custom { width, height } => (*width, *height),
310 }
311 }
312}
313
314#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
316pub struct Edges {
317 pub top: f64,
318 pub right: f64,
319 pub bottom: f64,
320 pub left: f64,
321}
322
323#[derive(Debug, Clone, Copy, Serialize)]
325pub enum EdgeValue {
326 Pt(f64),
327 Auto,
328}
329
330impl Default for EdgeValue {
331 fn default() -> Self {
332 EdgeValue::Pt(0.0)
333 }
334}
335
336impl EdgeValue {
337 pub fn resolve(&self) -> f64 {
339 match self {
340 EdgeValue::Pt(v) => *v,
341 EdgeValue::Auto => 0.0,
342 }
343 }
344
345 pub fn is_auto(&self) -> bool {
347 matches!(self, EdgeValue::Auto)
348 }
349}
350
351impl<'de> Deserialize<'de> for EdgeValue {
352 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
353 where
354 D: Deserializer<'de>,
355 {
356 use serde::de;
357
358 struct EdgeValueVisitor;
359
360 impl<'de> de::Visitor<'de> for EdgeValueVisitor {
361 type Value = EdgeValue;
362
363 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
364 f.write_str("a number or the string \"auto\"")
365 }
366
367 fn visit_f64<E: de::Error>(self, v: f64) -> Result<EdgeValue, E> {
368 Ok(EdgeValue::Pt(v))
369 }
370
371 fn visit_i64<E: de::Error>(self, v: i64) -> Result<EdgeValue, E> {
372 Ok(EdgeValue::Pt(v as f64))
373 }
374
375 fn visit_u64<E: de::Error>(self, v: u64) -> Result<EdgeValue, E> {
376 Ok(EdgeValue::Pt(v as f64))
377 }
378
379 fn visit_str<E: de::Error>(self, v: &str) -> Result<EdgeValue, E> {
380 if v == "auto" {
381 Ok(EdgeValue::Auto)
382 } else {
383 Err(de::Error::invalid_value(de::Unexpected::Str(v), &self))
384 }
385 }
386 }
387
388 deserializer.deserialize_any(EdgeValueVisitor)
389 }
390}
391
392#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
394pub struct MarginEdges {
395 pub top: EdgeValue,
396 pub right: EdgeValue,
397 pub bottom: EdgeValue,
398 pub left: EdgeValue,
399}
400
401impl MarginEdges {
402 pub fn horizontal(&self) -> f64 {
404 self.left.resolve() + self.right.resolve()
405 }
406
407 pub fn vertical(&self) -> f64 {
409 self.top.resolve() + self.bottom.resolve()
410 }
411
412 pub fn has_auto_horizontal(&self) -> bool {
414 self.left.is_auto() || self.right.is_auto()
415 }
416
417 pub fn has_auto_vertical(&self) -> bool {
419 self.top.is_auto() || self.bottom.is_auto()
420 }
421
422 pub fn from_edges(e: Edges) -> Self {
424 MarginEdges {
425 top: EdgeValue::Pt(e.top),
426 right: EdgeValue::Pt(e.right),
427 bottom: EdgeValue::Pt(e.bottom),
428 left: EdgeValue::Pt(e.left),
429 }
430 }
431
432 pub fn to_edges(&self) -> Edges {
434 Edges {
435 top: self.top.resolve(),
436 right: self.right.resolve(),
437 bottom: self.bottom.resolve(),
438 left: self.left.resolve(),
439 }
440 }
441}
442
443impl Edges {
444 pub fn uniform(v: f64) -> Self {
445 Self {
446 top: v,
447 right: v,
448 bottom: v,
449 left: v,
450 }
451 }
452
453 pub fn symmetric(vertical: f64, horizontal: f64) -> Self {
454 Self {
455 top: vertical,
456 right: horizontal,
457 bottom: vertical,
458 left: horizontal,
459 }
460 }
461
462 pub fn horizontal(&self) -> f64 {
463 self.left + self.right
464 }
465
466 pub fn vertical(&self) -> f64 {
467 self.top + self.bottom
468 }
469}
470
471#[derive(Debug, Clone, Serialize, Deserialize)]
473#[serde(rename_all = "camelCase")]
474pub struct Node {
475 pub kind: NodeKind,
477
478 #[serde(default)]
480 pub style: Style,
481
482 #[serde(default)]
484 pub children: Vec<Node>,
485
486 #[serde(default)]
488 pub id: Option<String>,
489
490 #[serde(default, skip_serializing_if = "Option::is_none")]
492 pub source_location: Option<SourceLocation>,
493
494 #[serde(default, skip_serializing_if = "Option::is_none")]
496 pub bookmark: Option<String>,
497
498 #[serde(default, skip_serializing_if = "Option::is_none")]
500 pub href: Option<String>,
501
502 #[serde(default, skip_serializing_if = "Option::is_none")]
504 pub alt: Option<String>,
505}
506
507#[derive(Debug, Clone, Serialize, Deserialize)]
509#[serde(tag = "type")]
510pub enum NodeKind {
511 Page {
513 #[serde(default)]
514 config: PageConfig,
515 },
516
517 View,
519
520 Text {
522 content: String,
523 #[serde(default, skip_serializing_if = "Option::is_none")]
525 href: Option<String>,
526 #[serde(default, skip_serializing_if = "Vec::is_empty")]
528 runs: Vec<TextRun>,
529 },
530
531 Heading {
536 level: u8,
537 content: String,
538 #[serde(default, skip_serializing_if = "Option::is_none")]
539 href: Option<String>,
540 #[serde(default, skip_serializing_if = "Vec::is_empty")]
541 runs: Vec<TextRun>,
542 },
543
544 List {
547 ordered: bool,
549 marker_type: ListMarkerType,
551 #[serde(default = "default_list_start")]
554 start: u32,
555 },
556
557 ListItem,
559
560 Image {
562 src: String,
564 width: Option<f64>,
566 height: Option<f64>,
568 },
569
570 Table {
572 #[serde(default)]
574 columns: Vec<ColumnDef>,
575 },
576
577 TableRow {
579 #[serde(default)]
582 is_header: bool,
583 },
584
585 TableCell {
587 #[serde(default = "default_one")]
589 col_span: u32,
590 #[serde(default = "default_one")]
592 row_span: u32,
593 },
594
595 Fixed {
597 position: FixedPosition,
599 #[serde(default)]
602 pages: FixedPageFilter,
603 },
604
605 PageBreak,
607
608 Svg {
610 width: f64,
612 height: f64,
614 #[serde(default, skip_serializing_if = "Option::is_none")]
616 view_box: Option<String>,
617 content: String,
619 },
620
621 Canvas {
623 width: f64,
625 height: f64,
627 operations: Vec<CanvasOp>,
629 },
630
631 Barcode {
633 data: String,
635 #[serde(default)]
637 format: crate::barcode::BarcodeFormat,
638 #[serde(default, skip_serializing_if = "Option::is_none")]
640 width: Option<f64>,
641 #[serde(default = "default_barcode_height")]
643 height: f64,
644 },
645
646 QrCode {
648 data: String,
650 #[serde(default, skip_serializing_if = "Option::is_none")]
653 size: Option<f64>,
654 },
655
656 BarChart {
658 data: Vec<ChartDataPoint>,
660 width: f64,
662 height: f64,
664 #[serde(default, skip_serializing_if = "Option::is_none")]
666 color: Option<String>,
667 #[serde(default = "default_true")]
669 show_labels: bool,
670 #[serde(default)]
672 show_values: bool,
673 #[serde(default)]
675 show_grid: bool,
676 #[serde(default, skip_serializing_if = "Option::is_none")]
678 title: Option<String>,
679 },
680
681 LineChart {
683 series: Vec<ChartSeries>,
685 labels: Vec<String>,
687 width: f64,
689 height: f64,
691 #[serde(default)]
693 show_points: bool,
694 #[serde(default)]
696 show_grid: bool,
697 #[serde(default, skip_serializing_if = "Option::is_none")]
699 title: Option<String>,
700 },
701
702 PieChart {
704 data: Vec<ChartDataPoint>,
706 width: f64,
708 height: f64,
710 #[serde(default)]
712 donut: bool,
713 #[serde(default)]
715 show_legend: bool,
716 #[serde(default, skip_serializing_if = "Option::is_none")]
718 title: Option<String>,
719 },
720
721 AreaChart {
723 series: Vec<ChartSeries>,
725 labels: Vec<String>,
727 width: f64,
729 height: f64,
731 #[serde(default)]
733 show_grid: bool,
734 #[serde(default, skip_serializing_if = "Option::is_none")]
736 title: Option<String>,
737 },
738
739 DotPlot {
741 groups: Vec<DotPlotGroup>,
743 width: f64,
745 height: f64,
747 #[serde(default, skip_serializing_if = "Option::is_none")]
749 x_min: Option<f64>,
750 #[serde(default, skip_serializing_if = "Option::is_none")]
752 x_max: Option<f64>,
753 #[serde(default, skip_serializing_if = "Option::is_none")]
755 y_min: Option<f64>,
756 #[serde(default, skip_serializing_if = "Option::is_none")]
758 y_max: Option<f64>,
759 #[serde(default, skip_serializing_if = "Option::is_none")]
761 x_label: Option<String>,
762 #[serde(default, skip_serializing_if = "Option::is_none")]
764 y_label: Option<String>,
765 #[serde(default)]
767 show_legend: bool,
768 #[serde(default = "default_dot_size")]
770 dot_size: f64,
771 },
772
773 Watermark {
775 text: String,
777 #[serde(default = "default_watermark_font_size")]
779 font_size: f64,
780 #[serde(default = "default_watermark_angle")]
782 angle: f64,
783 },
784
785 TextField {
787 name: String,
789 #[serde(default, skip_serializing_if = "Option::is_none")]
791 value: Option<String>,
792 #[serde(default, skip_serializing_if = "Option::is_none")]
794 placeholder: Option<String>,
795 width: f64,
797 #[serde(default = "default_form_field_height")]
799 height: f64,
800 #[serde(default)]
802 multiline: bool,
803 #[serde(default)]
805 password: bool,
806 #[serde(default)]
808 read_only: bool,
809 #[serde(default, skip_serializing_if = "Option::is_none")]
811 max_length: Option<u32>,
812 #[serde(default = "default_form_font_size")]
814 font_size: f64,
815 },
816
817 Checkbox {
819 name: String,
821 #[serde(default)]
823 checked: bool,
824 #[serde(default = "default_checkbox_size")]
826 width: f64,
827 #[serde(default = "default_checkbox_size")]
829 height: f64,
830 #[serde(default)]
832 read_only: bool,
833 },
834
835 Dropdown {
837 name: String,
839 options: Vec<String>,
841 #[serde(default, skip_serializing_if = "Option::is_none")]
843 value: Option<String>,
844 width: f64,
846 #[serde(default = "default_form_field_height")]
848 height: f64,
849 #[serde(default)]
851 read_only: bool,
852 #[serde(default = "default_form_font_size")]
854 font_size: f64,
855 },
856
857 RadioButton {
860 name: String,
862 value: String,
864 #[serde(default)]
866 checked: bool,
867 #[serde(default = "default_checkbox_size")]
869 width: f64,
870 #[serde(default = "default_checkbox_size")]
872 height: f64,
873 #[serde(default)]
875 read_only: bool,
876 },
877}
878
879#[derive(Debug, Clone, Serialize, Deserialize)]
881pub struct ChartDataPoint {
882 pub label: String,
883 pub value: f64,
884 #[serde(default, skip_serializing_if = "Option::is_none")]
885 pub color: Option<String>,
886}
887
888#[derive(Debug, Clone, Serialize, Deserialize)]
890pub struct ChartSeries {
891 pub name: String,
892 pub data: Vec<f64>,
893 #[serde(default, skip_serializing_if = "Option::is_none")]
894 pub color: Option<String>,
895}
896
897#[derive(Debug, Clone, Serialize, Deserialize)]
899pub struct DotPlotGroup {
900 pub name: String,
901 #[serde(default, skip_serializing_if = "Option::is_none")]
902 pub color: Option<String>,
903 pub data: Vec<(f64, f64)>,
904}
905
906#[derive(Debug, Clone, Serialize, Deserialize)]
908#[serde(tag = "op")]
909pub enum CanvasOp {
910 MoveTo {
911 x: f64,
912 y: f64,
913 },
914 LineTo {
915 x: f64,
916 y: f64,
917 },
918 BezierCurveTo {
919 cp1x: f64,
920 cp1y: f64,
921 cp2x: f64,
922 cp2y: f64,
923 x: f64,
924 y: f64,
925 },
926 QuadraticCurveTo {
927 cpx: f64,
928 cpy: f64,
929 x: f64,
930 y: f64,
931 },
932 ClosePath,
933 Rect {
934 x: f64,
935 y: f64,
936 width: f64,
937 height: f64,
938 },
939 Circle {
940 cx: f64,
941 cy: f64,
942 r: f64,
943 },
944 Ellipse {
945 cx: f64,
946 cy: f64,
947 rx: f64,
948 ry: f64,
949 },
950 Arc {
951 cx: f64,
952 cy: f64,
953 r: f64,
954 start_angle: f64,
955 end_angle: f64,
956 #[serde(default)]
957 counterclockwise: bool,
958 },
959 Stroke,
960 Fill,
961 FillAndStroke,
962 SetFillColor {
963 r: f64,
964 g: f64,
965 b: f64,
966 },
967 SetStrokeColor {
968 r: f64,
969 g: f64,
970 b: f64,
971 },
972 SetLineWidth {
973 width: f64,
974 },
975 SetLineCap {
976 cap: u32,
977 },
978 SetLineJoin {
979 join: u32,
980 },
981 Save,
982 Restore,
983}
984
985#[derive(Debug, Clone, Serialize, Deserialize)]
987#[serde(rename_all = "camelCase")]
988pub struct TextRun {
989 pub content: String,
990 #[serde(default)]
991 pub style: crate::style::Style,
992 #[serde(default, skip_serializing_if = "Option::is_none")]
993 pub href: Option<String>,
994}
995
996#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
998pub enum Position {
999 #[default]
1000 Relative,
1001 Absolute,
1002}
1003
1004fn default_one() -> u32 {
1005 1
1006}
1007
1008fn default_barcode_height() -> f64 {
1009 60.0
1010}
1011
1012fn default_dot_size() -> f64 {
1013 4.0
1014}
1015
1016fn default_watermark_font_size() -> f64 {
1017 60.0
1018}
1019
1020fn default_watermark_angle() -> f64 {
1021 -45.0
1022}
1023
1024fn default_form_field_height() -> f64 {
1025 24.0
1026}
1027
1028fn default_form_font_size() -> f64 {
1029 12.0
1030}
1031
1032fn default_checkbox_size() -> f64 {
1033 14.0
1034}
1035
1036#[derive(Debug, Clone, Serialize, Deserialize)]
1038pub struct ColumnDef {
1039 pub width: ColumnWidth,
1041}
1042
1043#[derive(Debug, Clone, Serialize, Deserialize)]
1044pub enum ColumnWidth {
1045 Fraction(f64),
1047 Fixed(f64),
1049 Auto,
1051}
1052
1053#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1058#[serde(rename_all = "camelCase")]
1059pub enum ListMarkerType {
1060 Disc,
1061 Circle,
1062 Square,
1063 None,
1064 Decimal,
1065 LowerAlpha,
1066 UpperAlpha,
1067 LowerRoman,
1068 UpperRoman,
1069}
1070
1071fn default_list_start() -> u32 {
1072 1
1073}
1074
1075#[derive(Debug, Clone, Serialize, Deserialize)]
1077pub enum FixedPosition {
1078 Header,
1080 Footer,
1082}
1083
1084#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
1086pub enum FixedPageFilter {
1087 #[default]
1089 All,
1090 First,
1092 NotFirst,
1094}
1095
1096impl FixedPageFilter {
1097 pub fn applies(self, page_index: usize) -> bool {
1100 match self {
1101 FixedPageFilter::All => true,
1102 FixedPageFilter::First => page_index == 0,
1103 FixedPageFilter::NotFirst => page_index > 0,
1104 }
1105 }
1106}
1107
1108#[derive(Debug, Clone, Serialize, Deserialize)]
1110#[serde(rename_all = "camelCase")]
1111pub struct SourceLocation {
1112 pub file: String,
1113 pub line: u32,
1114 pub column: u32,
1115}
1116
1117impl Node {
1118 pub fn view(style: Style, children: Vec<Node>) -> Self {
1120 Self {
1121 kind: NodeKind::View,
1122 style,
1123 children,
1124 id: None,
1125 source_location: None,
1126 bookmark: None,
1127 href: None,
1128 alt: None,
1129 }
1130 }
1131
1132 pub fn text(content: &str, style: Style) -> Self {
1134 Self {
1135 kind: NodeKind::Text {
1136 content: content.to_string(),
1137 href: None,
1138 runs: vec![],
1139 },
1140 style,
1141 children: vec![],
1142 id: None,
1143 source_location: None,
1144 bookmark: None,
1145 href: None,
1146 alt: None,
1147 }
1148 }
1149
1150 pub fn page(config: PageConfig, style: Style, children: Vec<Node>) -> Self {
1152 Self {
1153 kind: NodeKind::Page { config },
1154 style,
1155 children,
1156 id: None,
1157 source_location: None,
1158 bookmark: None,
1159 href: None,
1160 alt: None,
1161 }
1162 }
1163
1164 pub fn is_breakable(&self) -> bool {
1166 match &self.kind {
1167 NodeKind::View
1168 | NodeKind::Table { .. }
1169 | NodeKind::Text { .. }
1170 | NodeKind::Heading { .. }
1171 | NodeKind::List { .. }
1172 | NodeKind::ListItem => self.style.wrap.unwrap_or(true),
1173 NodeKind::TableRow { .. } => true,
1174 NodeKind::Image { .. } => false,
1175 NodeKind::Svg { .. } => false,
1176 NodeKind::Canvas { .. } => false,
1177 NodeKind::Barcode { .. } => false,
1178 NodeKind::QrCode { .. } => false,
1179 NodeKind::BarChart { .. } => false,
1180 NodeKind::LineChart { .. } => false,
1181 NodeKind::PieChart { .. } => false,
1182 NodeKind::AreaChart { .. } => false,
1183 NodeKind::DotPlot { .. } => false,
1184 NodeKind::Watermark { .. } => false,
1185 NodeKind::TextField { .. } => false,
1186 NodeKind::Checkbox { .. } => false,
1187 NodeKind::Dropdown { .. } => false,
1188 NodeKind::RadioButton { .. } => false,
1189 NodeKind::PageBreak => false,
1190 NodeKind::Fixed { .. } => false,
1191 NodeKind::Page { .. } => true,
1192 NodeKind::TableCell { .. } => true,
1193 }
1194 }
1195}