1//! A file printout, whose pictures OneNote 2010 stores as pages of an XPS package with a PNG
2//! rendering of each beside it (WebPictureContainer14), shows that rendering and edits like
3//! any picture: its pages stay printouts when moved, and a page deleted comes back on undo
4//! as the printout page it was (`corpus/printout`).
5
6use canvas::{document::TextPosition, editor::CanvasEditor, layout::TextEngine};
7use onestore::{
8 Arena, Section, Store,
9 op::{Edit, Op},
10 page::{Image, Page, PageObject},
11};
12
13const PRINTOUTS: &[u8] = include_bytes!("../../../corpus/printout/native/notebook/Printouts.one");
14
15fn pictures(page: &Page) -> Vec<&Image> {
16 page.objects
17 .iter()
18 .filter_map(|object| match object {
19 PageObject::Image(image) => Some(image),
20 _ => None,
21 })
22 .collect()
23}
24
25/// `SNOWBOUND_PRINTOUT_EXPORT` names a new directory receiving the candidate for a cold
26/// reopen.
27#[test]
28fn printouts_show_onenotes_rendering_and_move_as_pictures() {
29 let arena = Arena::default();
30 let mut section = Section::open(&arena, PRINTOUTS.to_vec()).unwrap();
31 let (space, ..) = section
32 .pages()
33 .unwrap()
34 .into_iter()
35 .find(|page| page.1 == "Printout")
36 .unwrap();
37 let page = section.page(space).unwrap();
38 let printed = pictures(&page);
39 assert_eq!(printed.len(), 2);
40 for picture in &printed {
41 let (stored, shown) = (
42 picture.bytes.as_ref().unwrap(),
43 picture.display.as_ref().unwrap(),
44 );
45 // The stored picture is the XPS package; the shown one a PNG.
46 assert!(stored.starts_with(b"PK\x03\x04"));
47 assert!(shown.starts_with(b"\x89PNG"));
48 }
49 assert!(Iterator::eq(
50 printed.iter().map(|picture| picture.bytes.as_ref()),
51 [printed[0].bytes.as_ref(); 2]
52 ));
53 let (moved, layout) = (printed[1].id, printed[1].layout.clone());
54 let origin = [layout.x.unwrap(), layout.y.unwrap() + 36.0];
55 let size = [layout.max_width.unwrap(), layout.max_height.unwrap()];
56
57 let mut engine = TextEngine::default();
58 let mut editor = CanvasEditor::from_page(page.clone(), &mut engine).unwrap();
59 #[cfg(feature = "gpu")]
60 {
61 let (scene, editor) =
62 canvas::gpu::page::PageScene::from_page(page.clone(), &mut engine).unwrap();
63 assert_eq!(scene.read_only(Some(&editor)).count(), 0);
64 }
65 editor
66 .place_image(&mut engine, moved, origin, size)
67 .unwrap();
68 let restored = printed[0].id;
69 editor.remove_image(&mut engine, restored).unwrap();
70 assert!(editor.undo(&mut engine).unwrap());
71 let body = editor.outlines().iter().find(|o| !o.title).unwrap().id;
72 editor.focus_outline(body).unwrap();
73 let at = TextPosition {
74 paragraph: 0,
75 offset: 0,
76 };
77 editor.select([at; 2].into()).unwrap();
78 editor.insert(&mut engine, "Printed ").unwrap();
79 let ops = editor
80 .take_ops()
81 .unwrap()
82 .into_iter()
83 .map(|op| Op::Page { space, op })
84 .collect();
85 section
86 .apply(
87 "Author",
88 &Edit {
89 at: 134_000_000_000_000_000,
90 ops,
91 },
92 )
93 .unwrap();
94 section.seal().unwrap();
95 let written = section.image();
96 let reread = Section::open(&arena, written.clone()).unwrap();
97 let page = reread.page(space).unwrap();
98 let after = pictures(&page);
99 assert_eq!(after.len(), 2);
100 let moved = after.iter().find(|picture| picture.id == moved).unwrap();
101 assert_eq!([moved.layout.x, moved.layout.y], origin.map(Some));
102 assert!(moved.printout.is_some() && moved.display.is_some());
103 // The page deleted and brought back by undo is the printout page it was: its package,
104 // page number and the raster OneNote showed of it.
105 let original = printed
106 .iter()
107 .find(|picture| picture.id == restored)
108 .unwrap();
109 let restored = after.iter().find(|picture| picture.id == restored).unwrap();
110 assert_eq!(restored.printout, original.printout);
111 assert_eq!(restored.bytes, original.bytes);
112 assert_eq!(restored.display, original.display);
113 if let Some(directory) = std::env::var_os("SNOWBOUND_PRINTOUT_EXPORT") {
114 let directory = std::path::PathBuf::from(directory);
115 std::fs::create_dir(&directory).unwrap();
116 std::fs::write(directory.join("Printouts.one"), &written).unwrap();
117 let file_id = Store::parse(&written).unwrap().header.file_id;
118 std::fs::write(
119 directory.join("Open Notebook.onetoc2"),
120 onestore::create_table_of_contents(
121 "Open Notebook.onetoc2",
122 &[("Printouts.one", file_id)],
123 )
124 .unwrap(),
125 )
126 .unwrap();
127 }
128}
129
130/// A copy of a printout's page keeps its pages printouts, as a page copied in OneNote does.
131#[test]
132fn a_copied_printout_page_keeps_its_printouts() {
133 let arena = Arena::default();
134 let mut section = Section::open(&arena, PRINTOUTS.to_vec()).unwrap();
135 let (space, ..) = section
136 .pages()
137 .unwrap()
138 .into_iter()
139 .find(|page| page.1 == "Printout")
140 .unwrap();
141 let page = section.page(space).unwrap();
142 let creation = onestore::PageCreation::new(None, Some("Copied"), "Author").unwrap();
143 let edit = Edit {
144 at: 134_000_000_000_000_000,
145 ops: vec![Op::Section(onestore::op::SectionOp::Import {
146 creation,
147 page: page.copy().unwrap(),
148 })],
149 };
150 section.apply("Author", &edit).unwrap();
151 section.seal().unwrap();
152 let (copied, ..) = section
153 .pages()
154 .unwrap()
155 .into_iter()
156 .find(|page| page.1 == "Copied")
157 .unwrap();
158 let copy = section.page(copied).unwrap();
159 let shown = |page: &Page| -> Vec<_> {
160 pictures(page)
161 .into_iter()
162 .map(|picture| {
163 (
164 picture.printout.clone(),
165 picture.bytes.clone(),
166 picture.display.clone(),
167 picture.text.clone(),
168 )
169 })
170 .collect()
171 };
172 assert_eq!(shown(&copy), shown(&page));
173}