1use hwpforge_foundation::{
25 ApplyPageType, HwpUnit, NumberFormatType, PageNumberPosition, ShowMode, TextDirection,
26};
27use schemars::JsonSchema;
28use serde::{Deserialize, Serialize};
29
30use crate::column::ColumnSettings;
31use crate::page::PageSettings;
32use crate::paragraph::Paragraph;
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
43pub struct Visibility {
44 #[serde(default)]
46 pub hide_first_header: bool,
47 #[serde(default)]
49 pub hide_first_footer: bool,
50 #[serde(default)]
52 pub hide_first_master_page: bool,
53 #[serde(default)]
55 pub hide_first_page_num: bool,
56 #[serde(default)]
58 pub hide_first_empty_line: bool,
59 #[serde(default)]
61 pub show_line_number: bool,
62 #[serde(default)]
64 pub border: ShowMode,
65 #[serde(default)]
67 pub fill: ShowMode,
68}
69
70impl Default for Visibility {
71 fn default() -> Self {
72 Self {
73 hide_first_header: false,
74 hide_first_footer: false,
75 hide_first_master_page: false,
76 hide_first_page_num: false,
77 hide_first_empty_line: false,
78 show_line_number: false,
79 border: ShowMode::ShowAll,
80 fill: ShowMode::ShowAll,
81 }
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
93pub struct LineNumberShape {
94 #[serde(default)]
96 pub restart_type: u8,
97 #[serde(default)]
99 pub count_by: u16,
100 #[serde(default)]
102 pub distance: HwpUnit,
103 #[serde(default)]
105 pub start_number: u32,
106}
107
108#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
117pub struct PageBorderFillEntry {
118 pub apply_type: String,
120 #[serde(default = "PageBorderFillEntry::default_border_fill_id")]
122 pub border_fill_id: u32,
123 #[serde(default = "PageBorderFillEntry::default_text_border")]
125 pub text_border: String,
126 #[serde(default)]
128 pub header_inside: bool,
129 #[serde(default)]
131 pub footer_inside: bool,
132 #[serde(default = "PageBorderFillEntry::default_fill_area")]
134 pub fill_area: String,
135 #[serde(default = "PageBorderFillEntry::default_offset")]
137 pub offset: [HwpUnit; 4],
138}
139
140impl PageBorderFillEntry {
141 fn default_border_fill_id() -> u32 {
142 1
143 }
144 fn default_text_border() -> String {
145 "PAPER".to_string()
146 }
147 fn default_fill_area() -> String {
148 "PAPER".to_string()
149 }
150 fn default_offset() -> [HwpUnit; 4] {
151 [
153 HwpUnit::new(1417).unwrap(),
154 HwpUnit::new(1417).unwrap(),
155 HwpUnit::new(1417).unwrap(),
156 HwpUnit::new(1417).unwrap(),
157 ]
158 }
159}
160
161impl Default for PageBorderFillEntry {
162 fn default() -> Self {
163 Self {
164 apply_type: "BOTH".to_string(),
165 border_fill_id: 1,
166 text_border: "PAPER".to_string(),
167 header_inside: false,
168 footer_inside: false,
169 fill_area: "PAPER".to_string(),
170 offset: Self::default_offset(),
171 }
172 }
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
185pub enum PageStartsOn {
186 #[default]
188 Both,
189 Even,
191 Odd,
193}
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
200pub struct BeginNum {
201 #[serde(default = "BeginNum::one")]
207 pub page: u32,
208 #[serde(default = "BeginNum::one")]
210 pub footnote: u32,
211 #[serde(default = "BeginNum::one")]
213 pub endnote: u32,
214 #[serde(default = "BeginNum::one")]
216 pub pic: u32,
217 #[serde(default = "BeginNum::one")]
219 pub tbl: u32,
220 #[serde(default = "BeginNum::one")]
222 pub equation: u32,
223 #[serde(default, skip_serializing_if = "PageStartsOn::is_default")]
225 pub page_starts_on: PageStartsOn,
226}
227
228impl PageStartsOn {
229 fn is_default(&self) -> bool {
231 *self == Self::Both
232 }
233}
234
235impl BeginNum {
236 fn one() -> u32 {
237 1
238 }
239}
240
241impl Default for BeginNum {
242 fn default() -> Self {
243 Self {
244 page: 1,
245 footnote: 1,
246 endnote: 1,
247 pic: 1,
248 tbl: 1,
249 equation: 1,
250 page_starts_on: PageStartsOn::Both,
251 }
252 }
253}
254
255#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
267pub struct MasterPage {
268 pub apply_page_type: ApplyPageType,
270 pub paragraphs: Vec<Paragraph>,
272}
273
274impl MasterPage {
275 pub fn new(apply_page_type: ApplyPageType, paragraphs: Vec<Paragraph>) -> Self {
277 Self { apply_page_type, paragraphs }
278 }
279}
280
281impl std::fmt::Display for MasterPage {
282 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
283 let n = self.paragraphs.len();
284 let word = if n == 1 { "paragraph" } else { "paragraphs" };
285 write!(f, "MasterPage({n} {word}, {:?})", self.apply_page_type)
286 }
287}
288
289#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
314pub struct HeaderFooter {
315 pub paragraphs: Vec<Paragraph>,
317 pub apply_page_type: ApplyPageType,
319 #[serde(default)]
324 pub vert_align: hwpforge_foundation::VerticalAlign,
325 #[serde(default, skip_serializing_if = "HwpUnit::is_zero")]
327 pub text_width: HwpUnit,
328 #[serde(default, skip_serializing_if = "HwpUnit::is_zero")]
333 pub text_height: HwpUnit,
334}
335
336impl HeaderFooter {
337 pub fn new(paragraphs: Vec<Paragraph>, apply_page_type: ApplyPageType) -> Self {
342 Self {
343 paragraphs,
344 apply_page_type,
345 vert_align: hwpforge_foundation::VerticalAlign::default(),
346 text_width: HwpUnit::ZERO,
347 text_height: HwpUnit::ZERO,
348 }
349 }
350
351 pub fn all_pages(paragraphs: Vec<Paragraph>) -> Self {
368 Self::new(paragraphs, ApplyPageType::Both)
369 }
370
371 #[deprecated(since = "0.2.0", note = "Use `all_pages()` instead")]
373 pub fn both(paragraphs: Vec<Paragraph>) -> Self {
374 Self::all_pages(paragraphs)
375 }
376}
377
378impl std::fmt::Display for HeaderFooter {
379 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
380 let n = self.paragraphs.len();
381 let word = if n == 1 { "paragraph" } else { "paragraphs" };
382 write!(f, "HeaderFooter({n} {word}, {:?})", self.apply_page_type)
383 }
384}
385
386#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
408pub struct PageNumber {
409 pub position: PageNumberPosition,
411 pub number_format: NumberFormatType,
413 pub decoration: String,
416}
417
418impl PageNumber {
419 pub fn new(position: PageNumberPosition, number_format: NumberFormatType) -> Self {
421 Self { position, number_format, decoration: String::new() }
422 }
423
424 pub fn bottom_center() -> Self {
442 Self {
443 position: PageNumberPosition::BottomCenter,
444 number_format: NumberFormatType::Digit,
445 decoration: String::new(),
446 }
447 }
448
449 pub fn with_decoration(
465 position: PageNumberPosition,
466 number_format: NumberFormatType,
467 decoration: impl Into<String>,
468 ) -> Self {
469 Self { position, number_format, decoration: decoration.into() }
470 }
471
472 #[deprecated(since = "0.2.0", note = "Use `with_decoration()` instead")]
474 pub fn with_side_char(
475 position: PageNumberPosition,
476 number_format: NumberFormatType,
477 side_char: impl Into<String>,
478 ) -> Self {
479 Self::with_decoration(position, number_format, side_char)
480 }
481}
482
483impl std::fmt::Display for PageNumber {
484 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
485 write!(f, "PageNumber({:?}, {:?})", self.position, self.number_format)
486 }
487}
488
489#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
510pub struct Section {
511 pub paragraphs: Vec<Paragraph>,
513 pub page_settings: PageSettings,
515 #[serde(default, skip_serializing_if = "Vec::is_empty")]
524 pub headers: Vec<HeaderFooter>,
525 #[serde(default, skip_serializing_if = "Vec::is_empty")]
528 pub footers: Vec<HeaderFooter>,
529 #[serde(default, skip_serializing_if = "Option::is_none")]
531 pub page_number: Option<PageNumber>,
532 #[serde(default, skip_serializing_if = "Option::is_none")]
534 pub column_settings: Option<ColumnSettings>,
535 #[serde(default, skip_serializing_if = "Option::is_none")]
538 pub visibility: Option<Visibility>,
539 #[serde(default, skip_serializing_if = "Option::is_none")]
541 pub line_number_shape: Option<LineNumberShape>,
542 #[serde(default, skip_serializing_if = "Option::is_none")]
544 pub page_border_fills: Option<Vec<PageBorderFillEntry>>,
545 #[serde(default, skip_serializing_if = "Option::is_none")]
548 pub master_pages: Option<Vec<MasterPage>>,
549 #[serde(default, skip_serializing_if = "Option::is_none")]
552 pub begin_num: Option<BeginNum>,
553 #[serde(default)]
556 pub text_direction: TextDirection,
557}
558
559impl Section {
560 pub fn for_each_paragraph_mut<F: FnMut(&mut Paragraph)>(&mut self, mut f: F) {
567 self.walk_paragraphs_mut(&mut f);
568 }
569
570 pub(crate) fn walk_paragraphs_mut(&mut self, f: &mut dyn FnMut(&mut Paragraph)) {
572 for p in &mut self.paragraphs {
573 p.walk_paragraphs_mut(f);
574 }
575 for hf in self.headers.iter_mut().chain(self.footers.iter_mut()) {
576 for p in &mut hf.paragraphs {
577 p.walk_paragraphs_mut(f);
578 }
579 }
580 if let Some(master_pages) = &mut self.master_pages {
581 for mp in master_pages {
582 for p in &mut mp.paragraphs {
583 p.walk_paragraphs_mut(f);
584 }
585 }
586 }
587 }
588
589 pub fn for_each_paragraph<F: FnMut(&Paragraph)>(&self, mut f: F) {
592 self.walk_paragraphs(&mut f);
593 }
594
595 pub(crate) fn walk_paragraphs(&self, f: &mut dyn FnMut(&Paragraph)) {
600 for p in &self.paragraphs {
601 p.walk_paragraphs(f);
602 }
603 for hf in self.headers.iter().chain(self.footers.iter()) {
604 for p in &hf.paragraphs {
605 p.walk_paragraphs(f);
606 }
607 }
608 if let Some(master_pages) = &self.master_pages {
609 for mp in master_pages {
610 for p in &mp.paragraphs {
611 p.walk_paragraphs(f);
612 }
613 }
614 }
615 }
616
617 pub fn new(page_settings: PageSettings) -> Self {
629 Self {
630 paragraphs: Vec::new(),
631 page_settings,
632 headers: Vec::new(),
633 footers: Vec::new(),
634 page_number: None,
635 column_settings: None,
636 visibility: None,
637 line_number_shape: None,
638 page_border_fills: None,
639 master_pages: None,
640 begin_num: None,
641 text_direction: TextDirection::Horizontal,
642 }
643 }
644
645 pub fn with_paragraphs(paragraphs: Vec<Paragraph>, page_settings: PageSettings) -> Self {
662 Self {
663 paragraphs,
664 page_settings,
665 headers: Vec::new(),
666 footers: Vec::new(),
667 page_number: None,
668 column_settings: None,
669 visibility: None,
670 line_number_shape: None,
671 page_border_fills: None,
672 master_pages: None,
673 begin_num: None,
674 text_direction: TextDirection::Horizontal,
675 }
676 }
677
678 pub fn with_text_direction(mut self, dir: TextDirection) -> Self {
692 self.text_direction = dir;
693 self
694 }
695
696 pub fn add_paragraph(&mut self, paragraph: Paragraph) {
698 self.paragraphs.push(paragraph);
699 }
700
701 pub fn paragraph_count(&self) -> usize {
703 self.paragraphs.len()
704 }
705
706 pub fn is_empty(&self) -> bool {
708 self.paragraphs.is_empty()
709 }
710
711 pub fn content_counts(&self) -> ContentCounts {
728 let mut tables: usize = 0;
729 let mut images: usize = 0;
730 let mut charts: usize = 0;
731
732 for para in &self.paragraphs {
733 for run in ¶.runs {
734 match &run.content {
735 crate::RunContent::Table(_) => tables += 1,
736 crate::RunContent::Image(_) => images += 1,
737 crate::RunContent::Control(c) => {
738 if matches!(**c, crate::control::Control::Chart { .. }) {
739 charts += 1;
740 }
741 }
742 _ => {}
743 }
744 }
745 }
746
747 ContentCounts { tables, images, charts }
748 }
749}
750
751#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
753pub struct ContentCounts {
754 pub tables: usize,
756 pub images: usize,
758 pub charts: usize,
760}
761
762impl std::fmt::Display for Section {
763 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
764 let n = self.paragraphs.len();
765 let word = if n == 1 { "paragraph" } else { "paragraphs" };
766 write!(f, "Section({n} {word})")
767 }
768}
769
770#[cfg(test)]
771mod tests {
772 use super::*;
773 use crate::run::Run;
774 use hwpforge_foundation::{
775 ApplyPageType, CharShapeIndex, NumberFormatType, PageNumberPosition, ParaShapeIndex,
776 };
777
778 fn simple_paragraph() -> Paragraph {
779 Paragraph::with_runs(
780 vec![Run::text("text", CharShapeIndex::new(0))],
781 ParaShapeIndex::new(0),
782 )
783 }
784
785 #[test]
786 fn new_is_empty() {
787 let section = Section::new(PageSettings::a4());
788 assert!(section.is_empty());
789 assert_eq!(section.paragraph_count(), 0);
790 }
791
792 #[test]
793 fn with_paragraphs() {
794 let section = Section::with_paragraphs(
795 vec![simple_paragraph(), simple_paragraph()],
796 PageSettings::a4(),
797 );
798 assert_eq!(section.paragraph_count(), 2);
799 assert!(!section.is_empty());
800 }
801
802 #[test]
803 fn add_paragraph() {
804 let mut section = Section::new(PageSettings::a4());
805 section.add_paragraph(simple_paragraph());
806 section.add_paragraph(simple_paragraph());
807 assert_eq!(section.paragraph_count(), 2);
808 }
809
810 #[test]
811 fn page_settings_preserved() {
812 let section = Section::new(PageSettings::letter());
813 assert_eq!(section.page_settings, PageSettings::letter());
814 }
815
816 #[test]
817 fn display_singular() {
818 let section = Section::with_paragraphs(vec![simple_paragraph()], PageSettings::a4());
819 assert_eq!(section.to_string(), "Section(1 paragraph)");
820 }
821
822 #[test]
823 fn display_plural() {
824 let section = Section::with_paragraphs(
825 vec![simple_paragraph(), simple_paragraph()],
826 PageSettings::a4(),
827 );
828 assert_eq!(section.to_string(), "Section(2 paragraphs)");
829 }
830
831 #[test]
832 fn equality() {
833 let a = Section::with_paragraphs(vec![simple_paragraph()], PageSettings::a4());
834 let b = Section::with_paragraphs(vec![simple_paragraph()], PageSettings::a4());
835 assert_eq!(a, b);
836 }
837
838 #[test]
839 fn inequality_different_page_settings() {
840 let a = Section::new(PageSettings::a4());
841 let b = Section::new(PageSettings::letter());
842 assert_ne!(a, b);
843 }
844
845 #[test]
846 fn clone_independence() {
847 let section = Section::with_paragraphs(vec![simple_paragraph()], PageSettings::a4());
848 let mut cloned = section.clone();
849 cloned.add_paragraph(simple_paragraph());
850 assert_eq!(section.paragraph_count(), 1);
851 assert_eq!(cloned.paragraph_count(), 2);
852 }
853
854 #[test]
855 fn serde_roundtrip() {
856 let section = Section::with_paragraphs(vec![simple_paragraph()], PageSettings::a4());
857 let json = serde_json::to_string(§ion).unwrap();
858 let back: Section = serde_json::from_str(&json).unwrap();
859 assert_eq!(section, back);
860 }
861
862 #[test]
863 fn serde_empty_section() {
864 let section = Section::new(PageSettings::a4());
865 let json = serde_json::to_string(§ion).unwrap();
866 let back: Section = serde_json::from_str(&json).unwrap();
867 assert_eq!(section, back);
868 }
869
870 #[test]
871 fn serde_letter_page() {
872 let section = Section::new(PageSettings::letter());
873 let json = serde_json::to_string(§ion).unwrap();
874 let back: Section = serde_json::from_str(&json).unwrap();
875 assert_eq!(section, back);
876 }
877
878 #[test]
883 fn header_footer_new() {
884 let hf =
885 HeaderFooter::new(vec![Paragraph::new(ParaShapeIndex::new(0))], ApplyPageType::Both);
886 assert_eq!(hf.paragraphs.len(), 1);
887 assert_eq!(hf.apply_page_type, ApplyPageType::Both);
888 }
889
890 #[test]
891 fn header_footer_even_odd() {
892 let even = HeaderFooter::new(vec![], ApplyPageType::Even);
893 let odd = HeaderFooter::new(vec![], ApplyPageType::Odd);
894 assert_eq!(even.apply_page_type, ApplyPageType::Even);
895 assert_eq!(odd.apply_page_type, ApplyPageType::Odd);
896 assert_ne!(even, odd);
897 }
898
899 #[test]
900 fn header_footer_display() {
901 let hf =
902 HeaderFooter::new(vec![Paragraph::new(ParaShapeIndex::new(0))], ApplyPageType::Both);
903 let s = hf.to_string();
904 assert!(s.contains("1 paragraph"), "display: {s}");
905 assert!(s.contains("Both"), "display: {s}");
906 }
907
908 #[test]
909 fn header_footer_serde_roundtrip() {
910 let hf = HeaderFooter::new(
911 vec![Paragraph::with_runs(
912 vec![Run::text("Header text", CharShapeIndex::new(0))],
913 ParaShapeIndex::new(0),
914 )],
915 ApplyPageType::Both,
916 );
917 let json = serde_json::to_string(&hf).unwrap();
918 let back: HeaderFooter = serde_json::from_str(&json).unwrap();
919 assert_eq!(hf, back);
920 }
921
922 #[test]
923 fn header_footer_clone_independence() {
924 let hf =
925 HeaderFooter::new(vec![Paragraph::new(ParaShapeIndex::new(0))], ApplyPageType::Both);
926 let mut cloned = hf.clone();
927 cloned.paragraphs.push(Paragraph::new(ParaShapeIndex::new(1)));
928 assert_eq!(hf.paragraphs.len(), 1);
929 assert_eq!(cloned.paragraphs.len(), 2);
930 }
931
932 #[test]
937 fn page_number_new() {
938 let pn = PageNumber::new(PageNumberPosition::BottomCenter, NumberFormatType::Digit);
939 assert_eq!(pn.position, PageNumberPosition::BottomCenter);
940 assert_eq!(pn.number_format, NumberFormatType::Digit);
941 assert!(pn.decoration.is_empty());
942 }
943
944 #[test]
945 fn page_number_with_decoration() {
946 let pn = PageNumber::with_decoration(
947 PageNumberPosition::BottomCenter,
948 NumberFormatType::RomanCapital,
949 "- ",
950 );
951 assert_eq!(pn.decoration, "- ");
952 assert_eq!(pn.number_format, NumberFormatType::RomanCapital);
953 }
954
955 #[test]
956 #[allow(deprecated)]
957 fn page_number_with_side_char_deprecated() {
958 let pn = PageNumber::with_side_char(
959 PageNumberPosition::BottomCenter,
960 NumberFormatType::Digit,
961 "- ",
962 );
963 assert_eq!(pn.decoration, "- ");
964 }
965
966 #[test]
967 fn page_number_display() {
968 let pn = PageNumber::new(PageNumberPosition::TopCenter, NumberFormatType::Digit);
969 let s = pn.to_string();
970 assert!(s.contains("TopCenter"), "display: {s}");
971 assert!(s.contains("Digit"), "display: {s}");
972 }
973
974 #[test]
975 fn page_number_serde_roundtrip() {
976 let pn = PageNumber::with_decoration(
977 PageNumberPosition::BottomCenter,
978 NumberFormatType::CircledDigit,
979 "< ",
980 );
981 let json = serde_json::to_string(&pn).unwrap();
982 let back: PageNumber = serde_json::from_str(&json).unwrap();
983 assert_eq!(pn, back);
984 }
985
986 #[test]
987 fn page_number_equality() {
988 let a = PageNumber::new(PageNumberPosition::BottomCenter, NumberFormatType::Digit);
989 let b = PageNumber::new(PageNumberPosition::BottomCenter, NumberFormatType::Digit);
990 assert_eq!(a, b);
991 }
992
993 #[test]
994 fn page_number_inequality() {
995 let a = PageNumber::new(PageNumberPosition::BottomCenter, NumberFormatType::Digit);
996 let b = PageNumber::new(PageNumberPosition::TopCenter, NumberFormatType::Digit);
997 assert_ne!(a, b);
998 }
999
1000 #[test]
1005 fn section_new_has_empty_header_footer_vecs() {
1006 let section = Section::new(PageSettings::a4());
1007 assert!(section.headers.is_empty());
1008 assert!(section.footers.is_empty());
1009 assert!(section.page_number.is_none());
1010 assert!(section.column_settings.is_none());
1011 }
1012
1013 #[test]
1014 fn section_with_header_footer() {
1015 let mut section = Section::new(PageSettings::a4());
1016 section.headers.push(HeaderFooter::new(
1017 vec![Paragraph::with_runs(
1018 vec![Run::text("Header", CharShapeIndex::new(0))],
1019 ParaShapeIndex::new(0),
1020 )],
1021 ApplyPageType::Both,
1022 ));
1023 section.footers.push(HeaderFooter::new(
1024 vec![Paragraph::with_runs(
1025 vec![Run::text("Footer", CharShapeIndex::new(0))],
1026 ParaShapeIndex::new(0),
1027 )],
1028 ApplyPageType::Both,
1029 ));
1030 assert_eq!(section.headers.len(), 1);
1031 assert_eq!(section.footers.len(), 1);
1032 }
1033
1034 #[test]
1035 fn section_with_page_number() {
1036 let mut section = Section::new(PageSettings::a4());
1037 section.page_number =
1038 Some(PageNumber::new(PageNumberPosition::BottomCenter, NumberFormatType::Digit));
1039 assert!(section.page_number.is_some());
1040 }
1041
1042 #[test]
1043 fn section_serde_with_optional_fields() {
1044 let mut section = Section::new(PageSettings::a4());
1045 section.headers.push(HeaderFooter::new(vec![], ApplyPageType::Both));
1046 section.page_number =
1047 Some(PageNumber::new(PageNumberPosition::BottomCenter, NumberFormatType::Digit));
1048 let json = serde_json::to_string(§ion).unwrap();
1049 let back: Section = serde_json::from_str(&json).unwrap();
1050 assert_eq!(section, back);
1051 }
1052
1053 #[test]
1054 fn section_serde_none_fields_skipped() {
1055 let section = Section::new(PageSettings::a4());
1056 let json = serde_json::to_string(§ion).unwrap();
1057 assert!(!json.contains("\"header\""));
1060 assert!(!json.contains("\"footer\""));
1061 assert!(!json.contains("\"page_number\""));
1062 assert!(!json.contains("\"column_settings\""));
1063 let back: Section = serde_json::from_str(&json).unwrap();
1064 assert_eq!(section, back);
1065 }
1066
1067 #[test]
1072 fn header_footer_all_pages_apply_page_type() {
1073 let hf = HeaderFooter::all_pages(vec![Paragraph::new(ParaShapeIndex::new(0))]);
1074 assert_eq!(hf.apply_page_type, ApplyPageType::Both);
1075 }
1076
1077 #[test]
1078 fn header_footer_all_pages_preserves_paragraphs() {
1079 let paras = vec![simple_paragraph(), simple_paragraph()];
1080 let hf = HeaderFooter::all_pages(paras);
1081 assert_eq!(hf.paragraphs.len(), 2);
1082 }
1083
1084 #[test]
1085 fn header_footer_all_pages_empty_paragraphs() {
1086 let hf = HeaderFooter::all_pages(vec![]);
1087 assert_eq!(hf.apply_page_type, ApplyPageType::Both);
1088 assert!(hf.paragraphs.is_empty());
1089 }
1090
1091 #[test]
1092 #[allow(deprecated)]
1093 fn header_footer_both_deprecated_alias() {
1094 let hf = HeaderFooter::both(vec![Paragraph::new(ParaShapeIndex::new(0))]);
1095 assert_eq!(hf.apply_page_type, ApplyPageType::Both);
1096 }
1097
1098 #[test]
1103 fn page_number_bottom_center_position() {
1104 let pn = PageNumber::bottom_center();
1105 assert_eq!(pn.position, PageNumberPosition::BottomCenter);
1106 }
1107
1108 #[test]
1109 fn page_number_bottom_center_format() {
1110 let pn = PageNumber::bottom_center();
1111 assert_eq!(pn.number_format, NumberFormatType::Digit);
1112 }
1113
1114 #[test]
1115 fn page_number_bottom_center_no_decoration() {
1116 let pn = PageNumber::bottom_center();
1117 assert!(pn.decoration.is_empty());
1118 }
1119
1120 #[test]
1121 fn page_number_bottom_center_equals_explicit() {
1122 let shortcut = PageNumber::bottom_center();
1123 let explicit = PageNumber::new(PageNumberPosition::BottomCenter, NumberFormatType::Digit);
1124 assert_eq!(shortcut, explicit);
1125 }
1126
1127 #[test]
1128 fn section_backward_compat_deserialize() {
1129 let a4 = PageSettings::a4();
1131 let json = serde_json::to_string(&Section::with_paragraphs(vec![], a4)).unwrap();
1132 let section: Section = serde_json::from_str(&json).unwrap();
1133 assert!(section.headers.is_empty());
1134 assert!(section.footers.is_empty());
1135 assert!(section.page_number.is_none());
1136 }
1137
1138 #[test]
1139 fn all_pages_equals_new_with_both() {
1140 let paras = vec![simple_paragraph()];
1141 let from_all_pages = HeaderFooter::all_pages(paras.clone());
1142 let from_new = HeaderFooter::new(paras, ApplyPageType::Both);
1143 assert_eq!(from_all_pages, from_new);
1144 }
1145}