| 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 | |
| 6 | use canvas::{ |
| 7 | editor::{CanvasEditor, DefaultFont}, |
| 8 | layout::TextEngine, |
| 9 | }; |
| 10 | use 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`. |
| 18 | const BLUE: u32 = 0xc07000; |
| 19 | |
| 20 | #[test] |
| 21 | fn 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 | } |