1//! OneNote's Default font as the editor stores it: a new page's title in its face and colour,
2//! and new text in the page's `p` quick style in its face, size and colour. `CANVAS_DEFAULT_FONT_EXPORT`
3//! names a directory receiving the section as a notebook, for a cold reopen in OneNote 2010
4//! (`corpus/default-font`).
5
6use canvas::{
7 editor::{CanvasEditor, DefaultFont},
8 layout::TextEngine,
9};
10use onestore::{
11 Arena, PageCreation, Section, Store,
12 document::Kind,
13 op::{Edit, Op, SectionOp},
14 page::{PageObject, ParagraphContent},
15};
16
17/// Office's standard Blue, COLORREF, which OneNote 2010 stores as `DefaultFontColor`.
18const BLUE: u32 = 0xc07000;
19
20#[test]
21fn new_pages_and_text_take_the_default_font() {
22 let source = onestore::create_section("fonts.one", "Calibri page", "Author").unwrap();
23 let arena = Arena::default();
24 let mut section = Section::open(&arena, source.clone()).unwrap();
25 let creation = PageCreation::new(None, Some("Georgia title"), "Author")
26 .unwrap()
27 .titled_in("Georgia", Some(BLUE))
28 .unwrap();
29 let mut at = 133_000_000_000_000_000;
30 section
31 .apply(
32 "Author",
33 &Edit {
34 at,
35 ops: vec![Op::Section(SectionOp::Create(creation))],
36 },
37 )
38 .unwrap();
39 let (space, ..) = section.pages().unwrap()[1].clone();
40 let mut engine = TextEngine::default();
41 let mut editor = CanvasEditor::from_page(section.page(space).unwrap(), &mut engine).unwrap();
42 editor.default_font = DefaultFont {
43 face: "Georgia".into(),
44 size: 14.0,
45 color: Some(BLUE),
46 };
47 for (text, y) in [("Georgia body", 120.0), ("Georgia again", 200.0)] {
48 editor.place_caret(&mut engine, [72.0, y], 240.0).unwrap();
49 editor.insert(&mut engine, text).unwrap();
50 let ops = editor.take_ops().unwrap();
51 at += 10_000_000;
52 let ops = ops.into_iter().map(|op| Op::Page { space, op }).collect();
53 section.apply("Author", &Edit { at, ops }).unwrap();
54 }
55 let mut image = source;
56 section.seal().unwrap().unwrap().apply(&mut image).unwrap();
57
58 let arena = Arena::default();
59 let reopened = Section::open(&arena, image.clone()).unwrap();
60 let page = reopened.page(space).unwrap();
61 let style = |id| {
62 let definition = &page.definitions[&id];
63 let Kind::Style { name, .. } = &definition.kind else {
64 panic!("{definition:?}")
65 };
66 (
67 name.as_deref().unwrap_or_default().to_owned(),
68 definition.format.font.clone().unwrap_or_default(),
69 definition.format.font_size,
70 definition.format.color,
71 )
72 };
73 let title = page
74 .objects
75 .iter()
76 .find_map(|object| match object {
77 PageObject::Title(title) => Some(title),
78 _ => None,
79 })
80 .unwrap();
81 let titled = title
82 .outlines
83 .iter()
84 .flat_map(|outline| &outline.paragraphs)
85 .filter_map(|paragraph| paragraph.style)
86 .map(style)
87 .find(|(name, ..)| name == "PageTitle");
88 assert_eq!(
89 titled,
90 Some(("PageTitle".into(), "Georgia".into(), Some(17.0), Some(BLUE)))
91 );
92 let bodies: Vec<_> = page
93 .objects
94 .iter()
95 .filter_map(|object| match object {
96 PageObject::Outline(outline) if !outline.title => Some(&outline.paragraphs[0]),
97 _ => None,
98 })
99 .collect();
100 assert_eq!(bodies.len(), 2);
101 for body in &bodies {
102 assert!(matches!(body.content, ParagraphContent::Text(_)));
103 assert_eq!(
104 style(body.style.unwrap()),
105 ("p".into(), "Georgia".into(), Some(14.0), Some(BLUE))
106 );
107 }
108 assert_eq!(bodies[0].style, bodies[1].style, "one p per font");
109
110 if let Some(directory) = std::env::var_os("CANVAS_DEFAULT_FONT_EXPORT") {
111 let directory = std::path::PathBuf::from(directory);
112 std::fs::create_dir_all(&directory).unwrap();
113 std::fs::write(directory.join("fonts.one"), &image).unwrap();
114 let file_id = Store::parse(&image).unwrap().header.file_id;
115 std::fs::write(
116 directory.join("Open Notebook.onetoc2"),
117 onestore::create_table_of_contents("Open Notebook.onetoc2", &[("fonts.one", file_id)])
118 .unwrap(),
119 )
120 .unwrap();
121 }
122}